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    }