source: release-kits/shared/core/ant-installer/src/org/tp23/antinstaller/selfextract/SelfExtractor.java@ 20043

Last change on this file since 20043 was 20043, checked in by oranfry, 15 years ago

store the antinstaller temp directory in a property

File size: 13.7 KB
Line 
1/*
2 * Copyright 2005 Paul Hinds
3 *
4 * 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 */
16package org.tp23.antinstaller.selfextract;
17
18import java.awt.HeadlessException;
19import java.io.BufferedOutputStream;
20import java.io.File;
21import java.io.FileFilter;
22import java.io.FileInputStream;
23import java.io.FileNotFoundException;
24import java.io.FileOutputStream;
25import java.io.IOException;
26import java.net.URL;
27import java.net.URLDecoder;
28import java.util.ArrayList;
29import java.util.jar.JarEntry;
30import java.util.jar.JarFile;
31import java.util.jar.JarInputStream;
32
33import javax.swing.JOptionPane;
34import javax.swing.UIManager;
35
36import org.tp23.antinstaller.InstallException;
37import org.tp23.antinstaller.renderer.swing.plaf.LookAndFeelFactory;
38import org.tp23.antinstaller.runtime.ExecInstall;
39import org.tp23.antinstaller.runtime.exe.FilterChain;
40import org.tp23.antinstaller.runtime.exe.FilterFactory;
41
42/**
43 *
44 * <p>Finds a file reference to the Jar that loads this class and then extracts that Jar
45 * to a temporary directory </p>
46 * <p> </p>
47 * @author Paul Hinds
48 * @version $Id: SelfExtractor.java,v 1.10 2007/01/28 08:44:40 teknopaul Exp $
49 */
50public class SelfExtractor {
51
52 public static final String CONFIG_RESOURCE = "/org/tp23/antinstaller/runtime/exe/selfextractor.fconfig";
53
54 private File extractDir;
55 private File archiveFile;
56 private boolean overwrite = true;
57
58 private static int DEFAULT_BUFFER_SIZE = 1024;
59 private int BUFFER_SIZE = DEFAULT_BUFFER_SIZE;
60 private static boolean graphicsEnv = false;
61 private static String lookAndFeel = null;
62
63 /**
64 * returns the Jar that the reference object was loaded from. If it was not
65 * loaded from a jar this methods behaviour is undefined
66 * @TODO define what happens
67 * @param reference
68 * @return A java.io.File reference to the Jar
69 */
70 public static File getEnclosingJar(Object reference) {
71 String thisClass = "/" + reference.getClass().getName().replace('.','/') + ".class";
72 URL jarUrl = reference.getClass().getResource(thisClass);
73 String stringForm = jarUrl.toString();
74 //String fileForm = jarUrl.getFile();
75
76 File file = null;
77 int endIdx = stringForm.indexOf("!/");
78 if(endIdx != -1){
79 String unescaped = null;
80 String fileNamePart = stringForm.substring("jar:file:".length(), endIdx);
81 file = new File(fileNamePart);
82 if ( ! file.exists()) {
83 // try to unescape encase the URL Handler has escaped the " " to %20
84 unescaped = unescape(fileNamePart);
85 file = new File(unescaped);
86 }
87 return file;
88 }
89 throw new RuntimeException("Failed expanding Jar.");
90 }
91
92 /**
93 * Constructor for the SelfExtractor object. Directly after constructing
94 * an instance the init() method should be called unless subclassing
95 */
96 public SelfExtractor() {
97 }
98
99 /**
100 * This has been moved from the default constructor to facilitate subclassing
101 * @return true if the lookAndFeel worked
102 */
103 public void init(){
104 System.out.println("Loading self extractor...");
105 archiveFile = getEnclosingJar(this);
106 makeTempDir();
107 try {
108 JarFile thisJar = new JarFile(archiveFile);
109 lookAndFeel = thisJar.getManifest().getMainAttributes().getValue("Look-And-Feel");
110 lookAndFeel = LookAndFeelFactory.getLafFromToken(lookAndFeel);
111 if(lookAndFeel != null) {
112 UIManager.setLookAndFeel(lookAndFeel);
113 }
114 }
115 catch (Throwable ex) {
116 // not concerned about Look and Feel
117 }
118 }
119
120 /**
121 * Creates a new empty temporary directory for the file extraction
122 * @return
123 */
124 protected File makeTempDir(){
125 // String tempDir = System.getProperty("java.io.tmpdir");
126 File curDir = new File("t.tmp").getParentFile();
127 extractDir = new File(curDir, "antinstall");
128 for ( int i=0; extractDir.exists(); i++) {
129 extractDir = new File(curDir, "antinstall" + i);
130 }
131 extractDir.mkdirs();
132 extractDir.deleteOnExit();
133 System.setProperty("antinstall.tmpdir",extractDir.getPath());
134 return extractDir;
135 }
136
137 /**
138 * Constructor for the SelfExtractor object that sets the buffersize in use.
139 * The write buffer is the same size as the write buffer size because the read buffer reads
140 * decompressed bytes
141 * @param newBufferSize the size of the read buffer
142 */
143 public SelfExtractor(int newBufferSize) {
144 BUFFER_SIZE = newBufferSize;
145 archiveFile = getEnclosingJar(this);
146 }
147
148 /**
149 * Sets the Directory into which the file will be extracted
150 *
151 *@param newExtractDir The new extract directory
152 */
153 public void setExtractDir(File newExtractDir) {
154 extractDir = newExtractDir;
155 }
156
157 /**
158 * changes the archive to be extracted
159 *@param newArchiveFile The new archiveFile value
160 */
161 public void setArchiveFile(File newArchiveFile) {
162 archiveFile = newArchiveFile;
163 }
164
165 /**
166 * Gets the Directory into which the files will be extracted that
167 * is currently set in the ZipExtractor object
168 *@return The extract directory value
169 */
170 public File getExtractDir() {
171 return extractDir;
172 }
173
174 /**
175 * Gets the set in the ZipExtractor
176 *@return The archiveFile value
177 */
178 public boolean isOverwrite() {
179 return overwrite;
180 }
181
182 /**
183 * Gets the Directory into which the files will be extracted that
184 * is currently set in the ZipExtractor object
185 *@return The extract directory value
186 */
187 public void setOverwrite(boolean overwrite) {
188 this.overwrite = overwrite;
189 }
190
191 /**
192 * Gets the set in the ZipExtractor
193 *@return The archiveFile value
194 */
195 public File getArchiveFile() {
196 return archiveFile;
197 }
198
199 /**
200 * Opens up the zip and gets a list of the files in it. If the zip file
201 * or the temp file have not been set NullPointerExceptions will get thrown
202 *@param vebose if true Prints out a list of the zips
203 * contents on to the command line
204 *@return an ArrayList of String objects that will
205 * be as per the path in the zip
206 *@exception FileNotFoundException Description of Exception
207 *@exception IOException Description of Exception
208 */
209 public ArrayList getList(boolean vebose) throws FileNotFoundException, IOException {
210 JarInputStream zis = new JarInputStream(new FileInputStream(archiveFile));
211 JarEntry entry = null;
212 ArrayList result = new ArrayList();
213 while ( (entry = zis.getNextJarEntry()) != null) {
214 if (vebose) {
215 System.out.println(entry.getName());
216 }
217 result.add(entry.getName());
218 }
219 return result;
220 }
221
222 /**
223 * @return the number of files in the jar
224 * @throws FileNotFoundException
225 * @throws IOException
226 */
227 public int getFileCount() throws FileNotFoundException, IOException {
228 JarInputStream zis = new JarInputStream(new FileInputStream(archiveFile));
229 int count = 0;
230 while ( zis.getNextJarEntry() != null) {
231 count++;
232 }
233 return count;
234 }
235
236 /**
237 * Opens up the zip and extracts the files to the temp dir.
238 *
239 *@param vebose if true Prints out a list of the zips contents on to System.out
240 *@return an ArrayList of java.io.File objects that
241 * will be as per the path in the zip with the root being the temp dir
242 *@exception FileNotFoundException
243 *@exception IOException
244 */
245 public ArrayList extract(boolean vebose, boolean isX) throws FileNotFoundException, IOException {
246 int fileCount = getFileCount();
247 ProgressIndicator indicator = null;
248 if(isX){
249 try {
250 indicator = new ProgressIndicator(fileCount);
251 indicator.show();
252 }
253 catch ( Exception exc ) {
254 /*
255 * Chances are, there are problems with the graphics environment
256 * so trying falling back to text mode
257 */
258 graphicsEnv = false;
259 isX = false;
260 }
261
262 }
263 JarInputStream zis = new JarInputStream(new FileInputStream(archiveFile));
264 JarEntry entry = null;
265 ArrayList result = new ArrayList();
266 while ( (entry = zis.getNextJarEntry()) != null) {
267 if (vebose) {
268 System.out.println("Extracting:" + entry.getName());
269 }
270 result.add(extract(zis, entry));
271 if (isX) {
272 indicator.tick();
273 }
274 }
275 if (isX) {
276 indicator.hide();
277 }
278 zis.close();
279 return result;
280 }
281
282
283
284 /**
285 * Extract a single file from the stream. N.B. the stream must be in the correct
286 * position for this to work
287 *@param zis ZipInputStream open and ready
288 *@param entry A valid entry read from the stream
289 *@return The inflated file generated in the temp dir
290 *@exception FileNotFoundException
291 *@exception IOException
292 */
293 private File extract(JarInputStream zis, JarEntry entry) throws FileNotFoundException, IOException {
294 createPath(entry.getName());
295 File fileToUse = new File(extractDir, entry.getName());
296 if (fileToUse.exists()) {
297 if (!overwrite) {
298 return fileToUse;
299 }
300 }
301 else {
302 fileToUse.createNewFile();
303 }
304 if (fileToUse.isDirectory()) {
305 return fileToUse;
306 }
307
308 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileToUse), BUFFER_SIZE);
309 byte[] bytes = new byte[BUFFER_SIZE];
310 int len = 0;
311 while ( (len = zis.read(bytes)) >= 0) {
312 bos.write(bytes, 0, len);
313 }
314 bos.close();
315 zis.closeEntry();
316 return fileToUse;
317 }
318
319 /**
320 * This adds all the necessary directories in the root of the zip path to the
321 * temp dir.
322 *@param entryName The string name in the Zip file (virtual path)
323 *@exception IOException if the directories can not be made
324 */
325 private void createPath(String entryName) throws IOException {
326 int slashIdx = entryName.lastIndexOf('/');
327 if (slashIdx >= 0) {
328 // there is path info
329 String firstPath = entryName.substring(0, slashIdx);
330 File dir = new File(extractDir, firstPath);
331 if (!dir.exists()) {
332 dir.mkdirs();
333 }
334 }
335 }
336
337 /**
338 * Run method to use from the command line. This is fired via an entry in the
339 * MANIFEST.MF in the Jar
340 *@param args The command line arguments
341 */
342 public static void main(String[] args) {
343 testX();
344 // FIXME move after parseArgs() and set graphicsEnv if text selected
345 // will need to test SelfExtractor and comment parseArgs() to ensure
346 // no side effects in the future.
347 SelfExtractor extractor = null;
348 try {
349 boolean verbose = false;
350 extractor = new SelfExtractor();
351 extractor.init();
352 extractor.extract(verbose, graphicsEnv);
353 }
354 catch (Exception e) {
355 e.printStackTrace();
356 String tempDir = "unknown";
357 if(extractor != null){
358 tempDir = extractor.getExtractDir().getAbsolutePath();
359 }
360 String warning = "Could not extract Jar file to directory:" + tempDir;
361 printXorTextWarning(warning);
362 }
363
364 try {
365 FilterChain chain = FilterFactory.factory(CONFIG_RESOURCE);
366 ExecInstall installExec = new ExecInstall(chain);
367 installExec.parseArgs(args, false);
368 installExec.setInstallRoot(extractor.getExtractDir());
369 // removes files on exit
370 installExec.setTempRoot(extractor.getExtractDir());
371
372 installExec.exec();
373 }
374 catch (InstallException e1) {
375 System.out.println("Cant load filter chain:/org/tp23/antinstaller/runtime/exe/selfextractor.fconfig");
376 e1.printStackTrace();
377 }
378 }
379
380 /**
381 * This tests for the existence of a graphics environment and sets an
382 * internal flag so the test does not have to be repeated, it may be expensive.
383 * Prior to running this method the isGraphicsEnv() method will be invalid.
384 */
385 protected static void testX(){
386 try {
387 java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
388 try {
389 boolean headless = java.awt.GraphicsEnvironment.isHeadless();
390 if(headless) {
391 graphicsEnv = false;
392 return;
393 }
394 } catch (Throwable e) {
395 // JDK 1.3 does not have the isHeadless() method but may still work in other situations
396 }
397 graphicsEnv = true;
398 }
399 catch (Throwable e) {
400 // thus graphicsEnv stays false;
401 }
402 }
403
404 /**
405 * @see #testX()
406 * @return true if an X or windows environment is available
407 */
408 protected boolean isGraphicsEnv(){
409 return graphicsEnv;
410 }
411
412 protected static void printXorTextWarning(String warning){
413 if(graphicsEnv){
414 try {
415 JOptionPane.showMessageDialog(null, warning);
416 }
417 catch( HeadlessException headlessExc ) {
418 graphicsEnv = false;
419 System.out.println(warning);
420 }
421 }
422 else {
423 System.out.println(warning);
424 }
425 }
426
427 public static int deleteRecursive(File directory) {
428 int count = 0;
429 File[] files = directory.listFiles(new FileFilter() {
430 public boolean accept(File file) {
431 return!file.isDirectory();
432 }
433 });
434 for (int i = 0; i < files.length; i++) {
435 files[i].delete();
436 count++;
437 }
438 File[] dirs = directory.listFiles(new FileFilter() {
439 public boolean accept(File file) {
440 return file.isDirectory();
441 }
442 });
443 for (int i = 0; i < dirs.length; i++) {
444 count += deleteRecursive(dirs[i]);
445 }
446 directory.delete();
447 return count;
448 }
449
450 /**
451 * UN-URL encode string
452 * TODO should this not support UNICODE escapes
453 */
454 private static String unescape(final String s) {
455
456 URLDecoder ud = new URLDecoder();
457
458 String decoded = null;
459
460 try {
461 decoded = ud.decode( s, "UTF-8" );
462 } catch ( java.io.UnsupportedEncodingException uee ) {
463 System.err.println( "unsupported encoding" );
464 return s;
465 }
466 return decoded;
467 }
468
469}
Note: See TracBrowser for help on using the repository browser.