001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.naming.deployment;
019    
020    import java.lang.reflect.Field;
021    import java.lang.reflect.Method;
022    import java.util.Collection;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    import javax.annotation.Resource;
027    import javax.xml.namespace.QName;
028    
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    import org.apache.geronimo.common.DeploymentException;
032    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
033    import org.apache.geronimo.gbean.GBeanInfo;
034    import org.apache.geronimo.gbean.GBeanInfoBuilder;
035    import org.apache.geronimo.j2ee.deployment.Module;
036    import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedApp;
037    import org.apache.geronimo.j2ee.deployment.annotation.HandlerChainAnnotationHelper;
038    import org.apache.geronimo.j2ee.deployment.annotation.ResourceAnnotationHelper;
039    import org.apache.geronimo.j2ee.deployment.annotation.WebServiceRefAnnotationHelper;
040    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
041    import org.apache.geronimo.kernel.repository.Environment;
042    import org.apache.geronimo.xbeans.geronimo.naming.GerServiceRefDocument;
043    import org.apache.geronimo.xbeans.geronimo.naming.GerServiceRefType;
044    import org.apache.geronimo.xbeans.javaee.DescriptionType;
045    import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
046    import org.apache.geronimo.xbeans.javaee.InjectionTargetType;
047    import org.apache.geronimo.xbeans.javaee.JndiNameType;
048    import org.apache.geronimo.xbeans.javaee.ServiceRefType;
049    import org.apache.geronimo.xbeans.javaee.XsdStringType;
050    import org.apache.xmlbeans.QNameSet;
051    import org.apache.xmlbeans.XmlObject;
052    
053    public class SwitchingServiceRefBuilder extends AbstractNamingBuilder {
054    
055        private static final Log log = LogFactory.getLog(SwitchingServiceRefBuilder.class);
056    
057        private static final QName GER_SERVICE_REF_QNAME = GerServiceRefDocument.type
058                .getDocumentElementName();
059    
060        private static final QNameSet GER_SERVICE_REF_QNAME_SET = QNameSet
061                .singleton(GER_SERVICE_REF_QNAME);
062    
063        private final QNameSet serviceRefQNameSet;
064    
065        private final Collection jaxrpcBuilders;
066    
067        private final Collection jaxwsBuilders;
068    
069        public SwitchingServiceRefBuilder(String[] eeNamespaces,
070                Collection jaxrpcBuilders,
071                Collection jaxwsBuilders) {
072            super(null);
073            this.jaxrpcBuilders = jaxrpcBuilders;
074            this.jaxwsBuilders = jaxwsBuilders;
075            this.serviceRefQNameSet = buildQNameSet(eeNamespaces, "service-ref");
076        }
077    
078        public void buildEnvironment(XmlObject specDD,
079                XmlObject plan,
080                Environment environment)
081                throws DeploymentException {
082            if (this.jaxrpcBuilders != null && !this.jaxrpcBuilders.isEmpty()) {
083                mergeEnvironment(environment, getJAXRCPBuilder());
084            }
085            if (this.jaxwsBuilders != null && !this.jaxwsBuilders.isEmpty()) {
086                mergeEnvironment(environment, getJAXWSBuilder());
087            }
088        }
089    
090        public void buildNaming(XmlObject specDD,
091                XmlObject plan,
092                Module module,
093                Map componentContext) throws DeploymentException {
094    
095            // Discover and process any @WebServiceRef annotations (if !metadata-complete)
096            if ((module != null) && (module.getClassFinder() != null)) {
097                processAnnotations(module);
098            }
099    
100            ClassLoader cl = module.getEarContext().getClassLoader();
101            Class jaxrpcClass = loadClass("javax.xml.rpc.Service", cl);
102            Class jaxwsClass = loadClass("javax.xml.ws.Service", cl);
103    
104            XmlObject[] serviceRefs = specDD.selectChildren(serviceRefQNameSet);
105    
106            XmlObject[] gerServiceRefsUntyped = plan == null ? NO_REFS : plan
107                    .selectChildren(GER_SERVICE_REF_QNAME_SET);
108            Map serviceRefMap = mapServiceRefs(gerServiceRefsUntyped);
109    
110            for (XmlObject serviceRef : serviceRefs) {
111                ServiceRefType serviceRefType = (ServiceRefType) convert(
112                        serviceRef, JEE_CONVERTER, ServiceRefType.type);
113    
114                String name = getStringValue(serviceRefType.getServiceRefName());
115                GerServiceRefType gerServiceRefType = (GerServiceRefType) serviceRefMap.get(name);
116                serviceRefMap.remove(name);
117    
118                String serviceInterfaceName = getStringValue(serviceRefType
119                        .getServiceInterface());
120                Class serviceInterfaceClass = loadClass(serviceInterfaceName, cl);
121    
122                InjectionTargetType[] injections = serviceRefType.getInjectionTargetArray();
123                addInjections(name, injections, componentContext);
124    
125                if (jaxrpcClass.isAssignableFrom(serviceInterfaceClass)) {
126                    // class jaxrpc handler
127                    ServiceRefBuilder jaxrpcBuilder = getJAXRCPBuilder();
128                    jaxrpcBuilder.buildNaming(serviceRef, gerServiceRefType,
129                            module, componentContext);
130                } else if (jaxwsClass.isAssignableFrom(serviceInterfaceClass)) {
131                    // calll jaxws handler
132                    ServiceRefBuilder jaxwsBuilder = getJAXWSBuilder();
133                    jaxwsBuilder.buildNaming(serviceRef, gerServiceRefType, module,
134                            componentContext);
135                } else {
136                    throw new DeploymentException(serviceInterfaceName
137                            + " does not extend "
138                            + jaxrpcClass.getName() + " or "
139                            + jaxwsClass.getName());
140                }
141            }
142    
143            if (serviceRefMap.size() > 0) {
144                log.warn("Failed to build reference to service reference "+serviceRefMap.keySet()+" defined in plan file, reason - corresponding entry in deployment descriptor missing.");
145            }
146        }
147    
148        private ServiceRefBuilder getJAXWSBuilder() throws DeploymentException {
149            ServiceRefBuilder jaxwsBuilder = null;
150            if (this.jaxwsBuilders == null || this.jaxwsBuilders.isEmpty()) {
151                throw new DeploymentException(
152                        "No JAX-WS ServiceRefBuilders registered");
153            } else {
154                jaxwsBuilder = (ServiceRefBuilder) this.jaxwsBuilders.iterator()
155                        .next();
156            }
157            return jaxwsBuilder;
158        }
159    
160        private ServiceRefBuilder getJAXRCPBuilder() throws DeploymentException {
161            ServiceRefBuilder jaxrpcBuilder = null;
162            if (this.jaxrpcBuilders == null || this.jaxrpcBuilders.isEmpty()) {
163                throw new DeploymentException(
164                        "No JAX-RPC ServiceRefBuilders registered");
165            } else {
166                jaxrpcBuilder = (ServiceRefBuilder) this.jaxrpcBuilders.iterator()
167                        .next();
168            }
169            return jaxrpcBuilder;
170        }
171    
172        private void mergeEnvironment(Environment environment, ServiceRefBuilder builder) {
173            Environment env = builder.getEnvironment();
174            if (env != null) {
175                EnvironmentBuilder.mergeEnvironments(environment, env);
176            }
177        }
178    
179        private Class loadClass(String name, ClassLoader cl)
180                throws DeploymentException {
181            try {
182                return cl.loadClass(name);
183            } catch (ClassNotFoundException e) {
184                throw new DeploymentException("Could not load service class "
185                        + name, e);
186            }
187        }
188    
189        private static Map mapServiceRefs(XmlObject[] refs) {
190            Map refMap = new HashMap();
191            if (refs != null) {
192                for (int i = 0; i < refs.length; i++) {
193                    GerServiceRefType ref = (GerServiceRefType) refs[i].copy()
194                            .changeType(GerServiceRefType.type);
195                    String serviceRefName = ref.getServiceRefName().trim();
196                    refMap.put(serviceRefName, ref);
197                }
198            }
199            return refMap;
200        }
201    
202        private void processAnnotations(Module module) throws DeploymentException {
203    
204            // Process all the annotations for this naming builder type
205            //At the moment the only exception thrown is if the resulting doc is not valid.  Bail now.
206            try {
207                WebServiceRefAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder());
208                ResourceAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder(), ServiceRefProcessor.INSTANCE);
209            }
210            catch (Exception e) {
211                log.warn("Unable to process @Resource annotations for module" + module.getName(), e);
212            }
213        }
214    
215        public QNameSet getSpecQNameSet() {
216            return serviceRefQNameSet;
217        }
218    
219        public QNameSet getPlanQNameSet() {
220            return GER_SERVICE_REF_QNAME_SET;
221        }
222    
223        public static class ServiceRefProcessor extends ResourceAnnotationHelper.ResourceProcessor {
224    
225            public static final ServiceRefProcessor INSTANCE = new ServiceRefProcessor();
226    
227            private ServiceRefProcessor() {
228            }
229    
230            public boolean processResource(AnnotatedApp annotatedApp, Resource annotation, Class cls, Method method, Field field) {
231                log.debug("processResource( [annotatedApp] " + annotatedApp.toString() + "," + '\n' +
232                        "[annotation] " + annotation.toString() + "," + '\n' +
233                        "[cls] " + (cls != null ? cls.getName() : null) + "," + '\n' +
234                        "[method] " + (method != null ? method.getName() : null) + "," + '\n' +
235                        "[field] " + (field != null ? field.getName() : null) + " ): Entry");
236    
237                String resourceName = getResourceName(annotation, method, field);
238                String resourceType = getResourceType(annotation, method, field);
239    
240                log.debug("processResource(): resourceName: " + resourceName);
241                log.debug("processResource(): resourceType: " + resourceType);
242    
243                if (resourceType.equals("javax.xml.rpc.Service") ||
244                    resourceType.equals("javax.xml.ws.Service") ||
245                    resourceType.equals("javax.jws.WebService")) {
246    
247                    log.debug("processResource(): <service-ref> found");
248    
249                    boolean exists = false;
250                    ServiceRefType[] serviceRefs = annotatedApp.getServiceRefArray();
251                    for (ServiceRefType serviceRef : serviceRefs) {
252                        if (serviceRef.getServiceRefName().getStringValue().trim().equals(resourceName)) {
253                            if (method != null || field != null) {
254                                InjectionTargetType[] targets = serviceRef.getInjectionTargetArray();
255                                if (!hasTarget(method, field, targets)) {
256                                    configureInjectionTarget(serviceRef.addNewInjectionTarget(), method, field);
257                                }
258                            }
259                            exists = true;
260                            break;
261                        }
262                    }
263                    if (!exists) {
264                        try {
265    
266                            log.debug("processResource(): Does not exist in DD: " + resourceName);
267    
268                            // Doesn't exist in deployment descriptor -- add new
269                            ServiceRefType serviceRef = annotatedApp.addNewServiceRef();
270    
271                            //------------------------------------------------------------------------------
272                            // <service-ref> required elements:
273                            //------------------------------------------------------------------------------
274    
275                            // service-ref-name
276                            JndiNameType serviceRefName = serviceRef.addNewServiceRefName();
277                            serviceRefName.setStringValue(resourceName);
278                            serviceRef.setServiceRefName(serviceRefName);
279    
280                            // service-ref-interface
281                            FullyQualifiedClassType serviceRefInterfaceClass = serviceRef.addNewServiceInterface();
282                            serviceRefInterfaceClass.setStringValue(resourceType);
283                            serviceRef.setServiceInterface(serviceRefInterfaceClass);
284    
285                            //------------------------------------------------------------------------------
286                            // <service-ref> optional elements:
287                            //------------------------------------------------------------------------------
288    
289                            // description
290                            String descriptionAnnotation = annotation.description();
291                            if (!descriptionAnnotation.equals("")) {
292                                DescriptionType description = serviceRef.addNewDescription();
293                                description.setStringValue(descriptionAnnotation);
294                            }
295    
296                            // service-ref-type
297                            if (!serviceRef.isSetServiceRefType()) {
298                                FullyQualifiedClassType serviceRefTypeClass = serviceRef.addNewServiceRefType();
299                                serviceRefTypeClass.setStringValue(resourceType);
300                                serviceRef.setServiceRefType(serviceRefTypeClass);
301                            }
302    
303                            // mappedName
304                            if (!serviceRef.isSetMappedName() && annotation.mappedName().trim().length() > 0) {
305                                XsdStringType mappedName = serviceRef.addNewMappedName();
306                                mappedName.setStringValue(annotation.mappedName().trim());
307                                serviceRef.setMappedName(mappedName);
308                            }
309                        }
310                        catch (Exception anyException) {
311                            log.debug("SwitchServiceRefBuilder: Exception caught while processing <service-ref>");
312                        }
313                    }
314                    return true;
315                }
316                return false;
317            }
318        }
319    
320        public static final GBeanInfo GBEAN_INFO;
321    
322        static {
323            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(
324                    SwitchingServiceRefBuilder.class, NameFactory.MODULE_BUILDER);
325            infoBuilder.addAttribute("eeNamespaces", String[].class, true, true);
326            infoBuilder.addReference("JAXRPCBuilder", ServiceRefBuilder.class,
327                    NameFactory.MODULE_BUILDER);
328            infoBuilder.addReference("JAXWSBuilder", ServiceRefBuilder.class,
329                    NameFactory.MODULE_BUILDER);
330    
331            infoBuilder.setConstructor(new String[]{"eeNamespaces",
332                    "JAXRPCBuilder", "JAXWSBuilder"});
333    
334            GBEAN_INFO = infoBuilder.getBeanInfo();
335        }
336    
337        public static GBeanInfo getGBeanInfo() {
338            return GBEAN_INFO;
339        }
340    }