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 }