/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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 org.pentaho.di.pkg;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.util.EnvUtil;
import org.pentaho.di.core.util.StreamLogger;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.version.BuildVersion;
public class JarfileGenerator {
private static LogChannelInterface log;
public static final String TRANSFORMATION_FILENAME = "transformation.xml";
public static final void generateJarFile( TransMeta transMeta ) {
log = new LogChannel( "Jar file generator" );
KettleDependencies deps = new KettleDependencies( transMeta );
File kar = new File( "kar" );
if ( kar.exists() ) {
log.logBasic( "Jar generator", "Removing directory: " + kar.getPath() );
deleteDirectory( kar );
}
kar.mkdir();
String filename = "kettle-engine-3.0.jar";
if ( !Utils.isEmpty( transMeta.getFilename() ) ) {
filename = Const.replace( transMeta.getFilename(), " ", "_" ).toLowerCase() + ".kar";
}
File karFile = new File( filename );
try {
// The manifest file
String strManifest = "";
strManifest += "Manifest-Version: 1.0" + Const.CR;
strManifest += "Created-By: Kettle version " + BuildVersion.getInstance().getVersion() + Const.CR;
strManifest += Attributes.Name.MAIN_CLASS.toString() + ": " + ( JarPan.class.getName() ) + Const.CR;
// Create a new manifest file in the root.
File manifestFile = new File( kar.getPath() + "/" + "manifest.mf" );
FileOutputStream fos = new FileOutputStream( manifestFile );
fos.write( strManifest.getBytes() );
fos.close();
log.logBasic( "Jar generator", "Wrote manifest file: " + manifestFile.getPath() );
// The transformation, also in the kar directory...
String strTrans = XMLHandler.getXMLHeader( Const.XML_ENCODING ) + transMeta.getXML();
File transFile = new File( kar.getPath() + "/" + TRANSFORMATION_FILENAME );
fos = new FileOutputStream( transFile );
fos.write( strTrans.getBytes( Const.XML_ENCODING ) );
fos.close();
log.logBasic( "Jar generator", "Wrote transformation file: " + transFile.getPath() );
// Execute the jar command...
executeJarCommand( kar, karFile, new File( "manifest.mf" ), new File( TRANSFORMATION_FILENAME ), deps
.getLibraryFiles() );
} catch ( Exception e ) {
log.logError( JarfileGenerator.class.getName(), "Error zipping files into archive ["
+ karFile.getPath() + "] : " + e.toString() );
log.logError( JarfileGenerator.class.getName(), Const.getStackTracker( e ) );
}
}
private static final void executeJarCommand( File karDirectory, File karFile, File manifestFile, File transFile,
String[] libs ) throws IOException, InterruptedException {
for ( int i = 0; i < libs.length; i++ ) {
List<String> commands = new ArrayList<String>();
commands.add( "jar" );
commands.add( "xf" );
commands.add( "../" + libs[i] );
String[] cmd = commands.toArray( new String[commands.size()] );
executeCommand( cmd, karDirectory );
}
List<String> commands = new ArrayList<String>();
commands.add( "jar" );
commands.add( "cf" );
commands.add( karFile.getPath() );
commands.add( "-m" );
commands.add( manifestFile.getPath() );
commands.add( transFile.getPath() );
commands.add( "build_version.txt" );
commands.add( "log4j.xml" );
String[] directories = getSubdirectories( karDirectory );
for ( int i = 0; i < directories.length; i++ ) {
if ( !directories[i].toUpperCase().equals( "META-INF" ) ) {
commands.add( directories[i] );
}
}
String[] cmd = commands.toArray( new String[commands.size()] );
executeCommand( cmd, karDirectory );
}
private static void executeCommand( String[] cmd, File directory ) throws IOException, InterruptedException {
String command = "";
for ( int i = 0; i < cmd.length; i++ ) {
command += " " + cmd[i];
}
log.logBasic( "Jar generator", "Executing command : " + command );
Runtime runtime = java.lang.Runtime.getRuntime();
Process proc = runtime.exec( cmd, EnvUtil.getEnvironmentVariablesForRuntimeExec(), directory );
// any error message?
StreamLogger errorLogger = new StreamLogger( log, proc.getErrorStream(), "Jar generator (stderr)" );
// any output?
StreamLogger outputLogger = new StreamLogger( log, proc.getInputStream(), "Jar generator (stdout)" );
// kick them off
new Thread( errorLogger ).start();
new Thread( outputLogger ).start();
proc.waitFor();
log.logDetailed( "Jar generator", "command [" + cmd[0] + "] has finished" );
// What's the exit status?
if ( proc.exitValue() != 0 ) {
log.logDetailed( "Jar generator", "Exit status of jar command was " + proc.exitValue() );
}
// close the streams
// otherwise you get "Too many open files, java.io.IOException" after a lot of iterations
try {
proc.getErrorStream().close();
proc.getInputStream().close();
} catch ( IOException e ) {
log.logDetailed( "Jar generator", "Warning: Error closing streams: " + e.getMessage() );
}
}
private static void deleteDirectory( File dir ) {
File[] files = dir.listFiles();
for ( int i = 0; i < files.length; i++ ) {
if ( files[i].isDirectory() ) {
deleteDirectory( files[i] );
}
files[i].delete();
}
dir.delete();
}
private static String[] getSubdirectories( File dir ) {
List<String> directories = new ArrayList<String>();
File[] files = dir.listFiles();
for ( int i = 0; i < files.length; i++ ) {
if ( files[i].isDirectory() ) {
directories.add( files[i].getName() );
}
}
return directories.toArray( new String[directories.size()] );
}
}