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.lib.impl;
016    
017    import java.lang.reflect.Modifier;
018    
019    import org.apache.hivemind.ApplicationRuntimeException;
020    import org.apache.hivemind.ServiceImplementationFactory;
021    import org.apache.hivemind.ServiceImplementationFactoryParameters;
022    import org.apache.hivemind.internal.ServicePoint;
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    import org.apache.hivemind.util.ConstructorUtils;
030    import org.apache.hivemind.util.PropertyAdaptor;
031    import org.apache.hivemind.util.PropertyUtils;
032    
033    /**
034     * Factory that dynamically exposes a property of another service. A proxy is constructed that
035     * accesses the target service and obtains a property from that. The service interface of the
036     * constructed service must match the type of the exposed property.
037     * 
038     * @author Howard Lewis Ship
039     */
040    public class ServicePropertyFactory implements ServiceImplementationFactory
041    {
042        private ClassFactory _classFactory;
043    
044        public Object createCoreServiceImplementation(
045                ServiceImplementationFactoryParameters factoryParameters)
046        {
047            ServicePropertyFactoryParameter p = (ServicePropertyFactoryParameter) factoryParameters
048                    .getFirstParameter();
049    
050            ServicePoint targetServicePoint = p.getServicePoint();
051            final Class targetServiceInterface = targetServicePoint.getServiceInterface();
052                    final Object targetService = targetServicePoint.getService( targetServiceInterface );
053            String propertyName = p.getPropertyName();
054    
055            PropertyAdaptor pa = PropertyUtils.getPropertyAdaptor(targetService, propertyName);
056    
057            String readMethodName = pa.getReadMethodName();
058    
059            if (readMethodName == null)
060                throw new ApplicationRuntimeException(ImplMessages.servicePropertyNotReadable(
061                        propertyName,
062                        targetService), null, p.getLocation(), null);
063            Class serviceInterface = factoryParameters.getServiceInterface();
064    
065            if (!(serviceInterface.isAssignableFrom(pa.getPropertyType())))
066                throw new ApplicationRuntimeException(ImplMessages.servicePropertyWrongType(
067                        propertyName,
068                        targetService,
069                        pa.getPropertyType(),
070                        serviceInterface), p.getLocation(), null);
071    
072            // Now we're good to go.
073    
074            String name = ClassFabUtils.generateClassName(serviceInterface);
075    
076            ClassFab cf = _classFactory.newClass(name, Object.class);
077    
078            addInfrastructure(cf, targetService, serviceInterface, targetServiceInterface, propertyName, readMethodName);
079    
080            addMethods(
081                    cf,
082                    factoryParameters.getServiceId(),
083                    serviceInterface,
084                    propertyName,
085                    targetService);
086    
087            Class proxyClass = cf.createClass();
088    
089            try
090            {
091                return ConstructorUtils.invokeConstructor(proxyClass, new Object[]
092                { targetService });
093            }
094            catch (Throwable ex)
095            {
096                throw new ApplicationRuntimeException(ex.getMessage(), p.getLocation(), ex);
097            }
098        }
099    
100        private void addInfrastructure(ClassFab cf, Object targetService, Class serviceInterface, Class targetServiceInterface,
101                String propertyName, String readPropertyMethodName)
102        {
103            cf.addInterface(serviceInterface);
104    
105            Class targetServiceClass = ClassFabUtils.getInstanceClass(cf, targetService, targetServiceInterface);
106            
107            cf.addField("_targetService", targetServiceClass);
108    
109            cf.addConstructor(new Class[]
110            { targetServiceClass }, null, "{ super(); _targetService = $1; }");
111    
112            BodyBuilder b = new BodyBuilder();
113    
114            b.begin();
115            b.addln(
116                    "{0} property = _targetService.{1}();",
117                    serviceInterface.getName(),
118                    readPropertyMethodName);
119    
120            b.addln("if (property == null)");
121            b.add("  throw new java.lang.NullPointerException(");
122            b.addQuoted(ImplMessages.servicePropertyWasNull(propertyName, targetService));
123            b.addln(");");
124    
125            b.addln("return property;");
126    
127            b.end();
128    
129            MethodSignature sig = new MethodSignature(serviceInterface, "_targetServiceProperty", null,
130                    null);
131            cf.addMethod(Modifier.FINAL | Modifier.PRIVATE, sig, b.toString());
132        }
133    
134        private void addMethods(ClassFab cf, String serviceId, Class serviceInterface,
135                String propertyName, Object targetService)
136        {
137            MethodIterator mi = new MethodIterator(serviceInterface);
138    
139            while (mi.hasNext())
140            {
141                MethodSignature sig = mi.next();
142    
143                String body = "return ($r) _targetServiceProperty()." + sig.getName() + "($$);";
144    
145                cf.addMethod(Modifier.PUBLIC, sig, body);
146            }
147    
148            if (!mi.getToString())
149                ClassFabUtils.addToStringMethod(cf, ImplMessages.servicePropertyToString(
150                        serviceId,
151                        serviceInterface,
152                        propertyName,
153                        targetService));
154        }
155    
156        public void setClassFactory(ClassFactory factory)
157        {
158            _classFactory = factory;
159        }
160    }