All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.topia.service.servers.RemoteClassLoader Maven / Gradle / Ivy

There is a newer version: 2.3.1
Show newest version
/* *##% 
 * ToPIA :: SOA
 * Copyright (C) 2004 - 2009 CodeLutin
 *
 * This program 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 (at your option) any later version.
 *
 * This program 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 Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * ##%*/

package org.nuiton.topia.service.servers;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.rmi.Remote;
import java.rmi.RemoteException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.topia.service.TopiaApplicationServiceFactory;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.CodeVisitor;

// Tout le code d'utilisation de truezip est present
// mais commente pour ne pas garder une dependance
// supplementaire
/*
 * import de.schlichtherle.io.ArchiveException; import de.schlichtherle.io.File;
 * import de.schlichtherle.io.FileOutputStream;
 */

/**
 * RMIRemoteHelper.java
 * 
 * Creer une nouvelle classe en transformant la classe passee en parametre.
 * 
 * Ajoute une interface "Remote" Ajoute des exception a toutes les methodes
 * "RemoteException"
 * 
 * Utilise les API asm.
 * 
 * @see java.rmi.Remote
 * @see java.rmi.RemoteException
 * 
 * @author chatellier
 * @version $Revision: 1715 $
 * 
 * Last update : $Date: 2009-12-15 01:26:16 +0100 (mar. 15 déc. 2009) $ By : $Author: tchemit $
 */
public class RemoteClassLoader implements ClassVisitor {

    /** Logger (common logging) */
    private static final Log logger = LogFactory
            .getLog(RemoteClassLoader.class);

    /** Ajout au nom de classe */
    public static final String EXTENSION = "Remote";

    /**
     * Le writer (global necessaire parce que parcourt par patron visiteur)
     */
    protected ClassWriter cWriter;

