/* * Copyright 2010-2011 Research In Motion Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.rim.tumbler.file; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Vector; import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import net.rim.tumbler.WidgetPackager; import net.rim.tumbler.config.FeatureManager; import net.rim.tumbler.config.WidgetAccess; import net.rim.tumbler.config.WidgetFeature; import net.rim.tumbler.exception.PackageException; import net.rim.tumbler.log.LogType; import net.rim.tumbler.log.Logger; import net.rim.tumbler.session.BBWPProperties; import net.rim.tumbler.session.SessionManager; public class FileManager { private BBWPProperties _bbwpProperties; private Vector< String > _outputFiles; private FeatureManager _featureManager; private static final String NL = System.getProperty( "line.separator" ); private static final String FILE_SEP = System.getProperty( "file.separator" ); private static final String STANDARD_OUTPUT = "StandardInstall"; private static final String OTA_OUTPUT = "OTAInstall"; private static final String NET_RIM_API = "net_rim_api.jar"; public FileManager( BBWPProperties bbwpProperties, Hashtable< WidgetAccess, Vector< WidgetFeature >> accessTable ) { _bbwpProperties = bbwpProperties; _outputFiles = new Vector< String >(); _featureManager = new FeatureManager( bbwpProperties, accessTable ); } public List< String > getFiles() { return _outputFiles; } public void cleanOutput() { String outputDir = SessionManager.getInstance().getOutputFolder(); String archiveName = SessionManager.getInstance().getArchiveName(); deleteDirectory( new File( outputDir + FILE_SEP + FileManager.OTA_OUTPUT ) ); deleteDirectory( new File( outputDir + FILE_SEP + FileManager.STANDARD_OUTPUT ) ); File jarFile = new File( outputDir + FILE_SEP + archiveName + ".jar" ); if( jarFile.exists() && jarFile.delete() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_DELETING_FILE", jarFile.getAbsolutePath() ); } File rapcFile = new File( outputDir + FILE_SEP + archiveName + ".rapc" ); if( rapcFile.exists() && rapcFile.delete() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_DELETING_FILE", rapcFile.getAbsolutePath() ); } } public void cleanSource() { deleteDirectory( new File( SessionManager.getInstance().getSourceFolder() ) ); } public void prepare() throws Exception { // Clean out source folder deleteDirectory( new File( SessionManager.getInstance().getSourceFolder() ) ); File f = new File( SessionManager.getInstance().getSourceFolder() ); if( !( f.exists() && f.isDirectory() ) ) { if( f.mkdirs() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY" ); } } // Copy templates try { TemplateWrapper templateWrapper = new TemplateWrapper( _bbwpProperties ); _outputFiles.addAll( templateWrapper.writeAllTemplates( SessionManager.getInstance().getSourceFolder() ) ); } catch( IOException ex ) { throw new PackageException( "EXCEPTION_IO_TEMPLATES" ); } // Extract archive String sourceFolder = SessionManager.getInstance().getSourceFolder(); ZipFile zip = new ZipFile( new File( SessionManager.getInstance().getWidgetArchive() ).getAbsolutePath() ); Enumeration< ? > en = zip.entries(); HashSet< ZipEntry > extJars = new HashSet< ZipEntry >(); while( en.hasMoreElements() ) { // Create output file name ZipEntry ze = (ZipEntry) en.nextElement(); if( ze.isDirectory() ) continue; String zipEntryName = ze.getName(); File zipEntryFile = new File( ze.getName() ); boolean isRoot = zipEntryFile.getParent() == null; String fname = sourceFolder + FILE_SEP + zipEntryFile.getPath(); if( zipEntryName.startsWith( "ext" ) && zipEntryName.endsWith( ".jar" ) ) { extJars.add( ze ); } else { // Extract file copyZipEntry( zip, ze, sourceFolder + FILE_SEP ); // Hack for icon files not displayed properly if similar named // files exist in sub folders if( !isRoot ) { _outputFiles.add( 0, fname ); } else { _outputFiles.add( fname ); } } } copyExtensionFiles( zip, extJars ); } /** * @param zip * WebWorks application archive * @param extJars * extension JAR zip entries found in archive's "ext" folder * @throws Exception * if there is extensions cannot be resolved */ private void copyExtensionFiles( ZipFile zip, HashSet< ZipEntry > extJars ) throws Exception { HashSet< String > resolvedExtensionPaths = _featureManager.resolveFeatures( zip, extJars ); _outputFiles.addAll( resolvedExtensionPaths ); for( String path : _featureManager.getCommonAPIPaths() ) { _outputFiles.add( 0, path ); } } public static File copyZipEntry( ZipFile zipFile, ZipEntry zipEntry, String dest ) throws IOException { String fname = dest; if( dest.endsWith( FILE_SEP ) ) { fname = dest + new File( zipEntry.getName() ).getPath(); } InputStream is = zipFile.getInputStream( zipEntry ); File fi = new File( fname ); if( !fi.getParentFile().isDirectory() || !fi.getParentFile().exists() ) { if( fi.getParentFile().mkdirs() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY", fi.getParentFile().toString() ); } } FileOutputStream fos = new FileOutputStream( fname ); int bytesRead; while( ( bytesRead = is.read() ) != -1 ) { fos.write( bytesRead ); } fos.close(); return fi; } // Generate .jdp and .jdw files public void generateProjectFiles( String sourceDir, String codName, String appName, String appVersion, String appVendor, String contentSource, String backgroundSource, boolean isStartupEnabled, Vector< String > icons, Vector< String > hoverIcons, List< String > inputFiles, List< String > libraryFiles ) throws IOException { String fileName; BufferedWriter writer; // jdw file fileName = sourceDir + FILE_SEP + codName + ".jdw"; writer = new BufferedWriter( new FileWriter( fileName ) ); writer.write( "## RIM Java Development Environment" + NL ); writer.write( "# RIM Workspace file" + NL ); writer.write( "#" + NL ); writer.write( "# This file is generated and managed by BlackBerry developer tools." + NL ); writer.write( "# It SHOULD NOT BE modified manually." + NL ); writer.write( "#" + NL ); writer.write( "[BuildConfigurations" + NL ); writer.write( "Debug" + NL ); writer.write( "Release" + NL ); writer.write( "]" + NL ); writer.write( "DependenciesInWorkspace=0" + NL ); writer.write( "[ImplicitRules" + NL ); writer.write( "]" + NL ); writer.write( "[Imports" + NL ); writer.write( "]" + NL ); writer.write( "[Projects" + NL ); writer.write( codName + ".jdp" + NL ); // Alternate entry project if( backgroundSource != null && isStartupEnabled ) { writer.write( "runOnStartup.jdp" + NL ); } writer.write( "]" + NL ); writer.write( "[ReleaseActiveProjects" + NL ); writer.write( codName + ".jdp" + NL ); writer.write( "]" + NL ); writer.close(); // jdp file fileName = sourceDir + FILE_SEP + codName + ".jdp"; writer = new BufferedWriter( new FileWriter( fileName ) ); writer.write( "## RIM Java Development Environment" + NL ); writer.write( "# RIM Project file" + NL ); writer.write( "#" + NL ); writer.write( "# This file is generated and managed by BlackBerry developer tools." + NL ); writer.write( "# It SHOULD NOT BE modified manually." + NL ); writer.write( "#" + NL ); writer.write( "AddOn=0" + NL ); writer.write( "AlwaysBuild=0" + NL ); writer.write( "[AlxImports" + NL ); writer.write( "]" + NL ); writer.write( "AutoRestart=0" + NL ); writer.write( "[ClassProtection" + NL ); writer.write( "]" + NL ); writer.write( "[CustomBuildFiles" + NL ); writer.write( "]" + NL ); writer.write( "[CustomBuildRules" + NL ); writer.write( "]" + NL ); writer.write( "[DefFiles" + NL ); writer.write( "]" + NL ); writer.write( "[DependsOn" + NL ); writer.write( "]" + NL ); writer.write( "ExcludeFromBuildAll=0" + NL ); writer.write( "Exported=0" + NL ); writer.write( "[Files" + NL ); for( int i = 0; i < inputFiles.size(); ++i ) { String inputFile = inputFiles.get( i ); inputFile = inputFile.substring( sourceDir.length() + 1 ); writer.write( inputFile + NL ); } writer.write( "]" + NL ); writer.write( "HaveAlxImports=0" + NL ); writer.write( "HaveDefs=0" + NL ); writer.write( "HaveImports=1" + NL ); writer.write( "[Icons" + NL ); if( icons != null ) { for( int i = 0; i < icons.size(); ++i ) { writer.write( icons.elementAt( i ) + NL ); } } writer.write( "]" + NL ); writer.write( "[ImplicitRules" + NL ); writer.write( "]" + NL ); writer.write( "[Imports" + NL ); for( int i = 0; i < libraryFiles.size(); ++i ) { String libraryFile = libraryFiles.get( i ); // net_rim_api.jar is not supposed to be imported if( !libraryFile.endsWith( ( NET_RIM_API ) ) ) writer.write( libraryFile + NL ); } for( String file : _featureManager.getCompiledJARDependencies() ) { // net_rim_api.jar is not supposed to be imported if( !file.endsWith( NET_RIM_API ) ) writer.write( file + NL ); } writer.write( "]" + NL ); writer.write( "Listing=0" + NL ); if( contentSource != null ) { writer.write( "MidletClass=rim:foreground" + NL ); } else { writer.write( "MidletClass=" + NL ); } writer.write( "Options=-quiet -deprecation" + NL ); writer.write( "OutputFileName=" + codName + NL ); writer.write( "[PackageProtection" + NL ); writer.write( "]" + NL ); writer.write( "Platform=0" + NL ); writer.write( "RibbonPosition=0" + NL ); writer.write( "[RolloverIcons" + NL ); if( hoverIcons != null ) { for( int i = 0; i < hoverIcons.size(); ++i ) { writer.write( hoverIcons.elementAt( i ) + NL ); } } writer.write( "]" + NL ); writer.write( "RunOnStartup=0" + NL ); writer.write( "StartupTier=7" + NL ); if( contentSource != null && contentSource.length() != 0 ) { writer.write( "SystemModule=0" + NL ); } else { writer.write( "SystemModule=1" + NL ); } writer.write( "Title=" + appName + NL ); writer.write( "Type=0" + NL ); if( appVendor != null ) { writer.write( "Vendor=" + appVendor + NL ); } writer.write( "Version=" + appVersion + NL ); writer.close(); // Alternate jdp file // Do not generate the alternate jdp file if it isn't required if( backgroundSource == null || !isStartupEnabled ) { return; } fileName = sourceDir + FILE_SEP + "runOnStartup.jdp"; writer = new BufferedWriter( new FileWriter( fileName ) ); writer.write( "## RIM Java Development Environment" + NL ); writer.write( "# RIM Project file" + NL ); writer.write( "#" + NL ); writer.write( "# This file is generated and managed by BlackBerry developer tools." + NL ); writer.write( "# It SHOULD NOT BE modified manually." + NL ); writer.write( "#" + NL ); writer.write( "AddOn=0" + NL ); writer.write( "AlwaysBuild=0" + NL ); writer.write( "[AlxImports" + NL ); writer.write( "]" + NL ); writer.write( "AutoRestart=0" + NL ); writer.write( "[ClassProtection" + NL ); writer.write( "]" + NL ); writer.write( "[CustomBuildFiles" + NL ); writer.write( "]" + NL ); writer.write( "[CustomBuildRules" + NL ); writer.write( "]" + NL ); writer.write( "[DefFiles" + NL ); writer.write( "]" + NL ); writer.write( "[DependsOn" + NL ); writer.write( "]" + NL ); writer.write( "EntryFor=" + codName + NL ); writer.write( "ExcludeFromBuildAll=0" + NL ); writer.write( "Exported=0" + NL ); writer.write( "[Files" + NL ); writer.write( "]" + NL ); writer.write( "HaveAlxImports=0" + NL ); writer.write( "HaveDefs=0" + NL ); writer.write( "HaveImports=1" + NL ); // list icons for show during startup writer.write( "[Icons" + NL ); if( icons != null ) { for( int i = 0; i < icons.size(); ++i ) { writer.write( icons.elementAt( i ) + NL ); } } writer.write( "]" + NL ); writer.write( "[ImplicitRules" + NL ); writer.write( "]" + NL ); writer.write( "[Imports" + NL ); writer.write( "]" + NL ); writer.write( "Listing=0" + NL ); writer.write( "MidletClass=rim:runOnStartup" + NL ); writer.write( "Options=-quiet -deprecation" + NL ); writer.write( "OutputFileName=" + codName + NL ); writer.write( "[PackageProtection" + NL ); writer.write( "]" + NL ); writer.write( "Platform=0" + NL ); writer.write( "RibbonPosition=0" + NL ); writer.write( "[RolloverIcons" + NL ); if( hoverIcons != null ) { for( int i = 0; i < hoverIcons.size(); ++i ) { writer.write( hoverIcons.elementAt( i ) + NL ); } } writer.write( "]" + NL ); writer.write( "RunOnStartup=1" + NL ); writer.write( "StartupTier=7" + NL ); writer.write( "SystemModule=1" + NL ); writer.write( "Title=" + appName + NL ); writer.write( "Type=3" + NL ); if( appVendor != null ) { writer.write( "Vendor=" + appVendor + NL ); } writer.write( "Version=" + appVersion + NL ); writer.close(); return; } public void writeToSource( byte[] fileToWrite, String relativeFile ) throws Exception { try { String s = SessionManager.getInstance().getSourceFolder() + FILE_SEP + relativeFile; if( !new File( s ).exists() ) { File pf = ( new File( s ) ).getParentFile(); if( pf != null && !( pf.exists() && pf.isDirectory() ) ) { if( pf.mkdirs() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY" ); } } } FileOutputStream fos = new FileOutputStream( s ); fos.write( fileToWrite ); fos.close(); _outputFiles.add( s ); } catch( Exception e ) { throw new PackageException( e, relativeFile ); } } public void copyOutputsFromSource() throws Exception { // TODO: verify for missing files String sourceFolder = SessionManager.getInstance().getSourceFolder(); String outputFolder = SessionManager.getInstance().getOutputFolder(); String archiveName = SessionManager.getInstance().getArchiveName(); createOutputDirs( outputFolder ); // Standard output File from, to; for( String ext : WidgetPackager.STANDARD_OUTPUTS ) { from = new File( sourceFolder + FILE_SEP + archiveName + ext ); to = new File( outputFolder + FILE_SEP + FileManager.STANDARD_OUTPUT + FILE_SEP + archiveName + ext ); copyFile( from, to ); } // OTA output for( String ext : WidgetPackager.OTA_OUTPUTS ) { from = new File( sourceFolder + FILE_SEP + archiveName + ext ); to = new File( outputFolder + FILE_SEP + FileManager.OTA_OUTPUT + FILE_SEP + archiveName + ext ); copyFile( from, to ); } from = new File( sourceFolder + FILE_SEP + archiveName + ".cod" ); expandCod( from ); } private void expandCod( File codFile ) throws Exception { // If the codFile can be unzipped, then the cod is too big // and actually in the zip format with smaller (sibling) cods // inside. Otherwise, the cod is already a good cod. ZipFile zipFile; // Check for file's existence if( !codFile.exists() ) throw new PackageException( "EXCEPTION_COD_NOT_FOUND" ); boolean containsSiblingCods; try { zipFile = new ZipFile( codFile ); zipFile.close(); containsSiblingCods = true; } catch( Exception e ) { containsSiblingCods = false; } if( !containsSiblingCods ) return; FileInputStream fis = new FileInputStream( codFile ); CheckedInputStream checksum = new CheckedInputStream( fis, new Adler32() ); ZipInputStream zis = new ZipInputStream( new BufferedInputStream( checksum ) ); ZipEntry entry; BufferedOutputStream dest = null; final int BUFFER_SIZE = 1024; while( ( entry = zis.getNextEntry() ) != null ) { int count; byte data[] = new byte[ BUFFER_SIZE ]; File f = new File( SessionManager.getInstance().getOutputFolder() + FILE_SEP + "OTAInstall" + FILE_SEP + entry.getName() ); if( f.getParentFile() != null && !( f.getParentFile().exists() && f.getParentFile().isDirectory() ) ) { if( f.getParentFile().mkdirs() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY", f.toString() ); } } if( !f.exists() && f.createNewFile() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_CREATING_FILE", f.toString() ); } // Write the files to the disk FileOutputStream fos = new FileOutputStream( f ); dest = new BufferedOutputStream( fos, BUFFER_SIZE ); while( ( count = zis.read( data, 0, BUFFER_SIZE ) ) != -1 ) { dest.write( data, 0, count ); } dest.flush(); dest.close(); } zis.close(); } // Copy a file public static void copyFile( File in, File out ) throws IOException { FileChannel inChannel = new FileInputStream( in ).getChannel(); FileChannel outChannel = new FileOutputStream( out ).getChannel(); try { // Windows is limited to 64mb chunks long size = inChannel.size(); long position = 0; while( position < size ) position += inChannel.transferTo( position, 67076096, outChannel ); } finally { if( inChannel != null ) inChannel.close(); if( outChannel != null ) outChannel.close(); } } private void createOutputDirs( String outputFolder ) { File standardInstallDir = new File( outputFolder + File.separator + FileManager.STANDARD_OUTPUT ); File otaInstallDir = new File( outputFolder + File.separator + FileManager.OTA_OUTPUT ); if( !( standardInstallDir.exists() && standardInstallDir.isDirectory() ) ) { if( standardInstallDir.mkdirs() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY", standardInstallDir.toString() ); } } if( !( otaInstallDir.exists() && otaInstallDir.isDirectory() ) ) { if( otaInstallDir.mkdirs() == false ) { Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY", otaInstallDir.toString() ); } } } // Delete a dir public static boolean deleteDirectory( File dir ) { // Remove files first if( dir.exists() && dir.isDirectory() ) { String[] children = dir.list(); for( String child : children ) { if( !deleteDirectory( new File( dir, child ) ) ) return false; } } if( dir.exists() ) { // Then remove the directory return dir.delete(); } return false; } public Vector< String > getExtensionClasses() { Vector< String > extClasses = new Vector< String >(); extClasses.addAll( _featureManager.getExtensionClasses() ); return extClasses; } public Vector< String > getSharedGlobalJSFiles() { Vector< String > sharedGlobalJS = new Vector< String >(); sharedGlobalJS.addAll( _featureManager.getSharedGlobalJSFiles() ); return sharedGlobalJS; } public Vector< String > getExtensionJSFiles() { Vector< String > extensionJS = new Vector< String >(); extensionJS.addAll( _featureManager.getExtensionJSFiles() ); return extensionJS; } public List< String > getCompiledJARDependencies() { List< String > jarDependencies = new ArrayList< String >(); jarDependencies.addAll( _featureManager.getCompiledJARDependencies() ); return jarDependencies; } }