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