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.connector.deployment;
019    
020    import java.lang.reflect.Field;
021    import java.lang.reflect.Method;
022    import java.net.MalformedURLException;
023    import java.net.URI;
024    import java.net.URL;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.HashMap;
028    import java.util.HashSet;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Set;
032    
033    import javax.annotation.Resource;
034    import javax.resource.ResourceException;
035    import javax.xml.namespace.QName;
036    
037    import org.apache.commons.logging.Log;
038    import org.apache.commons.logging.LogFactory;
039    import org.apache.geronimo.common.DeploymentException;
040    import org.apache.geronimo.gbean.AbstractName;
041    import org.apache.geronimo.gbean.AbstractNameQuery;
042    import org.apache.geronimo.gbean.GBeanInfo;
043    import org.apache.geronimo.gbean.GBeanInfoBuilder;
044    import org.apache.geronimo.gbean.SingleElementCollection;
045    import org.apache.geronimo.j2ee.deployment.CorbaGBeanNameSource;
046    import org.apache.geronimo.j2ee.deployment.Module;
047    import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedApp;
048    import org.apache.geronimo.j2ee.deployment.annotation.ResourceAnnotationHelper;
049    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
050    import org.apache.geronimo.kernel.GBeanNotFoundException;
051    import org.apache.geronimo.kernel.repository.Artifact;
052    import org.apache.geronimo.kernel.repository.Dependency;
053    import org.apache.geronimo.kernel.repository.Environment;
054    import org.apache.geronimo.naming.deployment.AbstractNamingBuilder;
055    import org.apache.geronimo.naming.deployment.ResourceEnvironmentBuilder;
056    import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
057    import org.apache.geronimo.naming.reference.ORBReference;
058    import org.apache.geronimo.naming.reference.ResourceReferenceFactory;
059    import org.apache.geronimo.naming.reference.URLReference;
060    import org.apache.geronimo.xbeans.geronimo.naming.GerPatternType;
061    import org.apache.geronimo.xbeans.geronimo.naming.GerResourceRefDocument;
062    import org.apache.geronimo.xbeans.geronimo.naming.GerResourceRefType;
063    import org.apache.geronimo.xbeans.javaee.DescriptionType;
064    import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
065    import org.apache.geronimo.xbeans.javaee.InjectionTargetType;
066    import org.apache.geronimo.xbeans.javaee.JndiNameType;
067    import org.apache.geronimo.xbeans.javaee.ResAuthType;
068    import org.apache.geronimo.xbeans.javaee.ResSharingScopeType;
069    import org.apache.geronimo.xbeans.javaee.ResourceRefType;
070    import org.apache.geronimo.xbeans.javaee.XsdStringType;
071    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
072    import org.apache.xmlbeans.QNameSet;
073    import org.apache.xmlbeans.XmlObject;
074    import org.omg.CORBA.ORB;
075    
076    /**
077     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
078     */
079    public class ResourceRefBuilder extends AbstractNamingBuilder implements ResourceEnvironmentSetter {
080    
081        private static final Log log = LogFactory.getLog(ResourceRefBuilder.class);
082    
083        private static final QName GER_RESOURCE_REF_QNAME = GerResourceRefDocument.type.getDocumentElementName();
084        private static final QNameSet GER_RESOURCE_REF_QNAME_SET = QNameSet.singleton(GER_RESOURCE_REF_QNAME);
085        private static final String JAXR_CONNECTION_FACTORY_CLASS = "javax.xml.registry.ConnectionFactory";
086        private static final String JAVAX_MAIL_SESSION_CLASS = "javax.mail.Session";
087    
088    
089        private final QNameSet resourceRefQNameSet;
090        private final Environment corbaEnvironment;
091        private final SingleElementCollection corbaGBeanNameSourceCollection;
092    
093        public ResourceRefBuilder(Environment defaultEnvironment, Environment corbaEnvironment, String[] eeNamespaces, Collection corbaGBeanNameSourceCollection) {
094            super(defaultEnvironment);
095    
096            resourceRefQNameSet = buildQNameSet(eeNamespaces, "resource-ref");
097            this.corbaEnvironment = corbaEnvironment;
098            this.corbaGBeanNameSourceCollection = new SingleElementCollection(corbaGBeanNameSourceCollection);
099        }
100    
101        protected boolean willMergeEnvironment(XmlObject specDD, XmlObject plan) {
102            return specDD.selectChildren(resourceRefQNameSet).length > 0;
103        }
104    
105        public void buildNaming(XmlObject specDD, XmlObject plan, Module module, Map componentContext) throws DeploymentException {
106    
107            // Discover and process any @Resource annotations (if !metadata-complete)
108            if ((module != null) && (module.getClassFinder() != null)) {
109    
110                // Process all the annotations for this naming builder type
111                try {
112                    ResourceAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder(), ResourceRefProcessor.INSTANCE);
113                }
114                catch (Exception e) {
115                    log.warn("Unable to process @Resource annotations for module" + module.getName(), e);
116                }
117            }
118    
119            List<ResourceRefType> resourceRefsUntyped = convert(specDD.selectChildren(resourceRefQNameSet), J2EE_CONVERTER, ResourceRefType.class, ResourceRefType.type);
120            XmlObject[] gerResourceRefsUntyped = plan == null ? NO_REFS : plan.selectChildren(GER_RESOURCE_REF_QNAME_SET);
121            Map refMap = mapResourceRefs(gerResourceRefsUntyped);
122            List unresolvedRefs = new ArrayList();
123            ClassLoader cl = module.getEarContext().getClassLoader();                
124            for (ResourceRefType resourceRef : resourceRefsUntyped) {
125                String name = resourceRef.getResRefName().getStringValue().trim();
126                addInjections(name, resourceRef.getInjectionTargetArray(), componentContext);
127                String type = resourceRef.getResType().getStringValue().trim();
128                GerResourceRefType gerResourceRef = (GerResourceRefType) refMap.get(name);
129                log.debug("trying to resolve " + name + ", type " + type + ", resourceRef " + gerResourceRef);
130                if(!refMap.containsKey(name)){
131                    unresolvedRefs.add(name);
132                } 
133                Class iface;
134                try {
135                    iface = cl.loadClass(type);
136                } catch (ClassNotFoundException e) {
137                    throw new DeploymentException("could not load class " + type, e);
138                }
139                if (iface == URL.class) {
140                    if (gerResourceRef == null || !gerResourceRef.isSetUrl()) {
141                        throw new DeploymentException("No url supplied to resolve: " + name);
142                    }
143                    String url = gerResourceRef.getUrl().trim();
144                    //TODO expose jsr-77 objects for these guys
145                    try {
146                        //check for malformed URL
147                        new URL(url);
148                    } catch (MalformedURLException e) {
149                        throw new DeploymentException("Could not convert " + url + " to URL", e);
150                    }
151                    getJndiContextMap(componentContext).put(ENV + name, new URLReference(url));
152                    unresolvedRefs.remove(name);
153                } else if (ORB.class.isAssignableFrom(iface)) {
154                    CorbaGBeanNameSource corbaGBeanNameSource = (CorbaGBeanNameSource) corbaGBeanNameSourceCollection.getElement();
155                    if (corbaGBeanNameSource == null) {
156                        throw new DeploymentException("No orb setup but there is a orb reference");
157                    }
158                    AbstractNameQuery corbaName = corbaGBeanNameSource.getCorbaGBeanName();
159                    if (corbaName != null) {
160                        Artifact[] moduleId = module.getConfigId();
161                        Map context = getJndiContextMap(componentContext);
162                        context.put(ENV + name, new ORBReference(moduleId, corbaName));
163                        unresolvedRefs.remove(name);
164                        EnvironmentBuilder.mergeEnvironments(module.getEnvironment(), corbaEnvironment);
165                    }
166                } else {
167                    //determine jsr-77 type from interface
168                    String j2eeType;
169    
170    
171                    if (JAVAX_MAIL_SESSION_CLASS.equals(type)) {
172                        j2eeType = NameFactory.JAVA_MAIL_RESOURCE;
173                    } else if (JAXR_CONNECTION_FACTORY_CLASS.equals(type)) {
174                        j2eeType = NameFactory.JAXR_CONNECTION_FACTORY;
175                    } else {
176                        j2eeType = NameFactory.JCA_MANAGED_CONNECTION_FACTORY;
177                    }
178                    try {
179                        AbstractNameQuery containerId = getResourceContainerId(name, j2eeType, null, gerResourceRef);
180    
181                        module.getEarContext().findGBean(containerId);
182    
183                        Object ref = new ResourceReferenceFactory<ResourceException>(module.getConfigId(), containerId, iface);
184                        getJndiContextMap(componentContext).put(ENV + name, ref);
185                        // we thought that this might be an unresolved
186                        // name because it wasn't in the refMap, but now
187                        // we've found it so we can take it out of the
188                        // unresolvedRefs list
189                        unresolvedRefs.remove(name);
190                    } catch (GBeanNotFoundException e) {
191    
192                        StringBuffer errorMessage = new StringBuffer("Unable to resolve resource reference '");
193                        errorMessage.append(name);
194                        errorMessage.append("' (");
195                        if (e.hasMatches()) {
196                            errorMessage.append("Found multiple matching resources.  Try being more specific in a resource-ref mapping in your Geronimo deployment plan.\n");
197                            for (AbstractName match : e.getMatches()) {
198                                errorMessage.append(match).append("\n");
199                            }
200                        } else if (gerResourceRef == null) {
201                            errorMessage.append("Could not auto-map to resource.  Try adding a resource-ref mapping to your Geronimo deployment plan.");
202                        } else if (gerResourceRef.isSetResourceLink()) {
203                            errorMessage.append("Could not find resource '");
204                            errorMessage.append(gerResourceRef.getResourceLink());
205                            errorMessage.append("'.  Perhaps it has not yet been configured, or your application does not have a dependency declared for that resource module?");
206                        } else {
207                            errorMessage.append("Could not find the resource specified in your Geronimo deployment plan:");
208                            errorMessage.append(gerResourceRef.getPattern());
209                        }
210                        errorMessage.append("\nSearch conducted in current module and dependencies:\n");
211                        for (Dependency dependency : module.getEnvironment().getDependencies()) {
212                            errorMessage.append(dependency).append("\n");
213                        }
214                        errorMessage.append(")");
215    
216                        throw new DeploymentException(errorMessage.toString());
217                    }
218                }
219            }
220    
221            if (unresolvedRefs.size() > 0) {
222                log.warn("Failed to build reference to resource reference "+ unresolvedRefs +" defined in plan file, reason - corresponding entry in deployment descriptor missing.");
223            }
224        }
225    
226        public void setResourceEnvironment(ResourceEnvironmentBuilder builder, XmlObject[] resourceRefs, GerResourceRefType[] gerResourceRefs) throws DeploymentException {
227            List<ResourceRefType> resourceRefList = convert(resourceRefs, J2EE_CONVERTER, ResourceRefType.class, ResourceRefType.type);
228            Map refMap = mapResourceRefs(gerResourceRefs);
229            Set unshareableResources = new HashSet();
230            Set applicationManagedSecurityResources = new HashSet();
231            for (ResourceRefType resourceRefType : resourceRefList) {
232    
233                String type = resourceRefType.getResType().getStringValue().trim();
234    
235                if (!URL.class.getName().equals(type)
236                        && !"javax.mail.Session".equals(type)
237                        && !JAXR_CONNECTION_FACTORY_CLASS.equals(type)) {
238    
239                    GerResourceRefType gerResourceRef = (GerResourceRefType) refMap.get(resourceRefType.getResRefName().getStringValue());
240                    AbstractNameQuery containerId = getResourceContainerId(getStringValue(resourceRefType.getResRefName()), NameFactory.JCA_MANAGED_CONNECTION_FACTORY, null, gerResourceRef);
241    
242                    if ("Unshareable".equals(getStringValue(resourceRefType.getResSharingScope()))) {
243                        unshareableResources.add(containerId);
244                    }
245                    if ("Application".equals(getStringValue(resourceRefType.getResAuth()))) {
246                        applicationManagedSecurityResources.add(containerId);
247                    }
248                }
249            }
250            builder.setUnshareableResources(unshareableResources);
251            builder.setApplicationManagedSecurityResources(applicationManagedSecurityResources);
252        }
253    
254        private Map<String, GerResourceRefType> mapResourceRefs(XmlObject[] refs) {
255            Map<String, GerResourceRefType> refMap = new HashMap<String, GerResourceRefType>();
256            if (refs != null) {
257                for (XmlObject ref1 : refs) {
258                    GerResourceRefType ref = (GerResourceRefType) ref1.copy().changeType(GerResourceRefType.type);
259                    refMap.put(ref.getRefName().trim(), ref);
260                }
261            }
262            return refMap;
263        }
264    
265        private AbstractNameQuery getResourceContainerId(String name, String type, URI moduleURI, GerResourceRefType gerResourceRef) {
266            AbstractNameQuery containerId;
267            String module = moduleURI == null ? null : moduleURI.toString();
268            if (gerResourceRef == null) {
269                containerId = buildAbstractNameQuery(null, module, name, type, NameFactory.RESOURCE_ADAPTER_MODULE);
270            } else if (gerResourceRef.isSetResourceLink()) {
271                containerId = buildAbstractNameQuery(null, module, gerResourceRef.getResourceLink().trim(), type, NameFactory.RESOURCE_ADAPTER_MODULE);
272            } else {
273                //construct name from components
274                GerPatternType patternType = gerResourceRef.getPattern();
275                containerId = buildAbstractNameQuery(patternType, type, NameFactory.RESOURCE_ADAPTER_MODULE, null);
276            }
277            return containerId;
278        }
279    
280    
281        public QNameSet getSpecQNameSet() {
282            return resourceRefQNameSet;
283        }
284    
285        public QNameSet getPlanQNameSet() {
286            return GER_RESOURCE_REF_QNAME_SET;
287        }
288    
289        public static class ResourceRefProcessor extends ResourceAnnotationHelper.ResourceProcessor {
290    
291            public static final ResourceRefProcessor INSTANCE = new ResourceRefProcessor();
292    
293            private ResourceRefProcessor() {
294            }
295    
296            public boolean processResource(AnnotatedApp annotatedApp, Resource annotation, Class cls, Method method, Field field) {
297                log.debug("processResource( [annotatedApp] " + annotatedApp.toString() + "," + '\n' +
298                        "[annotation] " + annotation.toString() + "," + '\n' +
299                        "[cls] " + (cls != null ? cls.getName() : null) + "," + '\n' +
300                        "[method] " + (method != null ? method.getName() : null) + "," + '\n' +
301                        "[field] " + (field != null ? field.getName() : null) + " ): Entry");
302    
303                String resourceName = getResourceName(annotation, method, field);
304                String resourceType = getResourceType(annotation, method, field);
305    
306                if (resourceType.equals("javax.sql.DataSource") ||
307                        resourceType.equals("javax.mail.Session") ||
308                        resourceType.equals("java.net.URL") ||
309                        resourceType.equals("org.omg.CORBA.ORB") ||
310                        resourceType.equals("org.omg.CORBA_2_3.ORB") ||
311                        resourceType.equals("org.omg.CORBA_2_4.ORB") ||
312                        resourceType.endsWith("ConnectionFactory")) {
313    
314                    log.debug("processResource(): <resource-ref> found");
315    
316                    boolean exists = false;
317                    ResourceRefType[] resourceRefs = annotatedApp.getResourceRefArray();
318                    for (ResourceRefType resourceRef : resourceRefs) {
319                        if (resourceRef.getResRefName().getStringValue().trim().equals(resourceName)) {
320                            if (method != null || field != null) {
321                                InjectionTargetType[] targets = resourceRef.getInjectionTargetArray();
322                                if (!hasTarget(method, field, targets)) {
323                                    configureInjectionTarget(resourceRef.addNewInjectionTarget(), method, field);
324                                }
325                            }
326                            exists = true;
327                            break;
328                        }
329                    }
330                    if (!exists) {
331                        try {
332    
333                            log.debug("processResource(): Does not exist in DD: " + resourceName);
334    
335                            // Doesn't exist in deployment descriptor -- add new
336                            ResourceRefType resourceRef = annotatedApp.addNewResourceRef();
337    
338                            //------------------------------------------------------------------------------
339                            // <resource-ref> required elements:
340                            //------------------------------------------------------------------------------
341    
342                            // resource-ref-name
343                            JndiNameType resourceRefName = resourceRef.addNewResRefName();
344                            resourceRefName.setStringValue(resourceName);
345    
346                            if (!resourceType.equals("")) {
347                                // resource-ref-type
348                                FullyQualifiedClassType qualifiedClass = resourceRef.addNewResType();
349                                qualifiedClass.setStringValue(resourceType);
350                            }
351                            if (method != null || field != null) {
352                                // injectionTarget
353                                InjectionTargetType injectionTarget = resourceRef.addNewInjectionTarget();
354                                configureInjectionTarget(injectionTarget, method, field);
355                            }
356    
357                            //------------------------------------------------------------------------------
358                            // <resource-ref> optional elements:
359                            //------------------------------------------------------------------------------
360    
361                            // description
362                            String descriptionAnnotation = annotation.description();
363                            if (!descriptionAnnotation.equals("")) {
364                                DescriptionType description = resourceRef.addNewDescription();
365                                description.setStringValue(descriptionAnnotation);
366                            }
367    
368                            // authentication
369                            if (annotation.authenticationType() == Resource.AuthenticationType.CONTAINER) {
370                                ResAuthType resAuth = resourceRef.addNewResAuth();
371                                resAuth.setStringValue("Container");
372                                resourceRef.setResAuth(resAuth);
373                            } else if (annotation.authenticationType() == Resource.AuthenticationType.APPLICATION) {
374                                ResAuthType resAuth = resourceRef.addNewResAuth();
375                                resAuth.setStringValue("Application");
376                                resourceRef.setResAuth(resAuth);
377                            }
378    
379                            // sharing scope
380                            ResSharingScopeType resScope = resourceRef.addNewResSharingScope();
381                            resScope.setStringValue(annotation.shareable() ? "Shareable" : "Unshareable");
382                            resourceRef.setResSharingScope(resScope);
383    
384                            // mappedName
385                            String mappdedNameAnnotation = annotation.mappedName();
386                            if (!mappdedNameAnnotation.equals("")) {
387                                XsdStringType mappedName = resourceRef.addNewMappedName();
388                                mappedName.setStringValue(mappdedNameAnnotation);
389                                resourceRef.setMappedName(mappedName);
390                            }
391    
392                        }
393                        catch (Exception anyException) {
394                            log.debug("ResourceRefBuilder: Exception caught while processing <resource-ref>");
395                        }
396                    }
397                    return true;
398                }
399                return false;
400            }
401        }
402    
403    
404        public static final GBeanInfo GBEAN_INFO;
405    
406        static {
407            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(ResourceRefBuilder.class, NameFactory.MODULE_BUILDER);
408            infoBuilder.addAttribute("eeNamespaces", String[].class, true, true);
409            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
410            infoBuilder.addAttribute("corbaEnvironment", Environment.class, true, true);
411            infoBuilder.addReference("CorbaGBeanNameSource", CorbaGBeanNameSource.class);
412    
413            infoBuilder.setConstructor(new String[]{"defaultEnvironment", "corbaEnvironment", "eeNamespaces", "CorbaGBeanNameSource"});
414    
415            GBEAN_INFO = infoBuilder.getBeanInfo();
416        }
417    
418        public static GBeanInfo getGBeanInfo() {
419            return GBEAN_INFO;
420        }
421    }