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