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
015package org.apache.hivemind.impl;
016
017import java.io.Serializable;
018import java.lang.reflect.Modifier;
019
020import org.apache.hivemind.internal.Module;
021import org.apache.hivemind.internal.ServicePoint;
022import org.apache.hivemind.internal.ser.ServiceSerializationHelper;
023import org.apache.hivemind.service.BodyBuilder;
024import org.apache.hivemind.service.ClassFab;
025import org.apache.hivemind.service.ClassFabUtils;
026import org.apache.hivemind.service.ClassFactory;
027import org.apache.hivemind.service.MethodIterator;
028import 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 */
035public 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}