package de.vksi.c4j.internal; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import javassist.CtClass; import javassist.NotFoundException; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Layout; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import de.vksi.c4j.internal.classfile.ClassFilePool; import de.vksi.c4j.internal.configuration.LocalConfigurationCallback; import de.vksi.c4j.internal.configuration.XmlConfigurationManager; import de.vksi.c4j.internal.configuration.XmlLocalConfiguration; import de.vksi.c4j.internal.contracts.ContractInfo; import de.vksi.c4j.internal.contracts.ContractRegistry; import de.vksi.c4j.internal.contracts.InvolvedTypeInspector; import de.vksi.c4j.internal.contracts.Transformed; import de.vksi.c4j.internal.transformer.affected.AffectedClassTransformer; import de.vksi.c4j.internal.transformer.contract.ContractClassTransformer; import de.vksi.c4j.internal.types.ListOrderedSet; public class RootTransformer { public static final RootTransformer INSTANCE = new RootTransformer(); private static final Logger LOGGER = Logger.getLogger(RootTransformer.class); AffectedClassTransformer targetClassTransformer; ContractClassTransformer contractClassTransformer; private final InvolvedTypeInspector involvedTypeInspector = new InvolvedTypeInspector(); private final XmlConfigurationManager xmlConfiguration = XmlConfigurationManager.INSTANCE; private final Set<ClassLoader> classLoaders = new HashSet<ClassLoader>(); private RootTransformer() { } public void init() throws Exception { targetClassTransformer = new AffectedClassTransformer(); contractClassTransformer = new ContractClassTransformer(); loadLogger(); xmlConfiguration.registerClassLoader(ClassLoader.getSystemClassLoader()); xmlConfiguration.registerAndFeedLocalConfigurationCallback(new LocalConfigurationCallback() { @Override public void scanExternalContracts(XmlLocalConfiguration xmlLocalConfiguration) throws Exception { ContractRegistry.INSTANCE.registerExternalContract(xmlLocalConfiguration); } }); } private void loadLogger() { Enumeration<?> allAppenders = Logger.getRootLogger().getAllAppenders(); if (!allAppenders.hasMoreElements()) { Layout layout = new PatternLayout("C4J %-5p - %m%n"); Logger.getRootLogger().addAppender(new ConsoleAppender(layout)); Logger.getRootLogger().setLevel(Level.INFO); LOGGER.info("No Appender on RootLogger found, added a new ConsoleAppender on Level INFO."); } } public byte[] transformType(CtClass affectedClass) throws Exception { if (affectedClass.isInterface()) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("transformation aborted, as class is an interface"); } return null; } if (!affectedClass.hasAnnotation(Transformed.class)) { transformClass(affectedClass); } if (xmlConfiguration.getGlobalConfiguration().writeTransformedClasses()) { affectedClass.writeFile(xmlConfiguration.getGlobalConfiguration().writeTransformedClassesDirectory()); } return affectedClass.toBytecode(); } private void transformClass(CtClass affectedClass) throws Exception { if (ContractRegistry.INSTANCE.isContractClass(affectedClass)) { ContractInfo contractInfo = ContractRegistry.INSTANCE.getContractInfo(affectedClass); transformContractClass(affectedClass, contractInfo); } else { transformAffectedClass(affectedClass); } } private void transformAffectedClass(CtClass affectedClass) throws NotFoundException, Exception { ListOrderedSet<CtClass> involvedTypes = involvedTypeInspector.inspect(affectedClass); ListOrderedSet<ContractInfo> contracts = transformInvolvedContracts(affectedClass, involvedTypes); for (ContractInfo contract : contracts) { LOGGER.info(affectedClass.getSimpleName() + " must fulfill contract " + contract.getContractClass().getSimpleName() + " (defined on " + contract.getTargetClass().getSimpleName() + ")."); } targetClassTransformer.transform(involvedTypes, contracts, affectedClass); } private ListOrderedSet<ContractInfo> transformInvolvedContracts(CtClass affectedClass, ListOrderedSet<CtClass> involvedTypes) throws NotFoundException, Exception { ListOrderedSet<ContractInfo> contracts = ContractRegistry.INSTANCE.getContractsForTypes(involvedTypes, affectedClass); for (ContractInfo contract : contracts) { for (CtClass contractClass : contract.getAllContractClasses()) { if (!contractClass.hasAnnotation(Transformed.class)) { transformContractClass(contractClass, contract); } } } return contracts; } private void transformContractClass(CtClass contractClass, ContractInfo contractInfo) throws Exception { contractClassTransformer.transform(contractInfo, contractClass); } public void updateClassPath(ClassLoader loader, byte[] classfileBuffer, String className) { if (loader != null && !classLoaders.contains(loader)) { classLoaders.add(loader); addClassLoader(loader); } if (classfileBuffer != null) { ClassFilePool.INSTANCE.addClassFile(classfileBuffer, className); } } private void addClassLoader(ClassLoader loader) { ClassFilePool.INSTANCE.addClassLoader(loader); try { xmlConfiguration.registerClassLoader(loader); } catch (Exception e) { LOGGER.error("Could not add ClassLoader " + loader.getClass().getName() + " to configuration.", e); } } public XmlConfigurationManager getXmlConfiguration() { return xmlConfiguration; } }