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