KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > phoenix > components > deployer > installer > Installer


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the LICENSE.txt file.
7  */

8 package org.apache.avalon.phoenix.components.deployer.installer;
9
10 import java.io.File JavaDoc;
11 import java.io.FileInputStream JavaDoc;
12 import java.io.FileOutputStream JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.InputStream JavaDoc;
15 import java.io.OutputStream JavaDoc;
16 import java.net.MalformedURLException JavaDoc;
17 import java.net.URL JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.Enumeration JavaDoc;
20 import java.util.zip.CRC32 JavaDoc;
21 import java.util.zip.CheckedInputStream JavaDoc;
22 import java.util.zip.Checksum JavaDoc;
23 import java.util.zip.ZipEntry JavaDoc;
24 import java.util.zip.ZipFile JavaDoc;
25 import org.apache.avalon.excalibur.i18n.ResourceManager;
26 import org.apache.avalon.excalibur.i18n.Resources;
27 import org.apache.avalon.excalibur.io.FileUtil;
28 import org.apache.avalon.excalibur.io.IOUtil;
29 import org.apache.avalon.framework.logger.AbstractLogEnabled;
30
31 /**
32  * An Installer is responsible for taking a URL for Sar
33  * and installing it as appropriate.
34  *
35  * @author <a HREF="mailto:peter at apache.org">Peter Donald</a>
36  * @version $Revision: 1.13 $ $Date: 2002/08/06 11:57:40 $
37  */

