blob: dc504517bbdb59e1b1f11e391125fa4ae777a03c [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * Copyright 2013 Florian Schmaus
3 *
4 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.jivesoftware.smack.compression;
17
18import java.io.IOException;
19import java.io.InputStream;
20import java.io.OutputStream;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.util.zip.Deflater;
24import java.util.zip.DeflaterOutputStream;
25import java.util.zip.Inflater;
26import java.util.zip.InflaterInputStream;
27
28/**
29 * This class provides XMPP "zlib" compression with the help of the Deflater class of the Java API. Note that the method
30 * needed is available since Java7, so it will only work with Java7 or higher (hence it's name).
31 *
32 * @author Florian Schmaus
33 * @see <a
34 * href="http://docs.oracle.com/javase/7/docs/api/java/util/zip/Deflater.html#deflate(byte[], int, int, int)">The
35 * required deflate() method</a>
36 *
37 */
38public class Java7ZlibInputOutputStream extends XMPPInputOutputStream {
39 private final static Method method;
40 private final static boolean supported;
41 private final static int compressionLevel = Deflater.DEFAULT_STRATEGY;
42
43 static {
44 Method m = null;
45 try {
46 m = Deflater.class.getMethod("deflate", byte[].class, int.class, int.class, int.class);
47 } catch (SecurityException e) {
48 } catch (NoSuchMethodException e) {
49 }
50 method = m;
51 supported = (method != null);
52 }
53
54 public Java7ZlibInputOutputStream() {
55 compressionMethod = "zlib";
56 }
57
58 @Override
59 public boolean isSupported() {
60 return supported;
61 }
62
63 @Override
64 public InputStream getInputStream(InputStream inputStream) {
65 return new InflaterInputStream(inputStream, new Inflater(), 512) {
66 /**
67 * Provide a more InputStream compatible version. A return value of 1 means that it is likely to read one
68 * byte without blocking, 0 means that the system is known to block for more input.
69 *
70 * @return 0 if no data is available, 1 otherwise
71 * @throws IOException
72 */
73 @Override
74 public int available() throws IOException {
75 /*
76 * aSmack related remark (where KXmlParser is used):
77 * This is one of the funny code blocks. InflaterInputStream.available violates the contract of
78 * InputStream.available, which breaks kXML2.
79 *
80 * I'm not sure who's to blame, oracle/sun for a broken api or the google guys for mixing a sun bug with
81 * a xml reader that can't handle it....
82 *
83 * Anyway, this simple if breaks suns distorted reality, but helps to use the api as intended.
84 */
85 if (inf.needsInput()) {
86 return 0;
87 }
88 return super.available();
89 }
90 };
91 }
92
93 @Override
94 public OutputStream getOutputStream(OutputStream outputStream) {
95 return new DeflaterOutputStream(outputStream, new Deflater(compressionLevel)) {
96 public void flush() throws IOException {
97 if (!supported) {
98 super.flush();
99 return;
100 }
101 int count = 0;
102 if (!def.needsInput()) {
103 do {
104 count = def.deflate(buf, 0, buf.length);
105 out.write(buf, 0, count);
106 } while (count > 0);
107 out.flush();
108 }
109 try {
110 do {
111 count = (Integer) method.invoke(def, buf, 0, buf.length, 2);
112 out.write(buf, 0, count);
113 } while (count > 0);
114 } catch (IllegalArgumentException e) {
115 throw new IOException("Can't flush");
116 } catch (IllegalAccessException e) {
117 throw new IOException("Can't flush");
118 } catch (InvocationTargetException e) {
119 throw new IOException("Can't flush");
120 }
121 super.flush();
122 }
123 };
124 }
125
126}