/*
* 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;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import net.rim.tumbler.config.WidgetConfig;
import net.rim.tumbler.exception.CommandLineException;
import net.rim.tumbler.exception.PackageException;
import net.rim.tumbler.exception.ValidationException;
import net.rim.tumbler.file.FileManager;
import net.rim.tumbler.log.LogType;
import net.rim.tumbler.log.Logger;
import net.rim.tumbler.rapc.Rapc;
import net.rim.tumbler.serialize.WidgetConfigSerializer;
import net.rim.tumbler.serialize.WidgetConfig_v1Serializer;
import net.rim.tumbler.session.BBWPProperties;
import net.rim.tumbler.session.SessionManager;
import net.rim.tumbler.xml.ConfigXMLParser;
import net.rim.tumbler.xml.XMLParser;
public class WidgetPackager {
public static final String[] STANDARD_OUTPUTS = new String[] { ".cod", ".alx", ".cso", ".csl" };
public static final String[] OTA_OUTPUTS = new String[] { ".cod", ".jad" };
// TODO: retrieve from logger
public static final String BLACKBERRY_WIDGET_PORTAL_URL = "http://www.blackberry.com/developers/webworkssdk/";
public static final String PROPERTIES_FILE = "bbwp.properties";
public static final String SIGNATURE_KEY_FILE = "sigtool.csk";
private static final String AUTOGEN_FILE = "blackberry/web/widget/autogen/WidgetConfigAutoGen.java";
private static final int NO_ERROR_RETURN_CODE = 0;
private static final int PACKAGE_ERROR_RCODE = 1;
private static final int VALIDATION_ERROR_RCODE = 2;
private static final int RUNTIME_ERROR_RCODE = 3;
private static final int UNEXPECTED_ERROR_RCODE = 4;
private static final int COMMAND_LINE_EXCEPTION = 5;
public static void main( String[] args ) {
WidgetPackager wp = new WidgetPackager();
int returnCode = wp.go( args );
System.exit( returnCode );
}
public int go( String[] args ) {
Logger.logMessage( LogType.INFO, "PROGRESS_CMDLINE_OPTIONS" );
int returnCode = NO_ERROR_RETURN_CODE;
try {
CmdLineHandler cmd = new CmdLineHandler();
if( !cmd.parse( args ) ) {
// nothing to package
return NO_ERROR_RETURN_CODE;
}
// create SessionManager
SessionManager sessionManager = cmd.createSession();
// create bbwp.properties
Logger.logMessage( LogType.INFO, "PROGRESS_SESSION_BBWP_PROPERTIES" );
String propertiesFile = sessionManager.getBBWPJarFolder() + WidgetPackager.PROPERTIES_FILE;
BBWPProperties bbwpProperties = new BBWPProperties( propertiesFile, sessionManager.getSessionHome() );
// validate BlackBerry WebWorks application archive
Logger.logMessage( LogType.INFO, "PROGRESS_VALIDATING_WIDGET_ARCHIVE" );
WidgetArchive wa = new WidgetArchive( sessionManager.getWidgetArchive() );
wa.validate();
// parse/validate config.xml
Logger.logMessage( LogType.INFO, "PROGRESS_SESSION_CONFIGXML" );
XMLParser xmlparser = new ConfigXMLParser();
WidgetConfig config = xmlparser.parseXML( wa ); // raw data, without \
// create/clean outputs/source
// Logger.printInfoMessage("BlackBerry WebWorks application packaging starts...");
FileManager fileManager = new FileManager( bbwpProperties, config.getAccessTable() );
Logger.logMessage( LogType.INFO, "PROGRESS_FILE_POPULATING_SOURCE" );
fileManager.prepare();
// Set 3rd party extension classes
config.setExtensionClasses( fileManager.getExtensionClasses() );
config.setExtensionJSFiles( fileManager.getExtensionJSFiles() );
config.setSharedGlobalJSFiles( fileManager.getSharedGlobalJSFiles() );
// create autogen file
WidgetConfigSerializer wcs = new WidgetConfig_v1Serializer( config );
byte[] autogenFile = wcs.serialize();
fileManager.writeToSource( autogenFile, AUTOGEN_FILE );
// create jdw/jdp files
fileManager.generateProjectFiles( sessionManager.getSourceFolder(), sessionManager.getArchiveName(),
config.getName(), config.getVersion(), config.getAuthor(), config.getContent(), config.getBackgroundSource(),
config.isStartupEnabled(), config.getIconSrc(), config.getHoverIconSrc(), fileManager.getFiles(),
bbwpProperties.getImports() );
// run rapc
Logger.logMessage( LogType.INFO, "PROGRESS_COMPILING" );
Rapc rapc = new Rapc( bbwpProperties, config, fileManager.getCompiledJARDependencies() );
if( !rapc.run( fileManager.getFiles() ) ) {
throw new PackageException( "EXCEPTION_RAPC" );
}
// generate ALX
generateAlxFile( config );
// Sign the cod if required
if( sessionManager.requireSigning() ) {
Logger.logMessage( LogType.INFO, "PROGRESS_SIGNING" );
signCod( sessionManager );
Logger.logMessage( LogType.INFO, "PROGRESS_SIGNING_COMPLETE" );
}
// If requires source and a safe source folder is used, copy source to where the user expects it to be
if( sessionManager.requireSource()
&& !sessionManager.getOriginalSourceFolder().equals( sessionManager.getSourceFolder() ) ) {
File f = new File( SessionManager.getInstance().getOriginalSourceFolder() );
if( !f.exists() ) {
if( f.mkdirs() == false ) {
Logger.logMessage( LogType.WARNING, "EXCEPTION_MAKING_DIRECTORY" );
}
}
ExecUtil.exec(
null,
new String[] { "cp", "-R", sessionManager.getSourceFolder() + "/",
sessionManager.getOriginalSourceFolder() }, null );
}
// clean/prep output folders
fileManager.cleanOutput();
// copy output files
Logger.logMessage( LogType.INFO, "PROGRESS_GEN_OUTPUT" );
fileManager.copyOutputsFromSource();
// clean source (if necessary)
if( !sessionManager.requireSource() ) {
fileManager.cleanSource();
}
Logger.logMessage( LogType.INFO, "PROGRESS_COMPLETE" );
} catch( CommandLineException cle ) {
Logger.logMessage( LogType.ERROR, cle.getMessage(), cle.getInfo() );
Logger.logMessage( LogType.NONE, "BBWP_USAGE", getVersion() );
returnCode = COMMAND_LINE_EXCEPTION;
} catch( PackageException pe ) {
Logger.logMessage( LogType.ERROR, pe.getMessage(), pe.getInfo() );
returnCode = PACKAGE_ERROR_RCODE;
} catch( ValidationException ve ) {
Logger.logMessage( LogType.ERROR, ve.getMessage(), ve.getInfo() );
returnCode = VALIDATION_ERROR_RCODE;
} catch( RuntimeException re ) {
Logger.logMessage( LogType.FATAL, re );
returnCode = RUNTIME_ERROR_RCODE;
} catch( Exception e ) {
Logger.logMessage( LogType.ERROR, e );
returnCode = UNEXPECTED_ERROR_RCODE;
}
return returnCode;
}
public static Object[] getVersion() {
return new Object[] { new WidgetPackager().getClass().getPackage().getImplementationVersion() };
}
private static void signCod( SessionManager sessionManager ) throws Exception {
Process signingProcess;
long lastModified = 0;
String codFullname = sessionManager.getSourceFolder() + System.getProperty( "file.separator" )
+ sessionManager.getArchiveName() + ".cod";
try {
lastModified = ( new File( codFullname ) ).lastModified();
String password = sessionManager.getPassword();
File cwd = new File( sessionManager.getBBWPJarFolder() );
String cmdline = "java -jar SignatureTool.jar -a -c " + ( password.length() == 0 ? "" : "-p " + password + " " );
String codName = sessionManager.getSourceFolder() + System.getProperty( "file.separator" )
+ sessionManager.getArchiveName() + ".cod";
if( OSUtils.isWindows() ) {
codName = "\"" + codName + "\"";
}
cmdline += codName;
signingProcess = Runtime.getRuntime().exec( cmdline, null, cwd );
} catch( IOException ex ) {
throw ex;
}
try {
int signingResult = signingProcess.waitFor();
// Check whether signing is successful
if( signingResult != 0 ) {
throw new PackageException( "EXCEPTION_SIGNING_FAILED" );
}
long newModified = ( new File( codFullname ) ).lastModified();
if( newModified == lastModified ) {
throw new PackageException( "EXCEPTION_SIGNING_FAILED" );
}
} catch( InterruptedException e ) {
throw e;
}
}
// Generate a .alx file
private static void generateAlxFile( WidgetConfig widgetConfig ) throws IOException {
String EOL = System.getProperty( "line.separator" );
String fileName = SessionManager.getInstance().getSourceFolder() + System.getProperty( "file.separator" )
+ SessionManager.getInstance().getArchiveName() + ".alx";
BufferedWriter writer = new BufferedWriter( new FileWriter( fileName ) );
writer.write( "<loader version=\"1.0\" >" + EOL );
writer.write( "<application id=\"" + SessionManager.getInstance().getArchiveName() + "\">" + EOL );
writer.write( "<name>" + widgetConfig.getName() + "</name>" + EOL );
if( widgetConfig.getDescription() != null ) {
writer.write( "<description>" + widgetConfig.getDescription() + "</description>" + EOL );
}
writer.write( "<version>" + widgetConfig.getVersion() + "</version>" + EOL );
if( widgetConfig.getAuthor() != null ) {
writer.write( "<vendor>" + widgetConfig.getAuthor() + "</vendor>" + EOL );
}
if( widgetConfig.getCopyright() != null ) {
writer.write( "<copyright>" + widgetConfig.getCopyright() + "</copyright>" + EOL );
}
writer.write( "<fileset Java=\"1.45\">" + EOL );
writer.write( "<directory>" );
writer.write( "</directory>" + EOL );
writer.write( "<files>" );
writer.write( SessionManager.getInstance().getArchiveName() + ".cod" );
writer.write( "</files>" + EOL );
writer.write( "</fileset>" + EOL );
writer.write( "</application>" + EOL );
writer.write( "</loader>" + EOL );
writer.close();
}
}