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                HandlerChainAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder());
209                ResourceAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder(), ServiceRefProcessor.INSTANCE);
210            }
211            catch (Exception e) {
212                log.warn("Unable to process @Resource annotations for module" + module.getName(), e);
213            }
214        }
215    
216        public QNameSet getSpecQNameSet() {
217            return serviceRefQNameSet;
218        }
219    
220        public QNameSet getPlanQNameSet() {
221            return GER_SERVICE_REF_QNAME_SET;
222        }
223    
224        public static class ServiceRefProcessor extends ResourceAnnotationHelper.ResourceProcessor {
225    
226            public static final ServiceRefProcessor INSTANCE = new ServiceRefProcessor();
227    
228            private ServiceRefProcessor() {
229            }
230    
231            public boolean processResource(AnnotatedApp annotatedApp, Resource annotation, Class cls, Method method, Field field) {
232                log.debug("processResource( [annotatedApp] " + annotatedApp.toString() + "," + '\n' +
233                        "[annotation] " + annotation.toString() + "," + '\n' +
234                        "[cls] " + (cls != null ? cls.getName() : null) + "," + '\n' +
235                        "[method] " + (method != null ? method.getName() : null) + "," + '\n' +
236                        "[field] " + (field != null ? field.getName() : null) + " ): Entry");
237    
238                String resourceName = getResourceName(annotation, method, field);
239                String resourceType = getResourceType(annotation, method, field);
240    
241                log.debug("processResource(): resourceName: " + resourceName);
242                log.debug("processResource(): resourceType: " + resourceType);
243    
244                if (resourceType.equals("javax.xml.rpc.Service") ||
245                    resourceType.equals("javax.xml.ws.Service") ||
246                    resourceType.equals("javax.jws.WebService")) {
247    
248                    log.debug("processResource(): <service-ref> found");
249    
250                    boolean exists = false;
251                    ServiceRefType[] serviceRefs = annotatedApp.getServiceRefArray();
252                    for (ServiceRefType serviceRef : serviceRefs) {
253                        if (serviceRef.getServiceRefName().getStringValue().trim().equals(resourceName)) {
254                            if (method != null || field != null) {
255                                InjectionTargetType[] targets = serviceRef.getInjectionTargetArray();
256                                if (!hasTarget(method, field, targets)) {
257                                    configureInjectionTarget(serviceRef.addNewInjectionTarget(), method, field);
258                                }
259                            }
260                            exists = true;
261                            break;
262                        }
263                    }
264                    if (!exists) {
265                        try {
266    
267                            log.debug("processResource(): Does not exist in DD: " + resourceName);
268    
269                            // Doesn't exist in deployment descriptor -- add new
270                            ServiceRefType serviceRef = annotatedApp.addNewServiceRef();
271    
272                            //------------------------------------------------------------------------------
273                            // <service-ref> required elements:
274                            //------------------------------------------------------------------------------
275    
276                            // service-ref-name
277                            JndiNameType serviceRefName = serviceRef.addNewServiceRefName();
278                            serviceRefName.setStringValue(resourceName);
279                            serviceRef.setServiceRefName(serviceRefName);
280    
281                            // service-ref-interface
282                            FullyQualifiedClassType serviceRefInterfaceClass = serviceRef.addNewServiceInterface();
283                            serviceRefInterfaceClass.setStringValue(resourceType);
284                            serviceRef.setServiceInterface(serviceRefInterfaceClass);
285    
286                            //------------------------------------------------------------------------------
287                            // <service-ref> optional elements:
288                            //------------------------------------------------------------------------------
289    
290                            // description
291                            String descriptionAnnotation = annotation.description();
292                            if (!descriptionAnnotation.equals("")) {
293                                DescriptionType description = serviceRef.addNewDescription();
294                                description.setStringValue(descriptionAnnotation);
295                            }
296    
297                            // service-ref-type
298                            if (!serviceRef.isSetServiceRefType()) {
299                                FullyQualifiedClassType serviceRefTypeClass = serviceRef.addNewServiceRefType();
300                                serviceRefTypeClass.setStringValue(resourceType);
301                                serviceRef.setServiceRefType(serviceRefTypeClass);
302                            }
303    
304                            // mappedName
305                            if (!serviceRef.isSetMappedName() && annotation.mappedName().trim().length() > 0) {
306                                XsdStringType mappedName = serviceRef.addNewMappedName();
307                                mappedName.setStringValue(annotation.mappedName().trim());
308                                serviceRef.setMappedName(mappedName);
309                            }
310                        }
311                        catch (Exception anyException) {
312                            log.debug("SwitchServiceRefBuilder: Exception caught while processing <service-ref>");
313                        }
314                    }
315                    return true;
316                }
317                return false;
318            }
319        }
320    
321        public static final GBeanInfo GBEAN_INFO;
322    
323        static {
324            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(
325                    SwitchingServiceRefBuilder.class, NameFactory.MODULE_BUILDER);
326            infoBuilder.addAttribute("eeNamespaces", String[].class, true, true);
327            infoBuilder.addReference("JAXRPCBuilder", ServiceRefBuilder.class,
328                    NameFactory.MODULE_BUILDER);
329            infoBuilder.addReference("JAXWSBuilder", ServiceRefBuilder.class,
330                    NameFactory.MODULE_BUILDER);
331    
332            infoBuilder.setConstructor(new String[]{"eeNamespaces",
333                    "JAXRPCBuilder", "JAXWSBuilder"});
334    
335            GBEAN_INFO = infoBuilder.getBeanInfo();
336        }
337    
338        public static GBeanInfo getGBeanInfo() {
339            return GBEAN_INFO;
340        }
341    }