/** * Copyright (C) 2008-2010, Squale Project - http://www.squale.org * * This file is part of Squale. * * Squale is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or any later version. * * Squale is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Squale. If not, see <http://www.gnu.org/licenses/>. */ package org.squale.squalix.tools.compiling.java.compiler.eclipse; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squale.jraf.commons.exception.JrafEnterpriseException; import org.squale.squalecommon.datatransfertobject.message.MessagesDTO; import org.squale.squalecommon.enterpriselayer.businessobject.component.parameters.ListParameterBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.parameters.MapParameterBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.parameters.ParametersConstants; import org.squale.squalecommon.enterpriselayer.businessobject.component.parameters.StringParameterBO; import org.squale.squalecommon.enterpriselayer.businessobject.result.ErrorBO; import org.squale.squalecommon.enterpriselayer.facade.message.MessageFacade; import org.squale.squalix.tools.compiling.CompilingMessages; import org.squale.squalix.tools.compiling.java.beans.JWSADProject; import org.squale.squalix.tools.compiling.java.configuration.EclipseCompilingConfiguration; import org.squale.squalix.util.file.FileUtility; import org.squale.squalix.util.process.ProcessErrorHandler; import org.squale.squalix.util.process.ProcessManager; import org.squale.squalix.util.process.ProcessOutputHandler; /** * Compilation avec le plugin eclipse 3.2 (test� sous Eclipse 3.2.1 et 3.3.0) */ public class EclipseCompiler implements ProcessErrorHandler, ProcessOutputHandler { /** * Logger. */ private static final Log LOGGER = LogFactory.getLog( EclipseCompiler.class ); /** Le s�parateur entre les variables */ private static String ECLIPSE_VAR_VAR_SEP = "|"; /** Le s�parateur entre le nom de la vrariables et ses jars */ private static String ECLIPSE_VAR_PATHS_SEP = "="; /** Le s�parateur entre les jars */ private static String ECLIPSE_PATH_SEP = ";"; /** Log de niveau WARNING */ private static final String WARNING_LOG = "ATTENTION: "; /** Log de niveau WARNING */ private static final String[] WARNING_LOG_ARRAY = {"ATTENTION: ","WARNING: "}; /** Log de niveau GRAVE */ private static final String ERROR_LOG = "GRAVE: "; /** Log de niveau GRAVE */ private static final String[] ERROR_LOG_ARRAY = {"GRAVE: ","SEVERE: ","ERROR: "}; /** Log de niveau INFO */ private static final String INFO_LOG = "INFO: "; /** Log de niveau INFO */ private static final String[] INFO_LOG_ARRAY = {"INFO: ","INFORMATION: "}; /** Le nombre max d'erreurs � sauvegarder par type */ private static final int MAX_ERRORS = 100; /** Log pour r�cup�rer le classpath */ private static final String CLASSPATH_LOG = "INFO: CLASSPATH="; /** Log pour r�cup�rer les r�pertoires de compilation */ private static final String OUTPUTS_LOG = "INFO: OUTPUT_DIRS="; /** Default advanced options */ private static final String DEFAULT_ADVANCED_OPTIONS = "-os win32 -arch x86 -ws win32"; /** * Liste des projets eclipse. */ private List projectList = null; /** Le view path */ private String mViewPath; /** La liste des erreurs */ private List errors; /** Le nombre d'erreur */ private int nbErrors; /** Le nombre de warning */ private int nbWarning; /** Le nomdre d'info */ private int nbInfo; /** Le classpath */ private String classpath = ""; /** Les r�pertoires de compilation */ private List outputDirs; /** L'option -vars */ private String varsOption = ""; /** L'option -userLibs */ private String userLibsOption = ""; /** Advanced eclipse launcher options (like -os, -arch, -ws) */ private String advancedOptions = DEFAULT_ADVANCED_OPTIONS; /** * Constructeur. * * @param pProjectList liste des projets eclipse � compiler. * @param pViewPath le viewPath * @param eclipseParams Eclipse parameters defined by user */ public EclipseCompiler( List pProjectList, String pViewPath, MapParameterBO eclipseParams ) { projectList = pProjectList; mViewPath = pViewPath; errors = new ArrayList(); outputDirs = new ArrayList(); if ( null != eclipseParams ) { buildVarsOption( (MapParameterBO) eclipseParams.getParameters().get( ParametersConstants.ECLIPSE_VARS ) ); buildUserLibsOption( (MapParameterBO) eclipseParams.getParameters().get( ParametersConstants.ECLIPSE_LIBS ) ); setAdvancedOptions( (StringParameterBO) eclipseParams.getParameters().get( ParametersConstants.ECLIPSE_ADVANCED_OPTIONS ) ); } else { setAdvancedOptions( null ); } } /** * Launch Java Compiling with Eclipse bundle * * @throws Exception in case of error * @return 0 if the compilation has been successfully done */ public int runCompilation() throws Exception { // Creation of the Eclipse compiling command String curPath; File currentPath; // Values for -projects option of Eclipse command String projectsOption = ""; // Parsing Eclipse compiling configuration file EclipseCompilingConfiguration conf = new EclipseCompilingConfiguration(); conf.parse( new FileInputStream( new File( "config/eclipsecompiling-config.xml" ) ) ); // Full clean of directories to prevent interaction with the previous tasks File workspaceDir = new File( conf.getWorkspace() ); FileUtility.deleteRecursively( new File( conf.getWorkspace() ) ); FileUtility.deleteRecursively( conf.getEclipseHome() ); // Workspace directory creation workspaceDir.mkdirs(); // Adding write rights on the project folders // Collecting projects paths for the compiling command for ( int i = 0; i < projectList.size(); i++ ) { curPath = ( (JWSADProject) projectList.get( i ) ).getPath(); currentPath = new File( curPath ); FileUtility.setWriteRights( currentPath, "both" ); // Adding project path for the compiling command projectsOption += curPath; projectsOption += ECLIPSE_PATH_SEP; } // Creating Eclipse Base directory conf.getEclipseHome().mkdirs(); // Getting the path of the minimal Eclipse environment and the destination path File squaleEclipsePluginsPath = new File( conf.getSqualeEclipsePlugins().getAbsolutePath() ); File eclipseHomePath = new File( conf.getEclipseHome().getAbsolutePath() ); // Copying the minimal Eclipse environment LOGGER.info( "Minimal Eclipse environment install from : " + conf.getSqualeEclipsePlugins().getAbsolutePath() ); FileUtility.copyDirContentIntoDir( squaleEclipsePluginsPath, eclipseHomePath ); // Getting the reference (archive file or directory) to the choosen Eclipse bundle File userPluginsPath = new File( ( (JWSADProject) projectList.get( 0 ) ).getBundleDir().getAbsolutePath() ); // Getting the path to the plugins directory of the minimal Eclipse environment File bundlePluginsPath = new File( new File( conf.getEclipseHome(), "plugins" ).getAbsolutePath() ); // Copying the given bundle into the Eclipse Environment LOGGER.info( "Choosen Eclipse bundle install from : " + userPluginsPath ); FileUtility.copyDirContentIntoDir( userPluginsPath, bundlePluginsPath ); // Collecting Java version for compiling command String dialect = ( (JWSADProject) projectList.get( 0 ) ).getJavaVersion().replaceAll( "_", "." ); // Building Exclude patterns String excludedPatterns = buildExcludedPatterns(); // Building Sun JAR list String sunLibs = buildSunLibs( ( (JWSADProject) projectList.get( 0 ) ).getBootClasspath(), ( (JWSADProject) projectList.get( 0 ) ).getJavaVersion() ); // Setting mandatory arguments List command = new ArrayList(); command.add( conf.getCommand() ); // On ajoute la liste des options obligatoires en rempla�ant les // param�tres de substitution // (sous la forme [valeur]) for ( int i = 0; i < conf.getOptions().size(); i++ ) { // On effectue tous les remplacement possible String curOption = ( (String) conf.getOptions().get( i ) ).replaceAll( "\\[dialect\\]", dialect ); curOption = curOption.replaceAll( "\\[squale_eclipse_home\\]", conf.getEclipseHome().getAbsolutePath() ); curOption = curOption.replaceAll( "\\[sun_libs\\]", sunLibs ); curOption = curOption.replaceAll( "\\[projects_list\\]", projectsOption ); curOption = curOption.replaceAll( "\\[var_libs\\]", varsOption ); curOption = curOption.replaceAll( "\\[user_libs\\]", userLibsOption ); curOption = curOption.replaceAll( "\\[workspace\\]", conf.getWorkspace() ); curOption = curOption.replaceAll( "\\[excluded_patterns\\]", excludedPatterns ); // Particular case of advanced options which may contain more than // one option if ( curOption.matches( "\\[advanced_options\\]" ) ) { String[] advancedOptionsTab = advancedOptions.split( " " ); for ( int o = 0; o < advancedOptionsTab.length; o++ ) { command.add( advancedOptionsTab[o] ); } } else { command.add( curOption ); } } // On supprime le fichier org.eclipse.wst.common.project.facet.core.xml FileUtility.deleteFilesinPath( new File ( mViewPath ) , "org.eclipse.wst.common.project.facet.core.xml" ); FileUtility.deleteFilesinPath( new File ( mViewPath ) , "org.eclipse.jst.common.project.facet.core.prefs" ); // On cr�e le process ProcessManager compileProcess = new ProcessManager( (String[]) command.toArray( new String[command.size()] ), null, workspaceDir ); compileProcess.setOutputHandler( this ); LOGGER.info( "On lance la compilation avec eclipse : " + printCommand( command ) ); // On demarre le processus et on retourne 0 si l'execution s'est bien // d�roul�e return compileProcess.startProcess( this ); } /** * Set advanced options for launching plugin. Advanced options are necessary to specify os configuration in order to * get plugins linked to OS like org.eclipse.swt.win32.win32.x86_3.2.0.v3232m.jar (see * http://help.eclipse.org/help21/index.jsp?topic=/org.eclipse.platform.doc.user/tasks/running_eclipse.htm) * * @param advancedOptionsParam advanced options define in project parameters */ private void setAdvancedOptions( StringParameterBO advancedOptionsParam ) { // Get user parameter if exists if ( null != advancedOptionsParam ) { advancedOptions = advancedOptionsParam.getValue(); } else { // get key configuration for advanced options try { MessagesDTO webMsg = MessageFacade.getMessages(); String advancedKey = webMsg.getMessage( "fr", "eclipse.compilation.advanced.options" ); if ( null != advancedKey ) { advancedOptions = advancedKey; } else { // Log warning for administrator LOGGER.warn( "Advanced options configuration key (eclipse.compilation.advanced.options ) " + "is not set!! Reload message.xml file." ); } } catch ( JrafEnterpriseException e ) { // Log warning and take default advanced options LOGGER.warn( "Cannot get advanced options configuration key: " + e.getMessage() ); } } } /** * Met en forme l'option -vars de la commande * * @param pVarsParams les param�tres � mettre en forme */ private void buildVarsOption( MapParameterBO pVarsParams ) { StringBuffer varLibs = new StringBuffer( "" ); if ( null != pVarsParams ) { for ( Iterator it = pVarsParams.getParameters().keySet().iterator(); it.hasNext(); ) { String curVarName = (String) it.next(); String lib = ( (StringParameterBO) pVarsParams.getParameters().get( curVarName ) ).getValue(); // On v�rifie que le fichier existe sinon on log un warning File libFile = FileUtility.getAbsoluteFile( mViewPath, new File( lib ) ); if ( null == libFile ) { LOGGER.warn( "File for variable " + curVarName + " doesn't exist: " + lib ); } else { varLibs.append( curVarName ); varLibs.append( ECLIPSE_VAR_PATHS_SEP ); varLibs.append( libFile.getAbsolutePath() ); varLibs.append( ECLIPSE_VAR_VAR_SEP ); } } varsOption = varLibs.toString(); } } /** * Met en forme l'option -userLibs de la commande * * @param pEclipseLibs les param�tres � mettre en forme */ private void buildUserLibsOption( MapParameterBO pEclipseLibs ) { StringBuffer libsBuf = new StringBuffer( "" ); if ( null != pEclipseLibs ) { for ( Iterator it = pEclipseLibs.getParameters().keySet().iterator(); it.hasNext(); ) { String curLibName = (String) it.next(); List libs = ( (ListParameterBO) pEclipseLibs.getParameters().get( curLibName ) ).getParameters(); // On met en forme les libs String libsForm = buildEclipseLibPaths( libs ); if ( libsForm.length() == 0 ) { LOGGER.warn( "Can't add " + curLibName + " because of no existing paths" ); } else { libsBuf.append( curLibName ); libsBuf.append( ECLIPSE_VAR_PATHS_SEP ); libsBuf.append( libsForm ); libsBuf.append( ECLIPSE_VAR_VAR_SEP ); } } } userLibsOption = libsBuf.toString(); } /** * @param libPaths les chemins vers les libraries d'un librairie utilistateur * @return les chemins vers toutes les librairies format�s */ private String buildEclipseLibPaths( List libPaths ) { StringBuffer value = new StringBuffer( "" ); for ( int i = 0; i < libPaths.size(); i++ ) { String lib = ( (StringParameterBO) libPaths.get( i ) ).getValue(); File libFile = FileUtility.getAbsoluteFile( mViewPath, new File( lib ) ); if ( null == libFile ) { LOGGER.warn( "File for library doesn't exist: " + lib ); } else { // On l'ajoute value.append( libFile.getAbsolutePath() ); value.append( ECLIPSE_PATH_SEP ); } } return value.toString(); } /** * @return les patterns s�par�s par ";" */ private String buildExcludedPatterns() { // On parcours tous les r�pertoires � exclure de tous les projets puis // on les cr�e et on les s�pare avec ";" StringBuffer value = new StringBuffer( "" ); for ( int p = 0; p < projectList.size(); p++ ) { JWSADProject project = (JWSADProject) projectList.get( p ); if ( null != project.getExcludedDirs() ) { for ( int i = 0; i < project.getExcludedDirs().size(); i++ ) { value.append( project.getExcludedDirs().get( i ) ); value.append( ECLIPSE_PATH_SEP ); } } } // On supprime les doublons return value.toString().replaceAll( "//", "/" ); } /** * @param bootClasspath le chemin vers la jre de sun * @param dialect le dialect afin de r�cup�rer les jar du J2EE de sun * @return la liste des librairies sun � ajouter au classpath des projets sous la forme jar1;jar2 */ private String buildSunLibs( String bootClasspath, String dialect ) { StringBuffer libs = new StringBuffer( "" ); String[] bootClasspathSplitting = bootClasspath.split( ECLIPSE_PATH_SEP ); // Parcours du bootclasspath et r�cup�ration des .jars et .zip for ( int i = 0; i < bootClasspathSplitting.length; i++ ) { addCompilingRessources( new File( bootClasspathSplitting[i].trim() ), libs ); } // On cr�e le descripteur vers le dossier contenant les jars � ajouter // pour la compilation StringBuffer path = new StringBuffer( CompilingMessages.getString( "dir.root.java" ) ); path.append( "/" ); path.append( dialect ); path.append( "/" ); File javaRoot = new File( path.toString().replace( '.', '_' ) ); addCompilingRessources( javaRoot, libs ); return libs.toString(); } /** * @param pFile le fichier (ou r�pertoire contenant les fichiers) � ajouter * @param jarsList le buffer contenant la liste des jars * @return la liste des jars ou zip */ private String addCompilingRessources( File pFile, StringBuffer jarsList ) { // Buffer qui contiendra la liste des jars // Parcours du r�pertoire if ( pFile.isDirectory() ) { File[] fileList = pFile.listFiles(); for ( int i = 0; i < fileList.length; i++ ) { // s'il s'agit d'un fichier if ( fileList[i].isFile() ) { // si l'extension est autoris�e if ( fileList[i].getAbsolutePath().endsWith( ".jar" ) || fileList[i].getAbsolutePath().endsWith( ".zip" ) ) { // on ajoute le fichier au buffer jarsList.append( fileList[i].getAbsolutePath() ); jarsList.append( ECLIPSE_PATH_SEP ); } // s'il s'agit d'un r�pertoire } else { // on appelle r�cursivement la m�thode addCompilingRessources( fileList[i], jarsList ); } } } else if ( pFile.isFile() ) { // on ajoute le fichier au buffer jarsList.append( pFile.getAbsolutePath() ); jarsList.append( ECLIPSE_PATH_SEP ); } else { // On log une warning LOGGER.warn( CompilingMessages.getString( "exception.jar_not_found", pFile.getAbsolutePath() ) ); } return jarsList.toString(); } /** * @param commandAndArgs la commande et ses arguments sous forme de liste * @return la comande qui va �tre ex�cut�e */ private String printCommand( List commandAndArgs ) { StringBuffer display = new StringBuffer( "" ); for ( int i = 0; i < commandAndArgs.size(); i++ ) { display.append( commandAndArgs.get( i ) ); display.append( " " ); } return display.toString(); } /** * {@inheritdoc} * * @see org.squale.squalix.util.process.ProcessErrorHandler#processError(java.lang.String) */ public void processError( String pErrorMessage ) { createError( pErrorMessage ); } /** * {@inheritdoc} * * @see org.squale.squalix.util.process.ProcessOutputHandler#processOutput(java.lang.String) */ public void processOutput( String pOutputLine ) { createError( pOutputLine ); } /** * @param pMessage le message de sortie */ private void createError( String pMessage ) { LOGGER.info( pMessage ); // Le niveau de criticit� d�pend du niveau de criticit� du log if ( arrayStartsWith( pMessage, WARNING_LOG_ARRAY ) ) { createError( pMessage.replaceFirst( WARNING_LOG, "" ), ErrorBO.CRITICITY_WARNING, nbWarning ); nbWarning++; } else if ( arrayStartsWith( pMessage, ERROR_LOG_ARRAY ) ) { createError( pMessage.replaceFirst( ERROR_LOG, "" ), ErrorBO.CRITICITY_FATAL, nbErrors ); nbErrors++; } else if ( arrayStartsWith( pMessage, INFO_LOG_ARRAY ) ) { // On r�cup�re le classpath �crit en log if ( pMessage.startsWith( CLASSPATH_LOG ) ) { classpath = pMessage.replaceFirst( CLASSPATH_LOG, "" ); } else if ( pMessage.startsWith( OUTPUTS_LOG ) ) { buildOutputDirs( pMessage.replaceFirst( OUTPUTS_LOG, "" ) ); } else { createError( pMessage.replaceFirst( INFO_LOG, "" ), ErrorBO.CRITICITY_LOW, nbInfo ); nbInfo++; } } // On ne traite pas les autres } /** * Permet de construire la liste des r�pertoires de compilation par le log sous la forme output1;output2 * * @param pOutputLog le log repr�sentant les r�pertoires de compilation */ private void buildOutputDirs( String pOutputLog ) { String[] outputDirsSplitting = pOutputLog.split( ECLIPSE_PATH_SEP ); for ( int i = 0; i < outputDirsSplitting.length; i++ ) { // If path name has spaces, we move all classes in an another // directory // without spaces (see CkjmTask) and we replace classes directory in // tempory classpath if ( outputDirsSplitting[i].indexOf( ' ' ) != -1 ) { // List classes File[] classesToMove = new File( outputDirsSplitting[i] ).listFiles(); // Change outputdir (unique name to be sure the classes are // renamming) String curDir = outputDirsSplitting[i]; outputDirsSplitting[i] = ( (JWSADProject) projectList.get( 0 ) ).getDestPath() + i; // Replace it in classpath classpath = classpath.replaceAll( curDir, outputDirsSplitting[i] ); File cleanOutputFile = new File( outputDirsSplitting[i] ); cleanOutputFile.mkdirs(); for ( int f = 0; f < classesToMove.length; f++ ) { classesToMove[f].renameTo( new File( outputDirsSplitting[i], classesToMove[f].getName() ) ); } } outputDirs.add( outputDirsSplitting[i] ); } } /** * Cr�ation d'une erreur et ajout dans la liste des erreurs * * @param pErrorMessage le message * @param pErrorLevel la criticit� * @param pNbErrors the number of created errors */ private void createError( String pErrorMessage, String pErrorLevel, int pNbErrors ) { if ( pNbErrors < MAX_ERRORS ) { ErrorBO error = new ErrorBO(); error.setMessage( pErrorMessage ); error.setInitialMessage( pErrorMessage ); error.setLevel( pErrorLevel ); errors.add( error ); } } /** * @return le classpath */ public String getClasspath() { return classpath; } /** * @return les erreurs trouv�es */ public List getErrors() { return errors; } /** * @return la liste des r�pertoires de compilation */ public List getOutputDirs() { return outputDirs; } /** * checks that Classpath and OutputDirs are correctly set */ public void checkOutputVariables() { // V�rif de la longueur du Classpath if (classpath.length() == 0) { ErrorBO error = new ErrorBO(); error.setMessage( "Compiled ClassPath is empty." ); error.setInitialMessage( "Compiled ClassPath is empty." ); error.setLevel( ErrorBO.CRITICITY_WARNING ); errors.add( error ); nbWarning++; } // V�rif de la liste des r�pertoires de compilation if (outputDirs.isEmpty()) { ErrorBO error = new ErrorBO(); error.setMessage( "Compiled Output Directories are empty." ); error.setInitialMessage( "Compiled Output Directories are empty." ); error.setLevel( ErrorBO.CRITICITY_WARNING ); errors.add( error ); nbWarning++; } // V�rif inverse et affichage d'une info si c'est bon if ( (classpath.length()!=0) && (!outputDirs.isEmpty()) ) { LOGGER.info( "Compiled Variables have been fulfilled." ); } } /** * arrayStartsWith checks if pStringToCheck starts with at least one of the pPatternArray elements * * @param pStringToCheck string to be checked * @param pPatternArray String[] with the list of start pattern to check * @return boolean true if pStringToCheck starts with any of the pPatternArray element */ private boolean arrayStartsWith(String pStringToCheck ,String[] pPatternArray) { boolean output = false; for (int i=0 ; i < pPatternArray.length ; i++) { if (pStringToCheck.startsWith( pPatternArray[i] )) { output = true; break; } } return output; } }