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.ServicePoint;
022    import org.apache.hivemind.internal.ser.ServiceSerializationHelper;
023    import org.apache.hivemind.service.BodyBuilder;
024    import org.apache.hivemind.service.ClassFab;
025    import org.apache.hivemind.service.ClassFabUtils;
026    import org.apache.hivemind.service.ClassFactory;
027    import org.apache.hivemind.service.MethodIterator;
028    import org.apache.hivemind.service.MethodSignature;
029    
030    /**
031     * Class used to assist service extension points in creating proxies.
032     * 
033     * @author Howard Lewis Ship
034     */
035    public final class ProxyBuilder
036    {
037        private ServicePoint _point;
038    
039        private Class _serviceInterface;
040    
041        private ClassFab _classFab;
042    
043        private String _type;
044    
045        public ProxyBuilder(String type, ServicePoint point)
046        {
047            this(type, point, false);
048        }
049    
050        /**
051         * Constructs a new builder. The type will be incorporated into value returned by the
052         * <code>toString()</code> method. The service extension point is used to identify the service
053         * interface and service id.
054         * 
055         * @param type
056         *            used as part of the <code>toString()</code> method's return value
057         * @param point
058         *            the service point for which this proxy is being constructed
059         * @param outerProxy
060         *            if false, then the proxy can extend the service points service interface always.
061         *            If true and the service point's declared interface is actually a bean class (not
062         *            an interface), then the proxy will be a subclass of that bean.
063         */
064        public ProxyBuilder(String type, ServicePoint point, boolean outerProxy)
065        {
066            _point = point;
067            _type = type;
068            _serviceInterface = point.getServiceInterface();
069    
070            Class declaredInterface = point.getDeclaredInterface();
071    
072            Module module = point.getModule();
073            ClassFactory factory = (ClassFactory) module.getService(
074                    "hivemind.ClassFactory",
075                    ClassFactory.class);
076    
077            boolean extendBeanClass = outerProxy && !declaredInterface.isInterface();
078            Class baseClass = extendBeanClass ? declaredInterface : Object.class;
079    
080            _classFab = factory.newClass(ClassFabUtils.generateClassName(_serviceInterface), baseClass);
081    
082            if (!extendBeanClass)
083                _classFab.addInterface(_serviceInterface);
084    
085            // Not exactly certain this will work with non-interface beans that already
086            // are serializable!
087    
088            if (outerProxy)
089                addSerializable(point.getExtensionPointId());
090        }
091    
092        /** @since 1.1 */
093        private void addSerializable(String pointId)
094        {
095            _classFab.addInterface(Serializable.class);
096    
097            BodyBuilder bb = new BodyBuilder();
098    
099            bb.add(
100                    "return {0}.getServiceSerializationSupport().getServiceTokenForService(\"{1}\");",
101                    ServiceSerializationHelper.class.getName(),
102                    pointId);
103    
104            MethodSignature sig = new MethodSignature(Object.class, "writeReplace", null, null);
105    
106            _classFab.addMethod(Modifier.PRIVATE, sig, bb.toString());
107        }
108    
109        public ClassFab getClassFab()
110        {
111            return _classFab;
112        }
113    
114        /**
115         * Creates the service methods for the class.
116         * 
117         * @param indirection
118         *            the name of a variable, or a method invocation snippet, used to redirect the
119         *            invocation on the proxy to the actual service implementation.
120         */
121        public void addServiceMethods(String indirection)
122        {
123            BodyBuilder builder = new BodyBuilder();
124    
125            MethodIterator mi = new MethodIterator(_serviceInterface);
126            while (mi.hasNext())
127            {
128                MethodSignature m = mi.next();
129                if( !_classFab.containsMethod( m ) )
130                {
131                    builder.clear();
132                    builder.begin();
133                    builder.add("return ($r) ");
134                    builder.add(indirection);
135                    builder.add(".");
136                    builder.add(m.getName());
137                    builder.addln("($$);");
138                    builder.end();
139                    _classFab.addMethod(Modifier.PUBLIC, m, builder.toString());
140                }
141            }
142    
143            if (!mi.getToString())
144                ClassFabUtils.addToStringMethod(_classFab, "<" + _type + " for "
145                        + _point.getExtensionPointId() + "(" + _serviceInterface.getName() + ")>");
146        }
147    }