001/* 002 * $Id: PackCompressorBase.java,v 1.2 2005/08/26 11:22:53 bartzkau Exp $ 003 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 004 * 005 * http://www.izforge.com/izpack/ 006 * http://developer.berlios.de/projects/izpack/ 007 * 008 * Copyright 2005 Klaus Bartz 009 * 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 */ 022package com.izforge.izpack.compressor; 023 024import java.io.BufferedOutputStream; 025import java.io.File; 026import java.io.FileOutputStream; 027import java.io.InputStream; 028import java.io.OutputStream; 029import java.lang.reflect.Constructor; 030import java.net.URL; 031import java.net.URLClassLoader; 032 033import com.izforge.izpack.compiler.Compiler; 034 035 036/** 037 * IzPack will be able to support different compression methods for the 038 * packs included in the installation jar file. 039 * This abstract class implements the interface PackCompressor for 040 * the common needed methods. 041 * 042 * @author Klaus Bartz 043 */ 044 045public abstract class PackCompressorBase implements PackCompressor 046{ 047 048 protected String [] formatNames = null; 049 protected String [] containerPaths = null; 050 protected String decoderMapper = null; 051 /** 052 * Should contain all full qualified (use dots, not slashes) 053 * names of the class files. Regex will be suported in the 054 * manner of <code>String.match</code>. <br> 055 * Example: 056 * <pre>"org.apache.tools.bzip2.CBZip2InputStream.*"</pre> 057 * Do not forget the dot before the asterix. 058 * For an other example see class BZip2PackCompressor. 059 */ 060 protected String [][] decoderClassNames = null; 061 protected String encoderClassName = null; 062 063 protected Class [] paramsClasses = null; 064 065 private Compiler compiler; 066 private Constructor constructor; 067 private int level = -1; 068 /** 069 * 070 */ 071 public PackCompressorBase() 072 { 073 super(); 074 } 075 076 /* (non-Javadoc) 077 * @see com.izforge.izpack.compressor.PackCompressor#getContainerPath() 078 */ 079 public String[] getContainerPaths() 080 { 081 return(containerPaths); 082 } 083 084 /* (non-Javadoc) 085 * @see com.izforge.izpack.compressor.PackCompressor#getEncoderClassName() 086 */ 087 public String getEncoderClassName() 088 { 089 return(encoderClassName); 090 } 091 /* (non-Javadoc) 092 * @see com.izforge.izpack.compressor.PackCompressor#getDecoderClassNames() 093 */ 094 public String[][] getDecoderClassNames() 095 { 096 return(decoderClassNames); 097 } 098 099 /* (non-Javadoc) 100 * @see com.izforge.izpack.compressor.PackCompressor#useStandardCompression() 101 */ 102 public boolean useStandardCompression() 103 { 104 return( false ); 105 } 106 107 /* (non-Javadoc) 108 * @see com.izforge.izpack.compressor.PackCompressor#getCompressionFormatSymbols() 109 */ 110 public String[] getCompressionFormatSymbols() 111 { 112 return(formatNames); 113 } 114 115 /* (non-Javadoc) 116 * @see com.izforge.izpack.compressor.PackCompressor#getDecoderMapperName() 117 */ 118 public String getDecoderMapperName() 119 { 120 return(decoderMapper); 121 } 122 123 /* (non-Javadoc) 124 * @see com.izforge.izpack.compressor.PackCompressor#setCompiler(com.izforge.izpack.compiler.Compiler) 125 */ 126 public void setCompiler(Compiler compiler) 127 { 128 this.compiler = compiler; 129 } 130 131 /* (non-Javadoc) 132 * @see com.izforge.izpack.compressor.PackCompressor#setCompressionLevel(int) 133 */ 134 public void setCompressionLevel(int level) 135 { 136 this.level = level; 137 } 138 139 /* (non-Javadoc) 140 * @see com.izforge.izpack.compressor.PackCompressor#getCompressionLevel() 141 */ 142 public int getCompressionLevel() 143 { 144 return( level); 145 } 146 147 /* (non-Javadoc) 148 * @see com.izforge.izpack.compressor.PackCompressor#needsBufferedOutputStream() 149 */ 150 public boolean needsBufferedOutputStream() 151 { 152 return(true); 153 } 154 155 156 /** 157 * Loads the given class from the previos setted container paths. 158 * @param className full qualified name of the class to be loaded 159 * @throws Exception 160 */ 161 public void loadClass( String className) throws Exception 162 { 163 if( getEncoderClassName() == null) 164 return; 165 Class encoder = null; 166 if( getContainerPaths() == null ) 167 { // May be class files are in the compiler.jar. 168 encoder = Class.forName(className); 169 } 170 if( encoder == null) 171 { 172 String [] rawPaths = getContainerPaths(); 173 URL [] uRLs = new URL[rawPaths.length]; 174 Object instance = null; 175 int i; 176 int j = 0; 177 178 for(i = 0; i < rawPaths.length; ++i) 179 { 180 if( rawPaths[i] == null ) 181 continue; 182 String jarPath = compiler.replaceProperties(rawPaths[i]); 183 URL url = compiler.findIzPackResource(jarPath, "Pack compressor jar file"); 184 if (url != null) 185 { 186 uRLs[j++] = url; 187 if (getClass().getResource("/" + jarPath) != null) 188 { // Oops, standalone, URLClassLoader will not work ... 189 // Write the jar to a temp file. 190 InputStream in = null; 191 FileOutputStream outFile = null; 192 byte[] buffer = new byte[5120]; 193 File tf = null; 194 try 195 { 196 tf = File.createTempFile("izpj", ".jar"); 197 tf.deleteOnExit(); 198 outFile = new FileOutputStream(tf); 199 in = getClass().getResourceAsStream("/" + jarPath); 200 long bytesCopied = 0; 201 int bytesInBuffer; 202 while ((bytesInBuffer = in.read(buffer)) != -1) 203 { 204 outFile.write(buffer, 0, bytesInBuffer); 205 bytesCopied += bytesInBuffer; 206 } 207 } 208 finally 209 { 210 if (in != null) in.close(); 211 if (outFile != null) outFile.close(); 212 } 213 url = tf.toURL(); 214 215 } 216 } 217 } 218 if( j > 0 ) 219 { 220 if( j < uRLs.length) 221 { 222 URL [] nurl = new URL[j]; 223 for( i = 0; i < j; ++i) 224 nurl[i] = uRLs[i]; 225 uRLs = nurl; 226 } 227 // Use the class loader of the interface as parent, else 228 // compile will fail at using it via an Ant task. 229 URLClassLoader ucl = new URLClassLoader(uRLs, PackCompressor.class 230 .getClassLoader()); 231 encoder = ucl.loadClass(className); 232 } 233 } 234 235 if (encoder != null) 236 { 237 // Be aware, paramsClasses should be defined earlier! For 238 // default in the constructor of this class. 239 constructor = encoder.getDeclaredConstructor(paramsClasses); 240 } 241 else 242 compiler.parseError( "Cannot find defined compressor " + className); 243 } 244 245 /** 246 * Returns a newly created instance of the output stream which should be 247 * used by this pack compressor. This method do not declare the 248 * return value as FilterOutputStream although there must be an constructor 249 * with a slave output stream as argument. This is done in this way because 250 * some encoding streams from third party are only implemented as 251 * "normal" output stream. 252 * @param slave output stream to be used as slave 253 * @return a newly created instance of the output stream which should be 254 * used by this pack compressor 255 * @throws Exception 256 */ 257 protected OutputStream getOutputInstance(OutputStream slave) 258 throws Exception 259 { 260 if( needsBufferedOutputStream()) 261 { 262 slave = new BufferedOutputStream( slave); 263 } 264 Object [] params = resolveConstructorParams( slave ); 265 if( constructor == null ) 266 loadClass(getEncoderClassName()); 267 if( constructor == null ) 268 return(null); 269 Object instance = null; 270 instance = constructor.newInstance( params); 271 if (!OutputStream.class.isInstance(instance)) 272 compiler.parseError( "'" + getEncoderClassName() + "' must be derived from " 273 + OutputStream.class.toString()); 274 return((OutputStream) instance ); 275 276 } 277 278 /** 279 * This method will be used to support different constructor signatures. 280 * The default is 281 * <pre>XXXOutputStream( OutputStream slave )</pre> 282 * if level is -1 or 283 * <pre>XXXOutputStream( OutputStream slave, int level )</pre> 284 * if level is other than -1.<br> 285 * If the signature of the used output stream will be other, overload 286 * this method in the derived pack compressor class. 287 * @param slave output stream to be used as slave 288 * @return the constructor params as Object [] to be used as construction 289 * of the constructor via reflection 290 * @throws Exception 291 */ 292 protected Object[] resolveConstructorParams( OutputStream slave) throws Exception 293 { 294 if( level == -1 ) 295 { 296 paramsClasses = new Class[1]; 297 paramsClasses[0] = Class.forName("java.io.OutputStream"); 298 Object[] params = { slave}; 299 return( params ); 300 } 301 paramsClasses = new Class[2]; 302 paramsClasses[0] = Class.forName("java.io.OutputStream"); 303 paramsClasses[1] = java.lang.Integer.TYPE; 304 Object[] params = { slave, new Integer(level)}; 305 return( params ); 306 } 307 308}