001/** 002 * 003 * Copyright 2013-2020 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.compression; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.OutputStream; 022import java.util.zip.Deflater; 023import java.util.zip.DeflaterOutputStream; 024import java.util.zip.Inflater; 025import java.util.zip.InflaterInputStream; 026 027/** 028 * This class provides XMPP "zlib" compression with the help of the Deflater class of the Java API. 029 * Note that the method needed for compression with synchronous flush support is available since 030 * Java7, so it will only work with Java7 or higher (hence it's name). On Android, the required 031 * <code>deflate()</code> method is available on API 19 or higher. 032 * <p> 033 * See also: 034 * <ul> 035 * <li><a href="http://docs.oracle.com/javase/7/docs/api/java/util/zip/Deflater.html#deflate(byte[],%20int,%20int,%20int)">The required deflate() method (Java7)</a> 036 * <li><a href="http://developer.android.com/reference/java/util/zip/Deflater.html#deflate(byte[],%20int,%20int,%20int)">The required deflate() method (Android)</a> 037 * </ul> 038 * 039 * @author Florian Schmaus 040 */ 041public class Java7ZlibInputOutputStream extends XMPPInputOutputStream { 042 private static final int compressionLevel = Deflater.DEFAULT_COMPRESSION; 043 044 public Java7ZlibInputOutputStream() { 045 super("zlib"); 046 } 047 048 @Override 049 public boolean isSupported() { 050 return true; 051 } 052 053 @Override 054 public InputStream getInputStream(InputStream inputStream) { 055 return new InflaterInputStream(inputStream, new Inflater(), 512) { 056 /** 057 * Provide a more InputStream compatible version. A return value of 1 means that it is likely to read one 058 * byte without blocking, 0 means that the system is known to block for more input. 059 * 060 * @return 0 if no data is available, 1 otherwise 061 * @throws IOException if an I/O error occurred. 062 */ 063 @Override 064 public int available() throws IOException { 065 /* 066 * aSmack related remark (where KXmlParser is used): 067 * This is one of the funny code blocks. InflaterInputStream.available violates the contract of 068 * InputStream.available, which breaks kXML2. 069 * 070 * I'm not sure who's to blame, oracle/sun for a broken api or the google guys for mixing a sun bug with 071 * a xml reader that can't handle it.... 072 * 073 * Anyway, this simple if breaks suns distorted reality, but helps to use the api as intended. 074 */ 075 if (inf.needsInput()) { 076 return 0; 077 } 078 return super.available(); 079 } 080 }; 081 } 082 083 @Override 084 public OutputStream getOutputStream(OutputStream outputStream) { 085 final int flushMethodInt; 086 switch (flushMethod) { 087 case SYNC_FLUSH: 088 flushMethodInt = Deflater.SYNC_FLUSH; 089 break; 090 case FULL_FLUSH: 091 flushMethodInt = Deflater.FULL_FLUSH; 092 break; 093 default: 094 throw new AssertionError(); 095 } 096 097 return new DeflaterOutputStream(outputStream, new Deflater(compressionLevel)) { 098 @Override 099 public void flush() throws IOException { 100 int count; 101 while ((count = def.deflate(buf, 0, buf.length, flushMethodInt)) > 0) { 102 out.write(buf, 0, count); 103 } 104 super.flush(); 105 } 106 }; 107 } 108 109}