source: release-kits/lirk3/bin/ant-installer/src/org/tp23/antinstaller/selfextract/SelfExtractor.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

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