• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
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  */
16 package org.jivesoftware.smack.compression;
17 
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.zip.Deflater;
24 import java.util.zip.DeflaterOutputStream;
25 import java.util.zip.Inflater;
26 import 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  */
38 public 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 
Java7ZlibInputOutputStream()54     public Java7ZlibInputOutputStream() {
55         compressionMethod = "zlib";
56     }
57 
58     @Override
isSupported()59     public boolean isSupported() {
60         return supported;
61     }
62 
63     @Override
getInputStream(InputStream inputStream)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 }
127