001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.impl;
016    
017    import java.io.Serializable;
018    import java.lang.reflect.Modifier;
019    
020    import org.apache.hivemind.internal.Module;
021    import org.apache.hivemind.internal.ser.ServiceSerializationHelper;
022    import org.apache.hivemind.service.BodyBuilder;
023    import org.apache.hivemind.service.ClassFab;
024    import org.apache.hivemind.service.ClassFabUtils;
025    import org.apache.hivemind.service.ClassFactory;
026    import org.apache.hivemind.service.MethodIterator;
027    import org.apache.hivemind.service.MethodSignature;
028    
029    /**
030     * Class used to assist extension points in creating proxies.
031     * 
032     * @author Howard Lewis Ship
033     */
034    public final class ProxyBuilder
035    {
036        private Class _serviceInterface;
037    
038        private ClassFab _classFab;
039    
040        private String _type;
041    
042        /**
043         * Constructs a new builder. The type will be incorporated into value returned by the
044         * <code>toString()</code> method. 
045         * The generated proxy has an constructor that expects the extension point id as single parameter. 
046         * 
047         * @param type
048         *            used as part of the <code>toString()</code> method's return value
049         * @param module
050         *            the module that constructs the proxy 
051         * @param serviceInterface
052         *            the interface of the proxied class.  
053         * @param declaredInterface
054         *            the interface of the proxied class or the class itself if the proxied class is actually a POJO.
055         * @param outerProxy
056         *            if false, then the proxy can extend the configuration points service interface always.
057         *            If true and the declared interface is actually a bean class (not
058         *            an interface), then the proxy will be a subclass of that bean.
059         */
060        public ProxyBuilder(String type, Module module, Class serviceInterface, Class declaredInterface, boolean outerProxy)
061        {
062            _type = type;
063            _serviceInterface = serviceInterface;
064    
065            ClassFactory factory = (ClassFactory) module.getService(
066                    "hivemind.ClassFactory",
067                    ClassFactory.class);
068    
069            boolean extendBeanClass = outerProxy && !declaredInterface.isInterface();
070            Class baseClass = extendBeanClass ? declaredInterface : Object.class;
071    
072            _classFab = factory.newClass(ClassFabUtils.generateClassName(_serviceInterface), baseClass);
073    
074            _classFab.addField("_extensionPointId", String.class);
075            
076            _classFab.addConstructor(new Class[] { String.class }, null, "{ _extensionPointId = $1; }");
077            
078            if (!extendBeanClass)
079                _classFab.addInterface(_serviceInterface);
080    
081            // Not exactly certain this will work with non-interface beans that already
082            // are serializable!
083    
084            if (outerProxy)
085                addSerializable();
086        }
087    
088        /** @since 1.1 */
089        private void addSerializable()
090        {
091            _classFab.addInterface(Serializable.class);
092    
093            BodyBuilder bb = new BodyBuilder();
094    
095            bb.add(
096                    "return {0}.getServiceSerializationSupport().getServiceTokenForService(_extensionPointId);",
097                    ServiceSerializationHelper.class.getName());
098    
099            MethodSignature sig = new MethodSignature(Object.class, "writeReplace", null, null);
100    
101            _classFab.addMethod(Modifier.PRIVATE, sig, bb.toString());
102        }
103    
104        public ClassFab getClassFab()
105        {
106            return _classFab;
107        }
108        
109        /**
110         * @see #addServiceMethods(String, boolean)
111         */
112        public void addServiceMethods(String indirection)
113        {
114            addServiceMethods(indirection, true);
115        }
116    
117        /**
118         * Creates the service methods for the class.
119         * 
120         * @param indirection
121         *            the name of a variable, or a method invocation snippet, used to redirect the
122         *            invocation on the proxy to the actual service implementation.
123         * @param addToString if true, a implementation of the toString method is generated that
124         *    returns some info about the proxy
125         */
126        public void addServiceMethods(String indirection, boolean addToString)
127        {
128            BodyBuilder builder = new BodyBuilder();
129    
130            MethodIterator mi = new MethodIterator(_serviceInterface);
131            while (mi.hasNext())
132            {
133                MethodSignature m = mi.next();
134                if( !_classFab.containsMethod( m ) )
135                {
136                    builder.clear();
137                    builder.begin();
138                    builder.add("return ($r) ");
139                    builder.add(indirection);
140                    builder.add(".");
141                    builder.add(m.getName());
142                    builder.addln("($$);");
143                    builder.end();
144                    _classFab.addMethod(Modifier.PUBLIC, m, builder.toString());
145                }
146            }
147    
148            if (!mi.getToString() && addToString)
149                ClassFabUtils.addToStringMethod(_classFab, "<" + _type + " for \" + _extensionPointId + \""
150                        + "(" + _serviceInterface.getName() + ")>");
151        }
152    }