38 public class Installer
39     extends AbstractLogEnabled
40 {
41     private static final Resources REZ =
42         ResourceManager.getPackageResources( Installer.class );
43
44     private static final String JavaDoc META_INF = "META-INF";
45
46     private static final String JavaDoc SAR_INF = "SAR-INF";
47
48     private static final String JavaDoc LIB = "SAR-INF/lib";
49
50     private static final String JavaDoc CLASSES = "SAR-INF/classes/";
51
52     //The names on the native filesystem
53
private static final String JavaDoc FS_CONFIG_XML = "SAR-INF" + File.separator + "config.xml";
54
55     private static final String JavaDoc FS_ASSEMBLY_XML = "SAR-INF" + File.separator + "assembly.xml";
56
57     private static final String JavaDoc FS_ENV_XML = "SAR-INF" + File.separator + "environment.xml";
58
59     private static final String JavaDoc FS_CLASSES =
60         "SAR-INF" + File.separator + "classes" + File.separator;
61
62     /**
63      * Base directory in which to install extracted application.
64      */

65     private File JavaDoc m_baseDirectory;
66
67     /**
68      * Base directory in which to install temporary/work files.
69      */

70     private File JavaDoc m_baseWorkDirectory;
71
72     /**
73      * Set the baseDirectory in which to install applications.
74      *
75      * @param baseDirectory the baseDirectory in which to install applications.
76      */

77     public void setBaseDirectory( File JavaDoc baseDirectory )
78     {
79         m_baseDirectory = baseDirectory;
80     }
81
82     /**
83      * Set the baseDirectory in which to install applications temporary Data.
84      *
85      * @param baseWorkDirectory the baseDirectory in which to install applications temporary Data.
86      */

87     public void setBaseWorkDirectory( File JavaDoc baseWorkDirectory )
88     {
89         m_baseWorkDirectory = baseWorkDirectory;
90     }
91
92     /**
93      * Uninstall the Sar designated installation.
94      *
95      * @param installation the installation
96      * @throws InstallationException if an error occurs
97      */

98     public void uninstall( final Installation installation )
99         throws InstallationException
100     {
101         final FileDigest[] infos = installation.getFileDigests();
102         final Checksum JavaDoc checksum = new CRC32 JavaDoc();
103
104         if( infos != null )
105         {
106             for( int i = 0; i < infos.length; i++ )
107             {
108                 final File JavaDoc file = infos[ i ].getFile();
109                 final File JavaDoc parent = file.getParentFile();
110
111                 final String JavaDoc message = REZ.getString( "skip-removal", file );
112
113                 if( file.exists() )
114                 {
115                     if( file.lastModified() <= installation.getTimestamp() )
116                     {
117                         getLogger().debug( message );
118                         continue;
119                     }
120
121                     checksum( file, checksum );
122
123                     if( checksum.getValue() != infos[ i ].getChecksum() )
124                     {
125                         getLogger().debug( message );
126                         continue;
127                     }
128
129                     file.delete();
130                     if( 0 == parent.list().length )
131                     {
132                         parent.delete();
133                     }
134                 }
135             }
136         }
137
138         deleteWorkDir( installation.getWorkDirectory() );
139     }
140
141     /**
142      * Utility method to delete the working directory.
143      *
144      * @param dir the working directory
145      */

146     private void deleteWorkDir( final File JavaDoc dir )
147     {
148         try
149         {
150             FileUtil.deleteDirectory( dir );
151         }
152         catch( final IOException JavaDoc ioe )
153         {
154             try
155             {
156                 //If can't delete them now (damn windows locking!)
157
//then schedule them for deletion when JVM exits
158
FileUtil.forceDeleteOnExit( dir );
159             }
160             catch( final IOException JavaDoc ioe2 )
161             {
162                 //ignore
163
}
164             final String JavaDoc message =
165                 REZ.getString( "nodelete-workdir.error",
166                                dir,
167                                ioe.getMessage() );
168             getLogger().warn( message, ioe );
169         }
170     }
171
172     /**
173      * Install the Sar designated by url.
174      *
175      * @param url the url of instalation
176      * @throws InstallationException if an error occurs
177      */

178     public Installation install( final String JavaDoc name, final URL JavaDoc url )
179         throws InstallationException
180     {
181         lock();
182         try
183         {
184             final String JavaDoc notice = REZ.getString( "installing-sar", url );
185             getLogger().info( notice );
186
187             final File JavaDoc file = getFileFor( url );
188             if( file.isDirectory() )
189             {
190                 final String JavaDoc message =
191                     REZ.getString( "install.sar-isa-dir.error", name, url );
192                 throw new InstallationException( message );
193             }
194
195             //Get Zipfile representing .sar file
196
final ZipFile JavaDoc zipFile = new ZipFile JavaDoc( file );
197             return install( name, url, file, zipFile );
198         }
199         catch( final IOException JavaDoc ioe )
200         {
201             final String JavaDoc message = REZ.getString( "bad-zip-file", url );
202             throw new InstallationException( message, ioe );
203         }
204         finally
205         {
206             unlock();
207         }
208     }
209
210     /**
211      * Utility method to compute the checksum for a given file.
212      * @param file the computed file.
213      * @param checksum the checksum algorithm.
214      */

215     private void checksum( final File JavaDoc file, final Checksum JavaDoc checksum )
216     {
217         checksum.reset();
218
219         InputStream JavaDoc input = null;
220         try
221         {
222             input = new CheckedInputStream JavaDoc( new FileInputStream JavaDoc( file ), checksum );
223             IOUtil.toByteArray( input );
224         }
225         catch( final IOException JavaDoc ioe )
226         {
227             final String JavaDoc message = REZ.getString( "checksum-failure", file );
228             getLogger().warn( message );
229         }
230         finally
231         {
232             IOUtil.shutdownStream( input );
233         }
234     }
235
236     /**
237      * Utility method to lock repository to disallow other installers to access it.
238      * Currently a no-op.
239      */

240     private void lock()
241     {
242     }
243
244     /**
245      * Utility method to unlock repository to allow other installers to access it.
246      * Currently a no-op.
247      */

248     private void unlock()
249     {
250     }
251
252     /**
253      * Install a new style sar.
254      *
255      * @param url the url designator of sar
256      * @param file the file of sar
257      * @param zipFile the ZipFile representing sar
258      * @return the Installation object
259      */

260     private Installation install( final String JavaDoc name,
261                                   final URL JavaDoc url,
262                                   final File JavaDoc file,
263                                   final ZipFile JavaDoc zipFile )
264         throws InstallationException
265     {
266         final File JavaDoc directory =
267             new File JavaDoc( m_baseDirectory, name ).getAbsoluteFile();
268
269         //Question: Should we be making sure that
270
//this directory is created?
271
directory.mkdirs();
272
273         final ArrayList JavaDoc digests = new ArrayList JavaDoc();
274         final ArrayList JavaDoc jars = new ArrayList JavaDoc();
275
276         final File JavaDoc workDir =
277             getRelativeWorkDir( m_baseWorkDirectory, name );
278         boolean success = false;
279         try
280         {
281             expandZipFile( zipFile, directory, workDir, jars, digests, url );
282
283             //Retrieve name of environment file
284
//need to check existence to support backwards compatability
285
final File JavaDoc envFile = new File JavaDoc( directory, FS_ENV_XML );
286
287             //Prepare and create Installation
288
final String JavaDoc[] classPath =
289                 (String JavaDoc[])jars.toArray( new String JavaDoc[ jars.size() ] );
290
291             final String JavaDoc assembly = getURLAsString( new File JavaDoc( directory, FS_ASSEMBLY_XML ) );
292             final String JavaDoc config = getURLAsString( new File JavaDoc( directory, FS_CONFIG_XML ) );
293             final String JavaDoc environment = getURLAsString( envFile );
294             final FileDigest[] fileDigests = (FileDigest[])digests.toArray( new FileDigest[ 0 ] );
295             final long timestamp = System.currentTimeMillis();
296
297             success = true;
298             return new Installation( file, directory, workDir,
299                                      config, assembly, environment,
300                                      classPath, fileDigests, timestamp );
301         }
302         finally
303         {
304             if( !success )
305             {
306                 deleteWorkDir( workDir );
307             }
308         }
309     }
310
311     /**
312      * Expand the specified Zip file.
313      *
314      * @param zipFile the zip file
315      * @param directory the directory where to extract non-jar,
316      * non-classes files
317      * @param workDir the directory to extract classes/jar files
318      * @param classpath the list to add classpath entries to
319      * @param digests the list to add file digests to
320      * @param url the url of deployment (for error reporting purposes)
321      * @throws InstallationException if an error occurs extracting files
322      */

323     private void expandZipFile( final ZipFile JavaDoc zipFile,
324                                 final File JavaDoc directory,
325                                 final File JavaDoc workDir,
326                                 final ArrayList JavaDoc classpath,
327                                 final ArrayList JavaDoc digests,
328                                 final URL JavaDoc url )
329         throws InstallationException
330     {
331         final Enumeration JavaDoc entries = zipFile.entries();
332         while( entries.hasMoreElements() )
333         {
334             final ZipEntry JavaDoc entry = (ZipEntry JavaDoc)entries.nextElement();
335             final String JavaDoc name = fixName( entry.getName() );
336
337             if( name.startsWith( META_INF ) )
338             {
339                 continue;
340             }
341
342             if( handleDirs( entry, name, directory ) )
343             {
344                 continue;
345             }
346
347             if( handleClasses( zipFile,
348                                entry,
349                                name,
350                                workDir,
351                                classpath ) )
352             {
353                 continue;
354             }
355
356             if( handleJars( zipFile, entry, name, workDir, classpath ) )
357             {
358                 continue;
359             }
360
361             //Expand the file if necesasry and issue a warning
362
//if there is a file in the way
363
final File JavaDoc destination = new File JavaDoc( directory, name );
364             handleFile( zipFile, entry, destination, digests, url );
365         }
366     }
367
368     /**
369      * Handle the extraction of normal resources
370      * from zip file/
371      */

372     private void handleFile( final ZipFile JavaDoc zipFile,
373                              final ZipEntry JavaDoc entry,
374                              final File JavaDoc destination,
375                              final ArrayList JavaDoc digests,
376                              final URL JavaDoc url )
377         throws InstallationException
378     {
379         if( !destination.exists() )
380         {
381             expandFile( zipFile, entry, destination );
382             calculateDigest( entry, destination, digests );
383         }
384         else
385         {
386             final String JavaDoc message =
387                 REZ.getString( "file-in-the-way",
388                                url,
389                                entry.getName(),
390                                destination );
391             getLogger().warn( message );
392         }
393     }
394
395     /**
396      * Handle extraction of jars.
397      *
398      * @param zipFile the zipFIle to exrtact from
399      * @param entry the entry to extract
400      * @param name the normalized name of entry
401      * @param workDir the working directory to extract to
402      * @param jars the classpath list
403      * @return true if handled, false otherwise
404      */

405     private boolean handleJars( final ZipFile JavaDoc zipFile,
406                                 final ZipEntry JavaDoc entry,
407                                 final String JavaDoc name,
408                                 final File JavaDoc workDir,
409                                 final ArrayList JavaDoc jars )
410         throws InstallationException
411     {
412         if( name.startsWith( LIB )
413             && name.endsWith( ".jar" )
414             && LIB.length() == name.lastIndexOf( "/" ) )
415         {
416             final File JavaDoc jar = new File JavaDoc( workDir, name );
417             jars.add( getURLAsString( jar ) );
418
419             final File JavaDoc file = new File JavaDoc( workDir, name );
420             expandFile( zipFile, entry, file );
421             return true;
422         }
423         else
424         {
425             return false;
426         }
427     }
428
429     /**
430      * Handle extraction of jars.
431      *
432      * @param zipFile the zipFIle to exrtact from
433      * @param entry the entry to extract
434      * @param name the normalized name of entry
435      * @param workDir the working directory to extract to
436      * @param jars the classpath list
437      * @return true if handled, false otherwise
438      */

439     private boolean handleClasses( final ZipFile JavaDoc zipFile,
440                                    final ZipEntry JavaDoc entry,
441                                    final String JavaDoc name,
442                                    final File JavaDoc workDir,
443                                    final ArrayList JavaDoc jars )
444         throws InstallationException
445     {
446         if( name.startsWith( CLASSES ) )
447         {
448             final File JavaDoc classDir = new File JavaDoc( workDir, FS_CLASSES );
449             if( !classDir.exists() )
450             {
451                 jars.add( getURLAsString( classDir ) );
452                 final File JavaDoc file = new File JavaDoc( workDir, name );
453                 expandFile( zipFile, entry, file );
454             }
455             return true;
456         }
457         else
458         {
459             return false;
460         }
461     }
462
463     /**
464      * Handle expansion of dirs in the zipfile.
465      *
466      * @param entry the current ZipEntry
467      * @param name the name of entry
468      * @param directory the base directory extraacting to
469      * @return true if handled, false otherwise
470      */

471     private boolean handleDirs( final ZipEntry JavaDoc entry,
472                                 final String JavaDoc name,
473                                 final File JavaDoc directory )
474     {
475         if( entry.isDirectory() )
476         {
477             if( !name.startsWith( SAR_INF ) )
478             {
479                 final File JavaDoc newDir =
480                     new File JavaDoc( directory, name );
481                 newDir.mkdirs();
482             }
483             return true;
484         }
485         else
486         {
487             return false;
488         }
489     }
490
491     /**
492      * Create working directory inside baseWorkDir
493      * for specified application.
494      *
495      * @param baseWorkDir the base workDir for all apps
496      * @param name the name of the application
497      * @return the working directory for app
498      */

499     private File JavaDoc getRelativeWorkDir( final File JavaDoc baseWorkDir,
500                                      final String JavaDoc name )
501     {
502         final String JavaDoc filename =
503             name + "-" + System.currentTimeMillis();
504         return new File JavaDoc( baseWorkDir, filename );
505     }
506
507     /**
508      * Fix the specified name so that it does not start
509      * with a "/" character.
510      *
511      * @param name the name to fix
512      * @return the name stripped of initial "/" if necessary
513      */

514     private String JavaDoc fixName( final String JavaDoc name )
515     {
516         if( name.startsWith( "/" ) )
517         {
518             return name.substring( 1 );
519         }
520         else
521         {
522             return name;
523         }
524     }
525
526     /**
527      * Get File object for URL.
528      * Currently it assumes that URL is a file URL but in the
529      * future it will allow downloading of remote URLs thus enabling
530      * a deploy from anywhere functionality.
531      *
532      * @param url the url of deployment
533      * @return the File for deployment
534      * @throws InstallationException if an error occurs
535      */

536     private File JavaDoc getFileFor( final URL JavaDoc url )
537         throws InstallationException
538     {
539         if( !url.getProtocol().equals( "file" ) )
540         {
541             final String JavaDoc message = REZ.getString( "install-nonlocal", url );
542             throw new InstallationException( message );
543         }
544
545         File JavaDoc file = new File JavaDoc( url.getFile() );
546         file = file.getAbsoluteFile();
547
548         if( !file.exists() )
549         {
550             final String JavaDoc message = REZ.getString( "install-nourl", file );
551             throw new InstallationException( message );
552         }
553
554         return file;
555     }
556
557     /**
558      * Calculate digest for specific entry.
559      *
560      * @param entry the entry
561      * @param file the extracted file
562      * @param digests the list of digests already
563      * calculated
564      */

565     private void calculateDigest( final ZipEntry JavaDoc entry,
566                                   final File JavaDoc file,
567                                   final ArrayList JavaDoc digests )
568     {
569         final long checksum = entry.getCrc();
570         digests.add( new FileDigest( file, checksum ) );
571     }
572
573     /**
574      * Expand a single zipEntry to a file.
575      */

576     private void expandFile( final ZipFile JavaDoc zipFile,
577                              final ZipEntry JavaDoc entry,
578                              final File JavaDoc file )
579         throws InstallationException
580     {
581         InputStream JavaDoc input = null;
582         OutputStream JavaDoc output = null;
583
584         try
585         {
586             file.getParentFile().mkdirs();
587             output = new FileOutputStream JavaDoc( file );
588             input = zipFile.getInputStream( entry );
589             IOUtil.copy( input, output );
590         }
591         catch( final IOException JavaDoc ioe )
592         {
593             final String JavaDoc message =
594                 REZ.getString( "failed-to-expand",
595                                entry.getName(),
596                                file,
597                                ioe.getMessage() );
598             throw new InstallationException( message, ioe );
599         }
600         finally
601         {
602             IOUtil.shutdownStream( input );
603             IOUtil.shutdownStream( output );
604         }
605     }
606
607     /**
608      * Utility method to extract URL from file in safe manner.
609      *
610      * @param file the file
611      * @return the URL representation of file
612      */

613     private String JavaDoc getURLAsString( final File JavaDoc file )
614     {
615         try
616         {
617             return file.toURL().toExternalForm();
618         }
619         catch( final MalformedURLException JavaDoc mue )
620         {
621             return null;
622             //should never occur
623
}
624     }
625 }
626
Popular Tags