/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library 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 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.generation.recompiler;
import org.objectweb.speedo.generation.mivisitor.MetaInfoVisitorImpl;
import org.objectweb.speedo.generation.mivisitor.MetaInfoVisitor;
import org.objectweb.speedo.generation.api.SpeedoCompilerParameter;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoXMLDescriptor;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.mapper.lib.Object2StringSerializer;
import org.objectweb.speedo.tools.StringReplace;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.util.monolog.api.BasicLevel;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.net.URLClassLoader;
import java.net.URL;
import java.net.MalformedURLException;
/**
* Checks the date of the source and the built files:
* - .jdo / JMI file
* - .java / .class
*
* For each .jdo file, if the .jdo or a .java has been modified all classes
* are removed in order to re-enhance all classed.
*
* TODO: check aware classes
*
* @author S.Chassande-Barrioz
*/
public class UpToDateVisitor extends MetaInfoVisitorImpl {
private File output;
private File input;
private ClassLoader classloader;
private List xmlsToRemove;
private List enhancedClasses;
/**
* builds a MetaInfoVisitor which is the last of the chain
*/
public UpToDateVisitor(Personality p) {
super(p);
}
/**
* builds a MetaInfoVisitor which is the last of the chain
*/
public UpToDateVisitor(MetaInfoVisitor mim, Personality p) {
super(mim, p);
}
protected String getLoggerName() {
return super.getLoggerName() + ".uptodate";
}
public String getTitle() {
return "Computing classes requiring enhancement ...";
}
public boolean init() throws SpeedoException {
super.init();
output = new File(scp.output);
if (!output.exists() && output.mkdirs()) {
throw new SpeedoException(
"Impossible to create the output directory: "
+ output.getAbsolutePath());
}
input = new File(scp.input);
if (!input.exists()) {
throw new SpeedoException("No input directory: "
+ input.getAbsolutePath());
}
try {
classloader = new URLClassLoader(
new URL[]{new File(scp.output).toURL()},
getClass().getClassLoader());
} catch (MalformedURLException e) {
}
enhancedClasses = new ArrayList();
return !scp.getXmldescriptor().isEmpty();
}
public void visitCompilerParameter(SpeedoCompilerParameter scp) throws SpeedoException {
super.visitCompilerParameter(scp);
if (xmlsToRemove != null) {
for(int i=(xmlsToRemove.size() -1); i>=0; i--) {
SpeedoXMLDescriptor xml = (SpeedoXMLDescriptor) xmlsToRemove.get(i);
scp.getXmldescriptor().remove(xml.xmlFile);
}
}
}
public void visitXml(SpeedoXMLDescriptor xml) throws SpeedoException {
//compare the date of the .jdo and JMI files
File xmlFile = new File(scp.xmlDir, xml.xmlFile);
File jmiFile = new File(output,
Object2StringSerializer.descFileName2ClassName(xml.xmlFile)
+ ".class");
if (debug) {
logger.log(BasicLevel.DEBUG, "XML file: "
+ xmlFile.getAbsolutePath()
+ "\n\ttime=" + xmlFile.lastModified());
if (jmiFile.exists()) {
logger.log(BasicLevel.DEBUG, "JMI file: "
+ jmiFile.getAbsolutePath()
+ "\n\ttime=" + jmiFile.lastModified());
} else {
logger.log(BasicLevel.DEBUG, "NO JMI file found : "
+ jmiFile.getAbsolutePath());
}
}
xml.requireEnhancement = !jmiFile.exists()
|| (xmlFile.lastModified() > jmiFile.lastModified());
if (debug) {
logger.log(BasicLevel.DEBUG, "Enhancement "
+ (xml.requireEnhancement ? "" : "not ")
+ "required for the jdo file:" + xml.xmlFile);
}
enhancedClasses.clear();
super.visitXml(xml);
List classes = xml.getSpeedoClasses();
if (!xml.requireEnhancement) {
for(int i=(classes.size() -1); i>=0 && !xml.requireEnhancement; i--) {
SpeedoClass sc = (SpeedoClass) classes.get(i);
sc.setRequireEnhancement(xml.requireEnhancement);
}
}
if (xml.requireEnhancement) {
logger.log(BasicLevel.INFO, "Enhancement required for :" + xml.xmlFile);
//Remove the JMI file
if (debug) {
logger.log(BasicLevel.DEBUG, "Removing the jmi file:"
+ jmiFile.getAbsolutePath());
}
if (jmiFile.exists() && !jmiFile.delete()) {
throw new SpeedoException(
"Impossible to remove the JMI file associated to the jdo file:"
+ xml.xmlFile);
}
for (int i = (enhancedClasses.size() - 1); i >= 0; i--) {
SpeedoClass sc = (SpeedoClass) enhancedClasses.get(i);
String baseName = StringReplace.replaceChar(
'.', File.separatorChar, sc.getFQName());
File classFile = new File(output, baseName + ".class");
if (debug) {
logger.log(BasicLevel.DEBUG, "Removing class files:" + classFile);
}
if (classFile.exists() && !classFile.delete()) {
throw new SpeedoException("Impossible to remove the file:"
+ classFile.getAbsolutePath());
}
}
} else {
if (xmlsToRemove == null) {
xmlsToRemove = new ArrayList();
}
xmlsToRemove.add(xml);
}
enhancedClasses.clear();//garbage
}
public void visitClass(SpeedoClass sc) throws SpeedoException {
super.visitClass(sc);
SpeedoXMLDescriptor xml = sc.moPackage.xmlDescriptor;
sc.setRequireEnhancement(xml.requireEnhancement);
String baseName = StringReplace.replaceChar(
'.', File.separatorChar, sc.getFQName());
File classFile = new File(output, baseName + ".class");
boolean classExist = classFile.exists();
if (debug) {
logger.log(BasicLevel.DEBUG, "Examining the class: " + sc.getFQName());
if (classExist) {
logger.log(BasicLevel.DEBUG, "class file: " + classFile.getAbsolutePath());
}
}
boolean javaModified = !classExist;
if (classExist) {
//compare the date of the .java and .class files
File javaFile = new File(scp.input, baseName + ".java");
logger.log(BasicLevel.DEBUG, "java file: " + javaFile.getAbsolutePath());
javaModified = (javaFile.lastModified() > classFile.lastModified());
if (!javaModified) {
// Is the class already enhanced ?
try {
Class clazz = classloader.loadClass(sc.getFQName());
sc.setAlreadyEnhanced(implement(clazz, PersistentObjectItf.class));
} catch (NoClassDefFoundError e) {
logger.log(BasicLevel.DEBUG, "Class " + sc.getFQName(), e);
javaModified = true; //in order to delete and recompile the class
} catch (ClassNotFoundException e) {
logger.log(BasicLevel.DEBUG, "Class " + sc.getFQName(), e);
} catch (ClassFormatError e) {
logger.log(BasicLevel.DEBUG, "Class " + sc.getFQName(), e);
} catch (Throwable e) {
String msg = "Impossible to analyze the class '" + sc.getFQName() + "': ";
logger.log(BasicLevel.ERROR, msg, e);
throw new SpeedoException(msg);
}
if (debug) {
logger.log(BasicLevel.DEBUG, "The class " + sc.getFQName()
+ " is "
+ (sc.isAlreadyEnhanced() ? "": "not ")
+ "already enhanced.");
}
}
}
if (classExist && javaModified) {
logger.log(BasicLevel.INFO, "The class " + sc.getFQName() + " has been modified (Remove the .class).");
if (!classFile.delete()) {
throw new SpeedoException("Impossible to remove the file:"
+ classFile.getAbsolutePath());
}
}
sc.setRequireEnhancement(!classExist || javaModified || !sc.isAlreadyEnhanced());
if (!sc.requireEnhancement()) {
enhancedClasses.add(sc);
}
//if this class requires the enhancement, the xml too
xml.requireEnhancement |= sc.requireEnhancement() ;
}
private boolean implement(Class clazz, Class itf) {
boolean res = Arrays.asList(clazz.getInterfaces()).contains(itf);
if (debug) {
logger.log(BasicLevel.DEBUG, "The class " + clazz.getName() + " is "
+ (res ? "" : "not ") + "a " + itf.getName());
}
return res || (clazz.getSuperclass() != null
&& implement(clazz.getSuperclass(), itf));
}
}