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