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