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.openejb.deployment;
019
020 import java.net.URI;
021 import java.net.URISyntaxException;
022 import java.util.Collection;
023 import java.util.Collections;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.HashMap;
027 import java.util.Set;
028 import java.util.TreeSet;
029 import java.util.Arrays;
030 import java.util.ArrayList;
031
032 import org.apache.commons.logging.Log;
033 import org.apache.commons.logging.LogFactory;
034 import org.apache.geronimo.common.DeploymentException;
035 import org.apache.geronimo.gbean.GBeanInfo;
036 import org.apache.geronimo.gbean.GBeanInfoBuilder;
037 import org.apache.geronimo.j2ee.deployment.Module;
038 import org.apache.geronimo.j2ee.deployment.annotation.EJBAnnotationHelper;
039 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
040 import org.apache.geronimo.kernel.config.ConfigurationModuleType;
041 import org.apache.geronimo.kernel.repository.Environment;
042 import org.apache.geronimo.naming.deployment.AbstractNamingBuilder;
043 import org.apache.geronimo.openejb.ClientEjbReference;
044 import org.apache.geronimo.xbeans.javaee.EjbLocalRefType;
045 import org.apache.geronimo.xbeans.javaee.EjbRefType;
046 import org.apache.geronimo.xbeans.javaee.InjectionTargetType;
047 import org.apache.geronimo.xbeans.geronimo.naming.GerEjbRefType;
048 import org.apache.geronimo.xbeans.geronimo.naming.GerEjbRefDocument;
049 import org.apache.geronimo.xbeans.geronimo.naming.GerPatternType;
050 import org.apache.geronimo.xbeans.geronimo.naming.GerEjbLocalRefDocument;
051 import org.apache.geronimo.xbeans.geronimo.naming.GerEjbLocalRefType;
052 import org.apache.openejb.OpenEJBException;
053 import org.apache.openejb.assembler.classic.EjbJarInfo;
054 import org.apache.openejb.assembler.classic.JndiEncBuilder;
055 import org.apache.openejb.assembler.classic.JndiEncInfo;
056 import org.apache.openejb.assembler.classic.AppInfo;
057 import org.apache.openejb.config.JndiEncInfoBuilder;
058 import org.apache.openejb.config.AppModule;
059 import org.apache.openejb.core.ivm.naming.IntraVmJndiReference;
060 import org.apache.openejb.jee.EjbLocalRef;
061 import org.apache.openejb.jee.EjbRef;
062 import org.apache.openejb.jee.InjectionTarget;
063 import org.apache.openejb.jee.JndiConsumer;
064 import org.apache.openejb.jee.SessionBean;
065 import org.apache.xmlbeans.QNameSet;
066 import org.apache.xmlbeans.XmlObject;
067
068 /**
069 * @version $Revision: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
070 */
071 public class EjbRefBuilder extends AbstractNamingBuilder {
072 private static final Log log = LogFactory.getLog(EjbRefBuilder.class);
073
074 private final QNameSet ejbRefQNameSet;
075 private final QNameSet ejbLocalRefQNameSet;
076 private final URI uri;
077
078 public EjbRefBuilder(Environment defaultEnvironment, String[] eeNamespaces, String host, int port) throws URISyntaxException {
079 super(defaultEnvironment);
080 if (host != null) {
081 uri = new URI("ejb", null, host, port, null, null, null);
082 } else {
083 uri = null;
084 }
085
086 ejbRefQNameSet = buildQNameSet(eeNamespaces, "ejb-ref");
087 ejbLocalRefQNameSet = buildQNameSet(eeNamespaces, "ejb-local-ref");
088 ejbRefQNameSet.union(ejbLocalRefQNameSet);
089 }
090
091 public QNameSet getSpecQNameSet() {
092 return ejbRefQNameSet;
093 }
094
095 public QNameSet getPlanQNameSet() {
096 return QNameSet.EMPTY;
097 }
098
099 protected boolean willMergeEnvironment(XmlObject specDD, XmlObject plan) {
100 return specDD.selectChildren(ejbRefQNameSet).length > 0 || specDD.selectChildren(ejbLocalRefQNameSet).length > 0;
101 }
102
103 public void buildNaming(XmlObject specDD, XmlObject plan, Module module, Map componentContext) throws DeploymentException {
104 // skip ejb modules... they have alreayd been processed
105 if (module.getType() == ConfigurationModuleType.EJB) {
106 return;
107 }
108
109 // map the refs declared in the vendor plan, so we can match them to the spec references
110 Map<String, GerEjbRefType> refMap = mapEjbRefs(plan);
111 Map<String, GerEjbLocalRefType> localRefMap = mapEjbLocalRefs(plan);
112
113 // JndiConsumer holds the ref objects that OpenEJB needs
114 JndiConsumer consumer = new SessionBean();
115
116 // Add the refs declaraed the the spec deployment descriptor (e.g., ejb-jar.xml or web.xml)
117 List<EjbRefType> ejbRefs = convert(specDD.selectChildren(ejbRefQNameSet), JEE_CONVERTER, EjbRefType.class, EjbRefType.type);
118 List<EjbLocalRefType> ejbLocalRefs = convert(specDD.selectChildren(ejbLocalRefQNameSet), JEE_CONVERTER, EjbLocalRefType.class, EjbLocalRefType.type);
119 addRefs(consumer, ejbRefs, refMap, ejbLocalRefs, localRefMap, componentContext);
120
121 // Discover and process any @EJB annotations (if !metadata-complete)
122 if ((module != null) && (module.getClassFinder() != null)) {
123 processAnnotations(module);
124
125 // @EJB remote refs
126 ejbRefs = asList(module.getAnnotatedApp().getEjbRefArray());
127
128 // @EJB local refs
129 ejbLocalRefs = asList(module.getAnnotatedApp().getEjbLocalRefArray());
130
131 // @EJB ambiguous refs
132 ejbRefs.addAll(module.getAnnotatedApp().getAmbiguousEjbRefs());
133
134 // add the refs
135 addRefs(consumer, ejbRefs, refMap, ejbLocalRefs, localRefMap, componentContext);
136 }
137
138 Map<String, Object> map = null;
139 try {
140 EjbModuleBuilder.EarData earData = (EjbModuleBuilder.EarData) module.getRootEarContext().getGeneralData().get(EjbModuleBuilder.EarData.class);
141 Collection<EjbJarInfo> ejbJars = Collections.emptySet();
142 if (earData != null) {
143 ejbJars = earData.getEjbJars();
144 }
145
146
147 AppInfo appInfo = new AppInfo();
148 appInfo.ejbJars.addAll(ejbJars);
149
150 JndiEncInfoBuilder jndiEncInfoBuilder = new JndiEncInfoBuilder(appInfo);
151 JndiEncInfo jndiEncInfo;
152 if (module.isStandAlone()) {
153 jndiEncInfo = jndiEncInfoBuilder.build(consumer, "GeronimoEnc", null);
154 } else {
155 jndiEncInfo = jndiEncInfoBuilder.build(consumer, "GeronimoEnc", module.getTargetPath());
156 }
157 JndiEncBuilder jndiEncBuilder = new JndiEncBuilder(jndiEncInfo, null, module.getName(), getClass().getClassLoader());
158
159 map = jndiEncBuilder.buildMap();
160 } catch (OpenEJBException e) {
161 throw new DeploymentException(e);
162 }
163
164 for (Map.Entry<String, Object> entry : map.entrySet()) {
165 String name = entry.getKey();
166 Object value = entry.getValue();
167
168 // work with names prefixed with java:comp/
169 if (name.startsWith("java:comp/")) {
170 name = name.substring("java:comp/".length());
171 }
172
173 // if this is a ref it will be prefixed with env/
174 if (name.startsWith("env/")) {
175 if (uri != null) {
176 value = createClientRef(value);
177 }
178 getJndiContextMap(componentContext).put(name, value);
179 }
180 }
181 }
182
183 private Object createClientRef(Object value) {
184 if (value instanceof IntraVmJndiReference) {
185 IntraVmJndiReference intraVmJndiReference = (IntraVmJndiReference) value;
186 String deploymentId = intraVmJndiReference.getJndiName();
187 if (deploymentId.startsWith("java:openejb/ejb/")) {
188 deploymentId = deploymentId.substring("java:openejb/ejb/".length());
189 }
190 if (deploymentId.startsWith("java:openejb/Deployment/")) {
191 deploymentId = deploymentId.substring("java:openejb/Deployment/".length());
192 }
193 ClientEjbReference clientRef = new ClientEjbReference(uri.toString(), deploymentId);
194 return clientRef;
195 }
196 return value;
197 }
198
199 private void addRefs(JndiConsumer jndiConsumer, List<EjbRefType> ejbRefs, Map<String, GerEjbRefType> refMap, List<EjbLocalRefType> ejbLocalRefs, Map<String, GerEjbLocalRefType> localRefMap, Map componentContext) {
200 Set<String> declaredEjbRefs = new TreeSet<String>();
201 for (EjbRef ejbRef : jndiConsumer.getEjbRef()) {
202 declaredEjbRefs.add(ejbRef.getName());
203 }
204 for (EjbRefType xmlbeansRef : ejbRefs) {
205 // skip refs that have already been declared
206 String refName = getStringValue(xmlbeansRef.getEjbRefName());
207 if (declaredEjbRefs.contains(refName)) {
208 continue;
209 }
210
211 // skip corba refs
212 GerEjbRefType ejbRefType = refMap.get(refName);
213 if (ejbRefType != null) {
214 if (ejbRefType.getNsCorbaloc() != null) {
215 continue;
216 }
217 }
218 // create the ejb-ref
219 EjbRef ref = new EjbRef();
220 // ejb-ref-name
221 ref.setEjbRefName(refName);
222
223 jndiConsumer.getEjbRef().add(ref);
224
225 // ejb-ref-type
226 String refType = getStringValue(xmlbeansRef.getEjbRefType());
227 if ("SESSION".equalsIgnoreCase(refType)) {
228 ref.setEjbRefType(org.apache.openejb.jee.EjbRefType.SESSION);
229 } else if ("ENTITY".equalsIgnoreCase(refType)) {
230 ref.setEjbRefType(org.apache.openejb.jee.EjbRefType.ENTITY);
231 } else {
232 ref.setRefType(EjbRef.Type.UNKNOWN);
233 }
234
235 // home
236 ref.setHome(getStringValue(xmlbeansRef.getHome()));
237
238 // remote
239 ref.setRemote(getStringValue(xmlbeansRef.getRemote()));
240
241 // ejb-link
242 ref.setEjbLink(getStringValue(xmlbeansRef.getEjbLink()));
243
244 // mapped-name
245 ref.setMappedName(getStringValue(xmlbeansRef.getMappedName()));
246
247 // handle external refs
248 if (ejbRefType != null) {
249 if (ejbRefType.getNsCorbaloc() != null) {
250 // corba refs are simple delegated back to Geronimo
251 ref.setMappedName("jndi:java:comp/geronimo/env/" + ref.getEjbRefName());
252 } else if (ejbRefType.getPattern() != null) {
253 // external ear ref
254 // set mapped name to the deploymentId of the external ref
255 GerPatternType pattern = ejbRefType.getPattern();
256 String module = pattern.getModule();
257 if (module == null) {
258 module = pattern.getArtifactId();
259 }
260 String ejbName = pattern.getName();
261 String deploymentId = module.trim() + "/" + ejbName;
262 ref.setMappedName(deploymentId.trim());
263 }
264 }
265
266 // openejb handling of injection-targets
267 if (xmlbeansRef.getInjectionTargetArray() != null) {
268 for (InjectionTargetType injectionTargetType : xmlbeansRef.getInjectionTargetArray()) {
269 InjectionTarget injectionTarget = new InjectionTarget();
270 injectionTarget.setInjectionTargetClass(getStringValue(injectionTargetType.getInjectionTargetClass()));
271 injectionTarget.setInjectionTargetName(getStringValue(injectionTargetType.getInjectionTargetName()));
272 ref.getInjectionTarget().add(injectionTarget);
273 }
274 }
275 //geronimo's handling of injection-target
276 addInjections(refName, xmlbeansRef.getInjectionTargetArray(), componentContext);
277
278 }
279
280 Set<String> declaredEjbLocalRefs = new TreeSet<String>();
281 for (EjbLocalRef ejbLocalRef : jndiConsumer.getEjbLocalRef()) {
282 declaredEjbLocalRefs.add(ejbLocalRef.getName());
283 }
284
285 for (EjbLocalRefType xmlbeansRef : ejbLocalRefs) {
286 // skip refs that have already been declared
287 String refName = getStringValue(xmlbeansRef.getEjbRefName());
288 if (declaredEjbLocalRefs.contains(refName)) {
289 continue;
290 }
291
292 // create the ejb-ref
293 EjbLocalRef ref = new EjbLocalRef();
294 // ejb-ref-name
295 ref.setEjbRefName(refName);
296
297 jndiConsumer.getEjbLocalRef().add(ref);
298
299 // ejb-ref-type
300 String refType = getStringValue(xmlbeansRef.getEjbRefType());
301 if ("SESSION".equalsIgnoreCase(refType)) {
302 ref.setEjbRefType(org.apache.openejb.jee.EjbRefType.SESSION);
303 } else if ("ENTITY".equalsIgnoreCase(refType)) {
304 ref.setEjbRefType(org.apache.openejb.jee.EjbRefType.ENTITY);
305 }
306
307 // home
308 ref.setLocalHome(getStringValue(xmlbeansRef.getLocalHome()));
309
310 // remote
311 ref.setLocal(getStringValue(xmlbeansRef.getLocal()));
312
313 // ejb-link
314 ref.setEjbLink(getStringValue(xmlbeansRef.getEjbLink()));
315
316 // mapped-name
317 ref.setMappedName(getStringValue(xmlbeansRef.getMappedName()));
318
319 // handle external refs
320 GerEjbLocalRefType ejbLocalRefType = localRefMap.get(ref.getEjbRefName());
321 if (ejbLocalRefType != null && ejbLocalRefType.getPattern() != null) {
322 // external ear ref
323 // set mapped name to the deploymentId of the external ref
324 GerPatternType pattern = ejbLocalRefType.getPattern();
325 String module = pattern.getModule();
326 if (module == null) {
327 module = pattern.getArtifactId();
328 }
329 String ejbName = pattern.getName();
330 String deploymentId = module.trim() + "/" + ejbName;
331 ref.setMappedName(deploymentId.trim());
332 }
333
334 // openejb handling of injection-targets
335 if (xmlbeansRef.getInjectionTargetArray() != null) {
336 for (InjectionTargetType injectionTargetType : xmlbeansRef.getInjectionTargetArray()) {
337 InjectionTarget injectionTarget = new InjectionTarget();
338 injectionTarget.setInjectionTargetClass(getStringValue(injectionTargetType.getInjectionTargetClass()));
339 injectionTarget.setInjectionTargetName(getStringValue(injectionTargetType.getInjectionTargetName()));
340 ref.getInjectionTarget().add(injectionTarget);
341 }
342 }
343 //geronimo's handling of injection-target
344 addInjections(refName, xmlbeansRef.getInjectionTargetArray(), componentContext);
345 }
346 }
347
348 private Map<String, GerEjbRefType> mapEjbRefs(XmlObject plan) {
349 Map<String, GerEjbRefType> refMap = new HashMap<String, GerEjbRefType>();
350
351 if (plan == null) {
352 return refMap;
353 }
354
355 QNameSet qnameSet = QNameSet.singleton(GerEjbRefDocument.type.getDocumentElementName());
356 XmlObject[] xmlObjects = plan.selectChildren(qnameSet);
357 if (xmlObjects != null) {
358 for (XmlObject xmlObject : xmlObjects) {
359 GerEjbRefType ref = (GerEjbRefType) xmlObject.copy().changeType(GerEjbRefType.type);
360 refMap.put(ref.getRefName().trim(), ref);
361 }
362 }
363 return refMap;
364 }
365
366 private Map<String, GerEjbLocalRefType> mapEjbLocalRefs(XmlObject plan) {
367 Map<String, GerEjbLocalRefType> refMap = new HashMap<String, GerEjbLocalRefType>();
368
369 if (plan == null) {
370 return refMap;
371 }
372
373 QNameSet qnameSet = QNameSet.singleton(GerEjbLocalRefDocument.type.getDocumentElementName());
374 XmlObject[] xmlObjects = plan.selectChildren(qnameSet);
375 if (xmlObjects != null) {
376 for (XmlObject xmlObject : xmlObjects) {
377 GerEjbLocalRefType ref = (GerEjbLocalRefType) xmlObject.copy().changeType(GerEjbLocalRefType.type);
378 refMap.put(ref.getRefName().trim(), ref);
379 }
380 }
381 return refMap;
382 }
383
384
385 // XMLBean uses lame arrays that can be null, so we need an asList that handles nulls
386 // Beware Arrays.asList(), it returns an ArrayList lookalike, that is not fully mutable...
387 public static <E> List<E> asList(E[] array) {
388 if (array == null) {
389 return new ArrayList<E>();
390 } else {
391 return new ArrayList<E>(Arrays.asList(array));
392 }
393 }
394
395 private void processAnnotations(Module module) {
396 // Process all the annotations for this naming builder type
397 if (EJBAnnotationHelper.annotationsPresent(module.getClassFinder())) {
398 try {
399 EJBAnnotationHelper.processAnnotations(module.getAnnotatedApp(), module.getClassFinder());
400 } catch (Exception e) {
401 log.warn("Unable to process @EJB annotations for module" + module.getName(), e);
402 }
403 }
404 }
405
406 public static final GBeanInfo GBEAN_INFO;
407
408 static {
409 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(EjbRefBuilder.class, NameFactory.MODULE_BUILDER);
410
411 infoBuilder.addAttribute("eeNamespaces", String[].class, true, true);
412 infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
413 infoBuilder.addAttribute("host", String.class, true);
414 infoBuilder.addAttribute("port", int.class, true);
415
416 infoBuilder.setConstructor(new String[]{"defaultEnvironment", "eeNamespaces", "host", "port"});
417
418 GBEAN_INFO = infoBuilder.getBeanInfo();
419 }
420
421 public static GBeanInfo getGBeanInfo() {
422 return GBEAN_INFO;
423 }
424
425 }