org.coldswap.tracker.ClassListener.java Source code

Java tutorial

Introduction

Here is the source code for org.coldswap.tracker.ClassListener.java

Source

package org.coldswap.tracker;

import net.contentobjects.jnotify.JNotifyListener;
import org.coldswap.instrumentation.ClassInstrumenter;
import org.coldswap.transformer.ClassRedefiner;
import org.coldswap.transformer.ReferenceReplacerManager;
import org.coldswap.util.ByteCodeClassLoader;
import org.coldswap.util.ClassUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;

import java.io.File;
import java.io.FileFilter;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.Map;
import java.util.logging.Logger;

/**
 * (C) Copyright 2013 Faur Ioan-Aurel.
 * <p/>
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 * <p/>
 * 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.
 * <p/>
 * Contributors:
 * faur
 * <p/>
 * Created at:
 * 5:53 PM       3/17/13
 */

/**
 * Listen for any class file modifications and instrument
 * JVM class loader to reload any modification that has been
 * made to the byte code.
 */
public class ClassListener implements JNotifyListener {
    private final static String sep = System.getProperty("file.separator");
    private final static Logger logger = Logger.getLogger(ClassListener.class.getName());
    private final int maxNumberOfMethods;
    private ReferenceReplacerManager refManager = ReferenceReplacerManager.getInstance();

    static {
        logger.setLevel(ClassUtil.logLevel);
    }

    public ClassListener(int maxNumberOfMethods) {
        this.maxNumberOfMethods = maxNumberOfMethods;
    }

    private final FileFilter filter = new ClassFileFilter();

    @Override
    public void fileCreated(int i, String s, String s2) {
        //pass
    }

    @Override
    public void fileDeleted(int i, String s, String s2) {
        //pass
    }

    @Override
    public void fileModified(int i, String root, String className) {
        if ((!"".equals(className)) && (null != className))
            if (filter.accept(new File(className))) {
                Map<String, Class<?>> loaded = ClassInstrumenter.getInstance().getLoadedClasses();
                byte[] bytes = ByteCodeClassLoader.loadClassBytes(root + sep + className);
                // first eight bytes for versioning
                if (bytes.length < 8) {
                    return;
                }
                ClassNode classNode = new ClassNode(Opcodes.ASM5);
                ClassReader classReader = new ClassReader(bytes);
                classReader.accept(classNode, 0);
                String clsName = classNode.name.replace('/', '.').replace(".class", "");

                logger.info("Class " + clsName + " has been modified on the disk");

                if ((!"".equals(clsName)) && (null != clsName)) {
                    Class<?> clazz = loaded.get(clsName);
                    if (clazz == null) {
                        logger.info(clsName + " is new class file!");
                        try {
                            Class<?> cla = Class.forName(clsName);
                            // run reference replacer
                            bytes = refManager.runReferenceReplacer(bytes);
                            ClassDefinition def = new ClassDefinition(cla, bytes);
                            Instrumentation inst = ClassInstrumenter.getInstance().getInstrumenter();
                            inst.redefineClasses(def);
                        } catch (ClassNotFoundException e) {
                            // it means that the class was not loaded yet into machine
                            // so no worries here
                        } catch (UnmodifiableClassException e) {
                            logger.warning(toString());
                        }

                        return;
                    }
                    ClassRedefiner redefiner = new ClassRedefiner(clsName, root + sep + className,
                            maxNumberOfMethods);
                    try {
                        redefiner.redefineClass(clazz);
                    } catch (UnmodifiableClassException e) {
                        logger.severe(e.toString());
                    } catch (ClassNotFoundException e) {
                        logger.severe(e.toString());
                    }
                }
            }
    }

    @Override
    public void fileRenamed(int i, String s, String s2, String s3) {
        //pass
    }
}