    /**
     * Constructeur
     */
    protected RemoteClassLoader() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.objectweb.asm.ClassVisitor#visit(int, int, java.lang.String,
     *      java.lang.String, java.lang.String[], java.lang.String)
     */
    public void visit(int version, int access, String name, String superName,
            String[] interfaces, String sourceFile) {
        // creer un writer
        cWriter = new ClassWriter(false);

        logger.debug("Converting class " + name + " as remote class");

        // nouveau nom de fichier
        String newSourceFileName = sourceFile.replaceAll("(.*)\\.java$", "$1"
                + EXTENSION + ".java");

        // liste des interfaces
        String remoteInterface = Remote.class.getName().replaceAll("\\.",
                String.valueOf(File.separatorChar));
        String[] interfacesNew = null;
        if (interfaces == null) {
            interfacesNew = new String[] { remoteInterface };
        } else {
            interfacesNew = new String[interfaces.length + 1];
            for (int i = 0; i < interfaces.length; ++i)
                interfacesNew[i] = interfaces[i];
            interfacesNew[interfacesNew.length - 1] = remoteInterface;
        }

        cWriter.visit(version, // version de la classe
                access, // visibilite
                name + EXTENSION, // nom
                superName, // superclasse
                interfacesNew, // les interface
                newSourceFileName); // le fichier sources
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.objectweb.asm.ClassVisitor#visitAttribute(org.objectweb.asm.Attribute)
     */
    public void visitAttribute(Attribute attr) {
        logger.warn("Attribute " + attr.toString()
                + " found : conversion not supported");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.objectweb.asm.ClassVisitor#visitEnd()
     */
    public void visitEnd() {
        cWriter.visitEnd();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String,
     *      java.lang.String, java.lang.Object, org.objectweb.asm.Attribute)
     */
    public void visitField(int access, String name, String desc, Object value,
            Attribute attrs) {
        logger.warn("Field " + name + " found : conversion not supported");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.objectweb.asm.ClassVisitor#visitInnerClass(java.lang.String,
     *      java.lang.String, java.lang.String, int)
     */
    public void visitInnerClass(String name, String outerName,
            String innerName, int access) {
        logger.warn("InnerClass " + name + " found : conversion not supported");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String,
     *      java.lang.String, java.lang.String[], org.objectweb.asm.Attribute)
     */
    public CodeVisitor visitMethod(int access, String name, String desc,
            String[] exceptions, Attribute attrs) {
        logger.debug("Processing method " + name);

        // liste des interfaces
        String remoteExceptionName = RemoteException.class.getName()
                .replaceAll("\\.", String.valueOf(File.separatorChar));
        String[] exceptionsNew = null;
        if (exceptions == null) {
            exceptionsNew = new String[] { remoteExceptionName };
        } else {
            exceptionsNew = new String[exceptions.length + 1];
            for (int i = 0; i < exceptions.length; ++i)
                exceptionsNew[i] = exceptions[i];
            exceptionsNew[exceptionsNew.length - 1] = remoteExceptionName;
        }

        cWriter.visitMethod(access, // visibilite
                name, // nom
                desc, // arguments
                exceptionsNew, // les exceptions lancees
                attrs); // retour

        return null;
    }

    /**
     * Retourne le nom d'origine sans l'ajout
     * 
     * @param clazz
     *            la classe remote
     * @return le nom non-remote
     */
    public static String getOriginClassName(Class clazz) {
        return clazz.getName().replaceAll("(.*)" + EXTENSION + "$", "$1");
    }

    /**
     * Retourne la classe version "remote" de celle specifiee
     * 
     * @param clazz
     *            la classe non remote
     * @return la classe remote
     * @throws ClassNotFoundException
     *             si on ne peut pas genere la classe
     */
    public static Class getRemoteClass(Class clazz)
            throws ClassNotFoundException {

        Class remoteClass = null;

        // onregarde si la ressource existe,
        // sinon on la genere

        URL url = ClassLoader.getSystemClassLoader().getResource(
                clazz.getName().replaceAll("\\.", "/") + EXTENSION + ".class");

        if (url == null) {
            // classe non trouvee, on la genere
            logger.debug("Remote class not found for '" + clazz.getName()
                    + "', generating a new one");

            RemoteClassLoader rcl = new RemoteClassLoader();
            // recupere la classe generee
            byte[] clazzBytes = rcl.getClassData(clazz);

            try {
                // la sauve en dur
                rcl.saveOnDisk(clazz, clazzBytes);
            } catch (IOException e2) {
                logger.debug("Can't save generated class on disk", e2);
                throw new ClassNotFoundException("Can't find class : "
                        + clazz.getName() + EXTENSION, e2);
            }
        }

        // retente un forName et devrai maintenant la trouver
        try {
            remoteClass = Class.forName(clazz.getName() + EXTENSION);
        } catch (ClassNotFoundException e2) {
            logger.debug("Can't find generated class", e2);
            throw new ClassNotFoundException("Can't find class : "
                    + clazz.getName() + EXTENSION, e2);
        }

        return remoteClass;
    }

    /**
     * Sauve la classe generee sur le disque.
     * 
     * Dans un jar, ou ds un fichier...
     * 
     * @param clazz
     *            La class d'origine
     * @param clazzBytes
     *            La classe generee
     * @throws IOException
     */
    protected void saveOnDisk(Class clazz, byte[] clazzBytes)
            throws IOException {
        // ensuite, on la sauve en dur, sur disque
        // soit dans un jar, soit sur un fichier physique
        // au meme endroit que la classe d'origine
        URL originClazzPath = ClassLoader.getSystemClassLoader().getResource(
                clazz.getName().replaceAll("\\.",
                        String.valueOf(File.separatorChar))
                        + ".class");

        // suivant le protocol
        // file, jar...
        String protocol = originClazzPath.getProtocol();

        if ("file".equals(protocol)) {
            saveInFile(originClazzPath, clazzBytes);
        } else if (protocol.matches("ear|jar|war|zip")) {
            saveInJarFile(originClazzPath, clazzBytes);
        } else {
            logger.warn("Unsupported protocol '" + protocol
                    + "' for saving clazz.");
        }
    }

    /**
     * Sauve le fichier dans un jar.
     * 
     * Java ne supporte pas le modification des jar. On le sauve maintenant dan
     * le dossier topiagen.
     * 
     * @param originClazzPath
     * @param clazzBytes
     * @throws IOException
     *             si erreur d'ecriture
     */
    protected void saveInJarFile(URL originClazzPath, byte[] clazzBytes)
            throws IOException {
        /*
         * TRUEZIP code // write class on disk String path =
         * originClazzPath.getFile(); // chemin valid truezip String archive =
         * path.replaceAll("^file:(.*)!(.*)\\.class$", "$1"); path =
         * path.replaceAll("^file:(.*)!(.*)\\.class$", "$1$2" + EXTENSION +
         * ".class"); // File f = new File(path); // f.createNewFile();
         * FileOutputStream out = new FileOutputStream(path);
         * out.write(clazzBytes, 0, clazzBytes.length); out.close(); // force
         * l'update de l'archive // sinon la modification n'est pas prise en
         * compte dans l'excecution // courante de la JVM // File.update(new
         * File(archive));
         * 
         * try { File.umount(new File(archive)); } catch (ArchiveException ouch) {
         * logger .debug("ArchiveException , can't umount archive : " +
         * archive); // At least one exception occured which is not just an //
         * ArchiveWarningException. This is a severe situation that // needs to
         * be handled. // Print the sequential chain of exceptions in order of //
         * descending priority and ascending appearance. //
         * ouch.printStackTrace(); // Print the sequential chain of exceptions
         * in order of // appearance instead.
         * ouch.sortAppearance().printStackTrace(); } TRUEZIP code
         */

        String path = originClazzPath.getFile().replaceAll(
                "^file:(.*)!(.*)\\.class$", "$2" + EXTENSION + ".class");

        // creer les dossier et fichier
        java.io.File fRemoteClass = new java.io.File(
                TopiaApplicationServiceFactory.TOPIA_GENERATION_DIRECTORY
                        + String.valueOf(File.separatorChar) + path);
        fRemoteClass.getParentFile().mkdirs();

        // ecrit les donnees
        java.io.FileOutputStream out = new java.io.FileOutputStream(
                fRemoteClass);
        out.write(clazzBytes, 0, clazzBytes.length);
        out.close();

        logger.debug("Remote clazz saved into archive as path : "
                + fRemoteClass.getAbsolutePath());
    }

    /**
     * Genere la classe dans son fichier phisyque
     * 
     * /home/chatellier/tmp/toto.java file:/home/chatellier/tmp/toto.class
     * 
     * @param originClazzPath
     *            l'url d'origine
     * @param clazzBytes
     *            les donnees de la classe
     * @throws IOException
     *             si erreur d'ecriture
     */
    protected void saveInFile(URL originClazzPath, byte[] clazzBytes)
            throws IOException {
        // write class on disk
        String path = originClazzPath.getFile();

        // ajout de "Remote" au nom
        path = path.replaceAll("\\.class$", EXTENSION + ".class");

        java.io.File f = new java.io.File(path);
        // f.createNewFile();
        java.io.FileOutputStream out = new java.io.FileOutputStream(f);
        out.write(clazzBytes, 0, clazzBytes.length);
        out.close();

        logger.debug("Remote class saved to : " + path);
    }

    /**
     * Convertit la class en class "Remote"
     * 
     * @param clazz
     *            la classe non remote
     * @return la classe remote
     */
    private byte[] getClassData(Class clazz) {
        byte[] b = null;

        ClassReader cr;
        try {
            cr = new ClassReader(clazz.getName());
            cr.accept(this, false);
            b = cWriter.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return b;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy