001    // Copyright 2006 Howard M. Lewis Ship
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 com.javaforge.tapestry.spring;
016    
017    import java.lang.reflect.Modifier;
018    
019    import org.apache.hivemind.ApplicationRuntimeException;
020    import org.apache.hivemind.Location;
021    import org.apache.hivemind.service.BodyBuilder;
022    import org.apache.hivemind.service.MethodSignature;
023    import org.apache.hivemind.util.Defense;
024    import org.apache.tapestry.enhance.EnhancementOperation;
025    import org.apache.tapestry.enhance.InjectEnhancementWorker;
026    import org.apache.tapestry.spec.InjectSpecification;
027    import org.springframework.beans.factory.BeanFactory;
028    
029    /**
030     * Injects a spring bean into a Tapestry page or component. This is done a bit differently than
031     * injecting a HiveMind service. With HiveMind, a proxy can be obtained and injected, and the
032     * HiveMind proxy will encapsulate the lifecycle of the underlying service (the client doesn't have
033     * to be aware of when the service is instantiated, or what is lifecycle is). Spring is a bit
034     * different, for beans with the prototype lifecycle, it is necessary to keep re-acquiring them.
035     * This form of injection, "spring" injection, does just that; the implementation of the property
036     * getter method will go to the default Spring BeanFactory at ask for each bean; no caching of
037     * Spring beans occurs.
038     * 
039     * @author Howard M. Lewis Ship
040     */
041    public final class SpringBeanInjectionWorker implements InjectEnhancementWorker
042    {
043    
044        private BeanFactory _beanFactory;
045    
046        public void performEnhancement(EnhancementOperation op, InjectSpecification is)
047        {
048            String propertyName = is.getProperty();
049            String objectReference = is.getObject();
050            Location location = is.getLocation();
051    
052            injectSpringBean(op, objectReference, propertyName, location);
053        }
054    
055        private void injectSpringBean(EnhancementOperation op, String beanName, String propertyName,
056                Location location)
057        {
058            Defense.notNull(op, "op");
059            Defense.notNull(beanName, "beanName");
060            Defense.notNull(propertyName, "propertyName");
061    
062            Class propertyType = op.getPropertyType(propertyName);
063    
064            // In case there's just the <inject> in the XML and no property in
065            // the Java class
066    
067            if (propertyType == null)
068                propertyType = Object.class;
069    
070            Class beanType = _beanFactory.getType(beanName);
071    
072            if (!propertyType.isAssignableFrom(beanType))
073                throw new ApplicationRuntimeException(SpringMessages.beanTypeNotCompatibleWithProperty(
074                        beanName,
075                        propertyName,
076                        propertyType), location, null);
077    
078            op.claimReadonlyProperty(propertyName);
079    
080            // Inject the bean factory into the component class
081    
082            String beanFactoryName = op.addInjectedField(
083                    "_$beanFactory",
084                    BeanFactory.class,
085                    _beanFactory);
086            String locationName = op.addInjectedField("_$location", Location.class, location);
087    
088            BodyBuilder builder = new BodyBuilder();
089    
090            builder.begin();
091            builder.addln("try");
092            builder.begin();
093            builder.addln(
094                    "return ({0}) {1}.getBean(\"{2}\");",
095                    propertyType.getName(),
096                    beanFactoryName,
097                    beanName);
098            builder.end();
099            builder.addln("catch (Exception ex)");
100            builder.begin();
101            builder.addln("throw new {0}(ex.getMessage(), {1}, ex);", ApplicationRuntimeException.class
102                    .getName(), locationName);
103            builder.end();
104            builder.end();
105    
106            String methodName = op.getAccessorMethodName(propertyName);
107    
108            MethodSignature sig = new MethodSignature(propertyType, methodName, null, null);
109    
110            op.addMethod(Modifier.PUBLIC, sig, builder.toString(), location);
111        }
112    
113        /** For injection. */
114        public void setBeanFactory(BeanFactory beanFactory)
115        {
116            _beanFactory = beanFactory;
117        }
118    
119    }