/** * 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.macker; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import net.innig.macker.Macker; import net.innig.macker.event.ListenerException; import net.innig.macker.event.MackerIsMadException; import net.innig.macker.rule.RulesException; import net.innig.macker.structure.ClassParseException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squale.jraf.commons.exception.JrafDaoException; import org.squale.squalecommon.daolayer.result.MeasureDAOImpl; import org.squale.squalecommon.daolayer.rulechecking.ProjectRuleSetDAOImpl; 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.IntegerMetricBO; import org.squale.squalecommon.enterpriselayer.businessobject.result.rulechecking.MackerTransgressionBO; import org.squale.squalecommon.enterpriselayer.businessobject.result.rulechecking.RuleCheckingTransgressionItemBO; import org.squale.squalecommon.enterpriselayer.businessobject.rulechecking.ProjectRuleSetBO; import org.squale.squalecommon.enterpriselayer.businessobject.rulechecking.RuleBO; import org.squale.squalecommon.enterpriselayer.facade.macker.MackerConfigParser; import org.squale.squalix.core.AbstractTask; import org.squale.squalix.core.TaskData; import org.squale.squalix.core.TaskException; import org.squale.squalix.core.exception.ConfigurationException; import org.squale.squalix.util.buildpath.BuildProjectPath; import org.squale.squalix.util.file.FileUtility; /** * T�che Macker.<br/> V�rifie l'architecture du projet � auditer.<br/> La t�che de compilation java doit avoir �t� * �x�cut�e avant afin que le chemin du r�pertoire contenant les ".class" (CLASSES_DIR) soit pr�sent dans les param�tres * temporaires. */ public class MackerTask extends AbstractTask { /** * Logger */ private static final Log LOGGER = LogFactory.getLog( MackerTask.class ); /** Configuration de la t�che Macker */ protected MackerConfiguration mConfiguration; /** L'�couteur pour les classes java */ protected JavaStorageListener mListener; /** * Constructeur par defaut */ public MackerTask() { super(); mName = "MackerTask"; mConfiguration = new MackerConfiguration(); } // Cette tache n'a pas d'influence dans le calcul de la taille du file system /** * Analyse les fichiers compil�s afin de relever pour chacun les transgressions des r�gles concernant * l'architecture. * * @throws TaskException si erreur */ public void execute() throws TaskException { if ( init() ) { try { analyze(); // On fait persister les mesures persisteMeasures( mListener.getNbOcc(), mListener.getDetails(), mConfiguration.getRuleSet() ); } catch ( Exception e ) { throw new TaskException( e ); } } } /** * Analyse les fichiers * * @throws ClassParseException si erreur * @throws IOException si erreur * @throws RulesException si erreur * @throws ListenerException si erreur * @throws MackerIsMadException si erreur */ protected void analyze() throws ClassParseException, IOException, RulesException, ListenerException, MackerIsMadException { Macker macker = configMacker( mConfiguration.getFilesToAnalyze(), mConfiguration.getConfigFile() ); // On modifie le classpath pour ex�cuter Macker afin d'avoir le build classpath pour la r�solution // de l'ensemble des classes. // macker.setClassLoader(new URLClassLoader(getClasspathURLs())); /* On lance l'analyse en attachant notre �v�nement � Macker: */ mListener = new JavaStorageListener( getSession(), mProject, mConfiguration ); macker.addListener( mListener ); macker.checkRaw(); } /** * Construit le tableau des urls � ajouter au classpath pour ex�cuter Macker � partir du classpath temporaire * * @return les URLs */ protected URL[] getClasspathURLs() { ArrayList urlsList = new ArrayList(); // On d�coupe le classpath des param�tres temporaires String[] paths = ( (String) mData.getData( TaskData.CLASSPATH ) ).split( ";" ); for ( int i = 0; i < paths.length; i++ ) { try { urlsList.add( new File( paths[i] ).toURL() ); } catch ( MalformedURLException e ) { // On log juste l'erreur LOGGER.warn( "Error in temp classpath, malformed url: " + paths[i] ); } } return (URL[]) urlsList.toArray( new URL[urlsList.size()] ); } /** * Initialise la t�che Macker * * @throws TaskException si erreur * @return true si la t�che n'est pas annul�e */ protected boolean init() throws TaskException { // Les donn�es du projet doivent fournir le nom du fichier // de configuration MapParameterBO mackerMap = (MapParameterBO) getProject().getParameter( ParametersConstants.MACKER ); if ( mackerMap == null ) { String message = MackerMessages.getString( "macker.exception.no_configuration" ); // On affiche un warning sans lancer d'exception, la t�che ne sera pas ex�cut�e. initError( message ); LOGGER.warn( message ); // Les param�tres sont mal configur�s, on annule la t�che mStatus = CANCELLED; } else { // On r�cup�re le view_path cr�e par la t�che du source manager en ajoutant un s�parateur // Unix en bout au cas o�. String root = (String) mData.getData( TaskData.VIEW_PATH ); if ( null == root ) { String message = MackerMessages.getString( "macker.exception.view_path_not_found" ) + TaskData.VIEW_PATH; LOGGER.error( message ); // Lance une exception de configuration throw new TaskException( message ); } root += "/"; mConfiguration.setRoot( root ); // On r�cup�re les chemins relatifs des r�pertoires contenant les .java du projet ListParameterBO sources = (ListParameterBO) getProject().getParameter( ParametersConstants.SOURCES ); if ( sources == null ) { String message = MackerMessages.getString( "macker.exception.sources_not_found" ); LOGGER.error( message ); // Lance une exception de configuration throw new TaskException( message ); } mConfiguration.setSources( BuildProjectPath.buildProjectPath( root, sources.getParameters() ) ); // On r�cup�re les r�pertoires contenant les .class du projet � analyser // cr�e par la t�che de compilation java List classesDirs = (List) this.getData().getData( TaskData.CLASSES_DIRS ); if ( classesDirs == null ) { String message = MackerMessages.getString( "macker.exception.class_dir_not_found" ); LOGGER.error( message ); // Lance une exception de configuration throw new TaskException( message ); } // On g�n�re la liste des fichiers compil�s � analyser HashSet classes = new HashSet(); for ( int i = 0; i < classesDirs.size(); i++ ) { classes.addAll( mConfiguration.getFilesToAnalyze( (String) classesDirs.get( i ) ) ); } mConfiguration.setFilesToAnalyze( classes ); // Les nom des fichiers qui peuvent �tre persist�s List includedFileNames = FileUtility.getIncludedFiles( root, mConfiguration.getSources(), (ListParameterBO) mProject.getParameter( ParametersConstants.INCLUDED_PATTERNS ), (ListParameterBO) mProject.getParameter( ParametersConstants.EXCLUDED_PATTERNS ), null, new String[] { ".java" } ); mConfiguration.setIncludedFiles( includedFileNames ); try { /* On configure Macker */ // On r�cup�re le fichier de configuration File configFile = getConfigFile( mackerMap, root ); // Si le fichier de configuration n'existe pas if ( !configFile.exists() ) { String message = MackerMessages.getString( "macker.exception.configurationfile_not_found" ) + configFile.toString(); LOGGER.error( message ); // Lance une exception de configuration throw new TaskException( message ); } mConfiguration.setConfigFile( configFile ); // On r�cup�re la liste des r�gles en parsant le fichier de configuration ProjectRuleSetBO projectRuleSet = importMackerConfig( configFile ); mConfiguration.setRuleSet( projectRuleSet ); } catch ( Exception e ) { throw new TaskException( e ); } } return mStatus != CANCELLED; } /** * @param pConfigFile le fichier de configuration Macker * @throws ConfigurationException si erreur * @throws FileNotFoundException si erreur * @return la liste des r�gles Macker */ private ProjectRuleSetBO importMackerConfig( File pConfigFile ) throws ConfigurationException, FileNotFoundException { // Importation du fichier MackerConfigParser parser = new MackerConfigParser(); InputStream stream = new FileInputStream( pConfigFile ); StringBuffer errors = new StringBuffer(); ProjectRuleSetBO ruleset = parser.parseFile( stream, errors ); // Si le parsing s'est mal pass� on lance une erreur de configuration if ( errors.length() > 0 ) { String message = MackerMessages.getString( "macker.exception.configurationfile_parsing_error" ) + "(" + pConfigFile.getAbsolutePath() + ")" + errors; LOGGER.error( message ); // Lance une exception de configuration throw new ConfigurationException( message ); } return ruleset; } /** * Fait persister les mesures * * @param pNbOcc les r�sultats obtenus par Macker * @param pDetails le d�tail des transgressions * @param pRuleset les r�gles * @throws IOException si erreur * @throws JrafDaoException si erreur */ protected void persisteMeasures( HashMap pNbOcc, HashMap pDetails, ProjectRuleSetBO pRuleset ) throws IOException, JrafDaoException { // On sauvegarde le ruleset dans la base pRuleset.setProject( mProject ); ProjectRuleSetDAOImpl.getInstance().create( getSession(), pRuleset ); // Cr�ation de la transgression MackerTransgressionBO transgression = new MackerTransgressionBO(); transgression.setAudit( mAudit ); transgression.setComponent( mProject ); transgression.setRuleSet( pRuleset ); transgression.setTaskName( mName ); // On parcourt les r�gles connues dans le ruleset Map rules = pRuleset.getRules(); Iterator ruleCodes = rules.keySet().iterator(); while ( ruleCodes.hasNext() ) { String ruleCode = (String) ruleCodes.next(); // On r�cup�re les d�tails li�s � cette r�gle ArrayList items = (ArrayList) pDetails.get( ruleCode ); // si il y en a on modifie la transgression en cons�quence if ( null != items ) { RuleBO rule = (RuleBO) rules.get( ruleCode ); for ( int i = 0; i < items.size(); i++ ) { RuleCheckingTransgressionItemBO item = (RuleCheckingTransgressionItemBO) items.get( i ); item.setRule( rule ); transgression.getDetails().add( item ); } } Integer value = (Integer) pNbOcc.get( ruleCode ); int nbOcc = 0; // Si le parsing n'a pas donn� de r�sultat, on place 0 comme // nombre de transgression if ( null != value ) { nbOcc = value.intValue(); } // On ajoute une m�trique de type Integer pour chaque r�gle transgress�e // avec 0 comme valeur par d�faut IntegerMetricBO metric = new IntegerMetricBO(); metric.setName( ruleCode ); metric.setValue( nbOcc ); metric.setMeasure( transgression ); transgression.putMetric( metric ); } // Sauvegarde des donn�es dans la base MeasureDAOImpl.getInstance().create( getSession(), transgression ); } /** * Configure Macker en lui indiquant le fichier des r�gles et les classes qu'il doit analyser * * @param pFilesToAnalyse les fichiers compil�s � analyser * @param pConfigFile le fichier de configuration Macker * @throws ClassParseException si erreur * @throws RulesException si erreur * @throws IOException si erreur * @return Macker le macker configur� */ protected Macker configMacker( HashSet pFilesToAnalyse, File pConfigFile ) throws RulesException, ClassParseException, IOException { Macker macker = new Macker(); Iterator filesIt = pFilesToAnalyse.iterator(); while ( filesIt.hasNext() ) { try { macker.addClass( new File( (String) filesIt.next() ) ); } catch ( IllegalStateException ise ) { // On log juste un warning. Cette erreur peut survenir lorsque // l'utilisateur a par exemple laisser des .class dans le r�pertoire // de g�n�ration des .class LOGGER.warn( ise.getMessage() ); initError( ise.getMessage() ); } } // On indique le fichier de configuration macker.addRulesFile( pConfigFile ); return macker; } /** * R�cup�re le fichier de configuration Macker * * @param pMackerMap les param�tres Macker * @param pViewPath la vue * @return le fichier de configuration * @throws ConfigurationException si erreur */ private File getConfigFile( MapParameterBO pMackerMap, String pViewPath ) throws ConfigurationException { File result = new File( "" ); Map params = pMackerMap.getParameters(); StringParameterBO filePath = (StringParameterBO) params.get( ParametersConstants.MACKER_CONFIGURATION ); if ( filePath == null ) { String message = MackerMessages.getString( "macker.exception.no_configuration" ); LOGGER.warn( message ); // si le fichier n'est pas renseign�, on annule la t�che. mStatus = CANCELLED; } else { File configFile = new File( filePath.getValue() ); // Si le fichier de configuration a un nom absolue et existe, on prend celui-ci if ( configFile.isAbsolute() && configFile.exists() ) { result = configFile; } else { // Le fichier de configuration est suppos� �tre relatif � la vue result = new File( pViewPath, configFile.getPath() ); } } return result; } }