org.copperengine.core.wfrepo.AbstractWorkflowRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.copperengine.core.wfrepo.AbstractWorkflowRepository.java

Source

/*
 * Copyright 2002-2014 SCOOP Software GmbH
 *
 * 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.copperengine.core.wfrepo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

import org.copperengine.core.common.WorkflowRepository;
import org.copperengine.core.instrument.ClassInfo;
import org.copperengine.core.instrument.ScottyClassAdapter;
import org.copperengine.core.instrument.Transformed;
import org.copperengine.core.instrument.TryCatchBlockHandler;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.TraceClassVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

abstract class AbstractWorkflowRepository implements WorkflowRepository {

    private static final Logger logger = LoggerFactory.getLogger(AbstractWorkflowRepository.class);

    private static final int flags = ClassReader.EXPAND_FRAMES;

    void instrumentWorkflows(File adaptedTargetDir, Map<String, Clazz> clazzMap, Map<String, ClassInfo> classInfos,
            File compileTargetDir) throws IOException {
        logger.info("Instrumenting classfiles");
        URLClassLoader tmpClassLoader = new URLClassLoader(new URL[] { compileTargetDir.toURI().toURL() },
                Thread.currentThread().getContextClassLoader());
        for (Clazz clazz : clazzMap.values()) {
            byte[] bytes;
            FileInputStream fis = new FileInputStream(clazz.classfile);
            try {
                ClassReader cr2 = new ClassReader(fis);
                ClassNode cn = new ClassNode();
                cr2.accept(cn, flags);

                // Now content of ClassNode can be modified and then serialized back into bytecode:
                new TryCatchBlockHandler().instrument(cn);

                ClassWriter cw2 = new ClassWriter(0);
                cn.accept(cw2);
                bytes = cw2.toByteArray();

                if (logger.isTraceEnabled()) {
                    StringWriter sw = new StringWriter();
                    new ClassReader(bytes).accept(new TraceClassVisitor(new PrintWriter(sw)), 0);
                    logger.trace(sw.toString());
                }

                ClassReader cr = new ClassReader(bytes);
                ClassWriter cw = new ClassWriter(0);

                ScottyClassAdapter cv = new ScottyClassAdapter(cw, clazz.aggregatedInterruptableMethods);
                cr.accept(cv, flags);
                classInfos.put(clazz.classname, cv.getClassInfo());
                bytes = cw.toByteArray();

                // Recompute frames, etc.
                ClassReader cr3 = new ClassReader(bytes);
                ClassWriter cw3 = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                cr3.accept(cw3, ClassReader.SKIP_FRAMES);
                bytes = cw3.toByteArray();

                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), tmpClassLoader, false, pw);
                if (sw.toString().length() != 0) {
                    logger.error("CheckClassAdapter.verify failed for class " + cn.name + ":\n" + sw.toString());
                } else {
                    logger.info("CheckClassAdapter.verify succeeded for class " + cn.name);
                }

            } finally {
                fis.close();
            }

            File adaptedClassfileName = new File(adaptedTargetDir, clazz.classname + ".class");
            adaptedClassfileName.getParentFile().mkdirs();
            FileOutputStream fos = new FileOutputStream(adaptedClassfileName);
            try {
                fos.write(bytes);
            } finally {
                fos.close();
            }
        }
    }

    ClassLoader createClassLoader(Map<String, Class<?>> map, File adaptedTargetDir, File compileTargetDir,
            Map<String, Clazz> clazzMap) throws MalformedURLException, ClassNotFoundException {
        logger.info("Creating classes");
        final Map<String, Clazz> clazzMapCopy = new HashMap<String, Clazz>(clazzMap);
        URLClassLoader classLoader = new URLClassLoader(
                new URL[] { adaptedTargetDir.toURI().toURL(), compileTargetDir.toURI().toURL() },
                Thread.currentThread().getContextClassLoader()) {
            @Override
            protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    try {
                        c = super.findClass(name);
                        if (clazzMapCopy.containsKey(name)) {
                            // Check that the workflow class is transformed
                            if (c.getAnnotation(Transformed.class) == null)
                                throw new ClassFormatError("Copper workflow " + name + " is not transformed!");
                        }
                        logger.info(c.getName() + " created");
                    } catch (Exception e) {
                        c = super.loadClass(name, false);
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        };

        for (Clazz clazz : clazzMap.values()) {
            String name = clazz.classname.replace('/', '.');
            Class<?> c = classLoader.loadClass(name);
            map.put(name, c);
        }

        return classLoader;
    }

}