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 }