Package org.drools.rule

Source Code of org.drools.rule.PackageCompilationData$PackageClassLoader

package org.drools.rule;

/*
* Copyright 2005 JBoss Inc
*
* 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.
*/

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.drools.CheckedDroolsException;
import org.drools.RuntimeDroolsException;
import org.drools.base.accumulators.JavaAccumulatorFunctionExecutor;
import org.drools.common.DroolsObjectInputStream;
import org.drools.spi.Accumulator;
import org.drools.spi.Consequence;
import org.drools.spi.EvalExpression;
import org.drools.spi.PredicateExpression;
import org.drools.spi.ReturnValueExpression;

public class PackageCompilationData
    implements
    Externalizable {

    /**
     *
     */
    private static final long             serialVersionUID = 400L;

    private static final ProtectionDomain PROTECTION_DOMAIN;

    private Map                           invokerLookups;

    private Object                        AST;

    private Map                           store;

    private Map                           lineMappings;

    private transient PackageClassLoader  classLoader;

    private transient ClassLoader         parentClassLoader;
   
    private boolean                       dirty;

    static {
        PROTECTION_DOMAIN = (ProtectionDomain) AccessController.doPrivileged( new PrivilegedAction() {
            public Object run() {
                return PackageCompilationData.class.getProtectionDomain();
            }
        } );
    }

    /**
     * Default constructor - for Externalizable. This should never be used by a user, as it
     * will result in an invalid state for the instance.
     */
    public PackageCompilationData() {

    }

    public PackageCompilationData(final ClassLoader parentClassLoader) {
        initClassLoader( parentClassLoader );
        this.invokerLookups = new HashMap();
        this.store = new HashMap();
        this.lineMappings = new HashMap();
        this.dirty = false;
    }
   
    public boolean isDirty() {
        return this.dirty;
    }

    private void initClassLoader(ClassLoader parentClassLoader) {
        if ( parentClassLoader == null ) {
            throw new RuntimeDroolsException( "PackageCompilationData cannot have a null parentClassLoader" );
        }
        this.parentClassLoader = parentClassLoader;
        this.classLoader = new PackageClassLoader( this.parentClassLoader );
    }

    /**
     * Handles the write serialization of the PackageCompilationData. Patterns in Rules may reference generated data which cannot be serialized by
     * default methods. The PackageCompilationData holds a reference to the generated bytecode. The generated bytecode must be restored before any Rules.
     *
     */
    public void writeExternal(final ObjectOutput stream) throws IOException {
        stream.writeObject( this.store );
        stream.writeObject( this.AST );

        // Rules must be restored by an ObjectInputStream that can resolve using a given ClassLoader to handle seaprately by storing as
        // a byte[]
        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        final ObjectOutput out = new ObjectOutputStream( bos );
        out.writeObject( this.invokerLookups );
        stream.writeObject( bos.toByteArray() );
    }

    /**
     * Handles the read serialization of the PackageCompilationData. Patterns in Rules may reference generated data which cannot be serialized by
     * default methods. The PackageCompilationData holds a reference to the generated bytecode; which must be restored before any Rules.
     * A custom ObjectInputStream, able to resolve classes against the bytecode, is used to restore the Rules.
     *
     */
    public void readExternal(final ObjectInput stream) throws IOException,
                                                      ClassNotFoundException {
        if ( stream instanceof DroolsObjectInputStream ) {
            DroolsObjectInputStream droolsStream = (DroolsObjectInputStream) stream;
            initClassLoader( droolsStream.getClassLoader() );
        } else {
            initClassLoader( Thread.currentThread().getContextClassLoader() );
        }

        this.store = (Map) stream.readObject();
        this.AST = stream.readObject();

        // Return the rules stored as a byte[]
        final byte[] bytes = (byte[]) stream.readObject();

        //  Use a custom ObjectInputStream that can resolve against a given classLoader
        final DroolsObjectInputStream streamWithLoader = new DroolsObjectInputStream( new ByteArrayInputStream( bytes ),
                                                                                      this.classLoader );
        this.invokerLookups = (Map) streamWithLoader.readObject();
    }

    public ClassLoader getClassLoader() {
        return this.classLoader;
    }

    public byte[] read(final String resourceName) {
        byte[] bytes = null;

        if ( this.store != null ) {
            bytes = ( byte[] ) this.store.get( resourceName );
        }
        return bytes;
    }

    public void write(final String resourceName,
                      final byte[] clazzData) throws RuntimeDroolsException {
        if ( this.store.put( resourceName,
                             clazzData ) != null ) {
            // we are updating an existing class so reload();
            //reload();
            this.dirty = true;
        } else {
            try {
                wire( convertResourceToClassName( resourceName ) );
            } catch ( final Exception e ) {
                e.printStackTrace();
                throw new RuntimeDroolsException( e );
            }
        }

    }

    public boolean remove(final String resourceName) throws RuntimeDroolsException {
        this.invokerLookups.remove( resourceName );
        if ( this.store.remove( convertClassToResourcePath( resourceName ) ) != null ) {
            // we need to make sure the class is removed from the classLoader
            // reload();
            this.dirty = true;
            return true;
        }
        return false;
    }

    public String[] list() {
        if ( this.store == null ) {
            return new String[0];
        }
        final List names = new ArrayList();

        for ( final Iterator it = this.store.keySet().iterator(); it.hasNext(); ) {
            final String name = (String) it.next();
            names.add( name );
        }

        return (String[]) names.toArray( new String[this.store.size()] );
    }

    /**
     * This class drops the classLoader and reloads it. During this process  it must re-wire all the invokeables.
     * @throws CheckedDroolsException
     */
    public void reload() throws RuntimeDroolsException {
        // drops the classLoader and adds a new one
        this.classLoader = new PackageClassLoader( this.parentClassLoader );

        // Wire up invokers
        try {
            for ( final Iterator it = this.invokerLookups.entrySet().iterator(); it.hasNext(); ) {
                Entry entry = (Entry) it.next();
                wire( (String) entry.getKey(),
                      entry.getValue() );
            }
        } catch ( final ClassNotFoundException e ) {
            throw new RuntimeDroolsException( e );
        } catch ( final InstantiationError e ) {
            throw new RuntimeDroolsException( e );
        } catch ( final IllegalAccessException e ) {
            throw new RuntimeDroolsException( e );
        } catch ( final InstantiationException e ) {
            throw new RuntimeDroolsException( e );
        } finally {
            this.dirty = false;
        }
    }

    public void clear() {
        this.store.clear();
        this.invokerLookups.clear();
        this.AST = null;
        reload();
    }

    public void wire(final String className) throws ClassNotFoundException,
                                            InstantiationException,
                                            IllegalAccessException {
        final Object invoker = this.invokerLookups.get( className );
        wire( className,
              invoker );
    }

    public void wire(final String className,
                     final Object invoker) throws ClassNotFoundException,
                                          InstantiationException,
                                          IllegalAccessException {
        final Class clazz = this.classLoader.findClass( className );
        if ( invoker instanceof ReturnValueRestriction ) {
            ((ReturnValueRestriction) invoker).setReturnValueExpression( (ReturnValueExpression) clazz.newInstance() );
        } else if ( invoker instanceof PredicateConstraint ) {
            ((PredicateConstraint) invoker).setPredicateExpression( (PredicateExpression) clazz.newInstance() );
        } else if ( invoker instanceof EvalCondition ) {
            ((EvalCondition) invoker).setEvalExpression( (EvalExpression) clazz.newInstance() );
        } else if ( invoker instanceof Accumulate ) {
            ((Accumulate) invoker).setAccumulator( (Accumulator) clazz.newInstance() );
        } else if ( invoker instanceof Rule ) {
            ((Rule) invoker).setConsequence( (Consequence) clazz.newInstance() );
        } else if ( invoker instanceof JavaAccumulatorFunctionExecutor ) {
            ((JavaAccumulatorFunctionExecutor) invoker).setExpression( (ReturnValueExpression) clazz.newInstance() );
        }
    }

    public String toString() {
        return this.getClass().getName() + this.store.toString();
    }

    public void putInvoker(final String className,
                           final Object invoker) {
        this.invokerLookups.put( className,
                                 invoker );
    }

    public void putAllInvokers(final Map invokers) {
        this.invokerLookups.putAll( invokers );

    }

    public Map getInvokers() {
        return this.invokerLookups;
    }

    public void removeInvoker(final String className) {
        this.invokerLookups.remove( className );
    }

    public Map getLineMappings() {
        if ( this.lineMappings == null ) {
            this.lineMappings = new HashMap();
        }
        return this.lineMappings;
    }

    public LineMappings getLineMappings(final String className) {
        return (LineMappings) getLineMappings().get( className );
    }

    public Object getAST() {
        return this.AST;
    }

    public void setAST(final Object ast) {
        this.AST = ast;
    }

    /**
     * Lifted and adapted from Jakarta commons-jci
     *
     * @author mproctor
     *
     */
    public class PackageClassLoader extends ClassLoader
        implements
        DroolsClassLoader {

        public PackageClassLoader(final ClassLoader parentClassLoader) {
            super( parentClassLoader );
        }

        public Class fastFindClass(final String name) {
            final Class clazz = findLoadedClass( name );

            if ( clazz == null ) {
                final byte[] clazzBytes = read( convertClassToResourcePath( name ) );
                if ( clazzBytes != null ) {
                    return defineClass( name,
                                        clazzBytes,
                                        0,
                                        clazzBytes.length,
                                        PROTECTION_DOMAIN );
                }
            }

            return clazz;
        }

        /**
         * Javadocs recommend that this method not be overloaded. We overload this so that we can prioritise the fastFindClass
         * over method calls to parent.loadClass(name, false); and c = findBootstrapClass0(name); which the default implementation
         * would first - hence why we call it "fastFindClass" instead of standard findClass, this indicates that we give it a
         * higher priority than normal.
         *
         */
        protected synchronized Class loadClass(final String name,
                                               final boolean resolve) throws ClassNotFoundException {
            Class clazz = fastFindClass( name );

            if ( clazz == null ) {
                final ClassLoader parent = getParent();
                if ( parent != null ) {
                    clazz = parent.loadClass( name );
                } else {
                    throw new ClassNotFoundException( name );
                }
            }

            if ( resolve ) {
                resolveClass( clazz );
            }

            return clazz;
        }

        protected Class findClass(final String name) throws ClassNotFoundException {
            final Class clazz = fastFindClass( name );
            if ( clazz == null ) {
                throw new ClassNotFoundException( name );
            }
            return clazz;
        }

        public InputStream getResourceAsStream(final String name) {
            final byte[] bytes = (byte[]) PackageCompilationData.this.store.get( name );
            if ( bytes != null ) {
                return new ByteArrayInputStream( bytes );
            } else {
                InputStream input = this.getParent().getResourceAsStream( name );
                if ( input == null ) {
                    input = super.getResourceAsStream( name );
                }
                return input;
            }
        }
    }

    /**
     * Please do not use - internal
     * org/my/Class.xxx -> org.my.Class
     */
    public static String convertResourceToClassName(final String pResourceName) {
        return stripExtension( pResourceName ).replace( '/',
                                                        '.' );
    }

    /**
     * Please do not use - internal
     * org.my.Class -> org/my/Class.class
     */
    public static String convertClassToResourcePath(final String pName) {
        return pName.replace( '.',
                              '/' ) + ".class";
    }

    /**
     * Please do not use - internal
     * org/my/Class.xxx -> org/my/Class
     */
    public static String stripExtension(final String pResourceName) {
        final int i = pResourceName.lastIndexOf( '.' );
        final String withoutExtension = pResourceName.substring( 0,
                                                                 i );
        return withoutExtension;
    }

}
TOP

Related Classes of org.drools.rule.PackageCompilationData$PackageClassLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.