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