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    }