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 }