001    /**
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.openejb.deployment;
019    
020    import java.security.PermissionCollection;
021    import java.security.Permissions;
022    import java.util.ArrayList;
023    import java.util.Collections;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.TreeMap;
028    
029    import org.apache.geronimo.common.DeploymentException;
030    import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator;
031    import org.apache.geronimo.gbean.AbstractName;
032    import org.apache.geronimo.gbean.AbstractNameQuery;
033    import org.apache.geronimo.gbean.GBeanData;
034    import org.apache.geronimo.j2ee.deployment.EARContext;
035    import org.apache.geronimo.j2ee.deployment.NamingBuilder;
036    import org.apache.geronimo.j2ee.deployment.annotation.AnnotatedEjbJar;
037    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
038    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
039    import org.apache.geronimo.naming.deployment.AbstractNamingBuilder;
040    import org.apache.geronimo.naming.deployment.GBeanResourceEnvironmentBuilder;
041    import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter;
042    import org.apache.geronimo.openejb.EntityDeploymentGBean;
043    import org.apache.geronimo.openejb.MessageDrivenDeploymentGBean;
044    import org.apache.geronimo.openejb.OpenEjbSystem;
045    import org.apache.geronimo.openejb.StatefulDeploymentGBean;
046    import org.apache.geronimo.openejb.StatelessDeploymentGBean;
047    import org.apache.geronimo.openejb.xbeans.ejbjar.OpenejbGeronimoEjbJarType;
048    import org.apache.geronimo.security.deployment.SecurityConfiguration;
049    import org.apache.geronimo.security.jacc.ComponentPermissions;
050    import org.apache.geronimo.xbeans.geronimo.naming.GerResourceRefType;
051    import org.apache.geronimo.xbeans.javaee.EjbJarType;
052    import org.apache.geronimo.xbeans.javaee.EnterpriseBeansType;
053    import org.apache.geronimo.xbeans.javaee.EntityBeanType;
054    import org.apache.geronimo.xbeans.javaee.MessageDrivenBeanType;
055    import org.apache.geronimo.xbeans.javaee.ResourceRefType;
056    import org.apache.geronimo.xbeans.javaee.SessionBeanType;
057    import org.apache.openejb.DeploymentInfo;
058    import org.apache.openejb.jee.EnterpriseBean;
059    import org.apache.openejb.jee.EntityBean;
060    import org.apache.openejb.jee.MessageDrivenBean;
061    import org.apache.openejb.jee.RemoteBean;
062    import org.apache.openejb.jee.SecurityIdentity;
063    import org.apache.openejb.jee.SessionBean;
064    import org.apache.openejb.jee.SessionType;
065    import org.apache.openejb.jee.oejb3.EjbDeployment;
066    import org.apache.xbean.finder.ClassFinder;
067    import org.apache.xmlbeans.XmlObject;
068    
069    /**
070     * Handles building ejb deployment gbeans.
071     */
072    public class EjbDeploymentBuilder {
073        private final EARContext earContext;
074        private final EjbModule ejbModule;
075        private final NamingBuilder namingBuilder;
076        private final ResourceEnvironmentSetter resourceEnvironmentSetter;
077        private final Map<String, GBeanData> gbeans = new TreeMap<String, GBeanData>();
078    
079        public EjbDeploymentBuilder(EARContext earContext, EjbModule ejbModule, NamingBuilder namingBuilder, ResourceEnvironmentSetter resourceEnvironmentSetter) {
080            this.earContext = earContext;
081            this.ejbModule = ejbModule;
082            this.namingBuilder = namingBuilder;
083            this.resourceEnvironmentSetter = resourceEnvironmentSetter;
084        }
085    
086        public void initContext() throws DeploymentException {
087            for (EnterpriseBean enterpriseBean : ejbModule.getEjbJar().getEnterpriseBeans()) {
088                AbstractName abstractName = createEjbName(enterpriseBean);
089                GBeanData gbean = null;
090                if (enterpriseBean instanceof SessionBean) {
091                    SessionBean sessionBean = (SessionBean) enterpriseBean;
092                    switch (sessionBean.getSessionType()) {
093                        case STATELESS:
094                            gbean = new GBeanData(abstractName, StatelessDeploymentGBean.GBEAN_INFO);
095                            break;
096                        case STATEFUL:
097                            gbean = new GBeanData(abstractName, StatefulDeploymentGBean.GBEAN_INFO);
098                            break;
099                    }
100                } else if (enterpriseBean instanceof EntityBean) {
101                    gbean = new GBeanData(abstractName, EntityDeploymentGBean.GBEAN_INFO);
102                } else if (enterpriseBean instanceof MessageDrivenBean) {
103                    gbean = new GBeanData(abstractName, MessageDrivenDeploymentGBean.GBEAN_INFO);
104                }
105                if (gbean == null) {
106                    throw new DeploymentException("Unknown enterprise bean type " + enterpriseBean.getClass().getName());
107                }
108    
109                String ejbName = enterpriseBean.getEjbName();
110    
111                EjbDeployment ejbDeployment = ejbModule.getOpenejbJar().getDeploymentsByEjbName().get(ejbName);
112                if (ejbDeployment == null) {
113                    throw new DeploymentException("OpenEJB configuration not found for ejb " + ejbName);
114                }
115                gbean.setAttribute("deploymentId", ejbDeployment.getDeploymentId());
116                gbean.setAttribute("ejbName", ejbName);
117    
118                // set interface class names
119                if (enterpriseBean instanceof RemoteBean) {
120                    RemoteBean remoteBean = (RemoteBean) enterpriseBean;
121    
122                    // Remote
123                    if (remoteBean.getRemote() != null) {
124                        String remoteInterfaceName = remoteBean.getRemote();
125                        assureEJBObjectInterface(remoteInterfaceName, ejbModule.getClassLoader());
126                        gbean.setAttribute(EjbInterface.REMOTE.getAttributeName(), remoteInterfaceName);
127    
128                        String homeInterfaceName = remoteBean.getHome();
129                        assureEJBHomeInterface(homeInterfaceName, ejbModule.getClassLoader());
130                        gbean.setAttribute(EjbInterface.HOME.getAttributeName(), homeInterfaceName);
131                    }
132    
133                    // Local
134                    if (remoteBean.getLocal() != null) {
135                        String localInterfaceName = remoteBean.getLocal();
136                        assureEJBLocalObjectInterface(localInterfaceName, ejbModule.getClassLoader());
137                        gbean.setAttribute(EjbInterface.LOCAL.getAttributeName(), localInterfaceName);
138    
139                        String localHomeInterfaceName = remoteBean.getLocalHome();
140                        assureEJBLocalHomeInterface(localHomeInterfaceName, ejbModule.getClassLoader());
141                        gbean.setAttribute(EjbInterface.LOCAL_HOME.getAttributeName(), localHomeInterfaceName);
142                    }
143    
144                    if (enterpriseBean instanceof SessionBean && ((SessionBean) enterpriseBean).getSessionType() == SessionType.STATELESS) {
145                        SessionBean statelessBean = (SessionBean) enterpriseBean;
146                        gbean.setAttribute(EjbInterface.SERVICE_ENDPOINT.getAttributeName(), statelessBean.getServiceEndpoint());
147                    }
148                }
149    
150                // set reference patterns
151                gbean.setReferencePattern("TrackedConnectionAssociator", new AbstractNameQuery(null, Collections.EMPTY_MAP, TrackedConnectionAssociator.class.getName()));
152                gbean.setReferencePattern("OpenEjbSystem", new AbstractNameQuery(null, Collections.EMPTY_MAP, OpenEjbSystem.class.getName()));
153    
154                try {
155                    earContext.addGBean(gbean);
156                } catch (GBeanAlreadyExistsException e) {
157                    throw new DeploymentException("Could not add entity bean to context", e);
158                }
159                gbeans.put(ejbName, gbean);
160            }
161        }
162    
163    
164        public void addEjbModuleDependency(AbstractName ejbModuleName) {
165            for (GBeanData gbean : gbeans.values()) {
166                gbean.addDependency(ejbModuleName);
167            }
168        }
169    
170        public ComponentPermissions buildComponentPermissions() throws DeploymentException {
171            ComponentPermissions componentPermissions = new ComponentPermissions(new Permissions(), new Permissions(), new HashMap<String, PermissionCollection>());
172            for (EnterpriseBean enterpriseBean : ejbModule.getEjbJar().getEnterpriseBeans()) {
173                addSecurityData(enterpriseBean, componentPermissions);
174            }
175            return componentPermissions;
176        }
177    
178        private void addSecurityData(EnterpriseBean enterpriseBean, ComponentPermissions componentPermissions) throws DeploymentException {
179            SecurityConfiguration securityConfiguration = (SecurityConfiguration) earContext.getSecurityConfiguration();
180            if (securityConfiguration != null) {
181                GBeanData gbean = getEjbGBean(enterpriseBean.getEjbName());
182                if (enterpriseBean instanceof RemoteBean) {
183                    RemoteBean remoteBean = (RemoteBean) enterpriseBean;
184    
185                    SecurityBuilder securityBuilder = new SecurityBuilder();
186                    PermissionCollection permissions = new Permissions();
187    
188                    securityBuilder.addToPermissions(permissions,
189                            remoteBean.getEjbName(),
190                            EjbInterface.HOME.getJaccInterfaceName(),
191                            remoteBean.getHome(),
192                            ejbModule.getClassLoader());
193                    securityBuilder.addToPermissions(permissions,
194                            remoteBean.getEjbName(),
195                            EjbInterface.REMOTE.getJaccInterfaceName(),
196                            remoteBean.getRemote(),
197                            ejbModule.getClassLoader());
198                    securityBuilder.addToPermissions(permissions,
199                            remoteBean.getEjbName(),
200                            EjbInterface.LOCAL.getJaccInterfaceName(),
201                            remoteBean.getLocal(),
202                            ejbModule.getClassLoader());
203                    securityBuilder.addToPermissions(permissions,
204                            remoteBean.getEjbName(),
205                            EjbInterface.LOCAL_HOME.getJaccInterfaceName(),
206                            remoteBean.getLocalHome(),
207                            ejbModule.getClassLoader());
208                    if (remoteBean instanceof SessionBean) {
209                        securityBuilder.addToPermissions(permissions,
210                                remoteBean.getEjbName(),
211                                EjbInterface.SERVICE_ENDPOINT.getJaccInterfaceName(),
212                                ((SessionBean) remoteBean).getServiceEndpoint(),
213                                ejbModule.getClassLoader());
214                    }
215                    if (remoteBean.getBusinessRemote() != null && !remoteBean.getBusinessRemote().isEmpty()) {
216                        for (String businessRemote : remoteBean.getBusinessRemote()) {
217                            securityBuilder.addToPermissions(permissions,
218                                    remoteBean.getEjbName(),
219                                    EjbInterface.REMOTE.getJaccInterfaceName(),
220                                    businessRemote,
221                                    ejbModule.getClassLoader());
222                        }
223                        securityBuilder.addToPermissions(componentPermissions.getUncheckedPermissions(),
224                                remoteBean.getEjbName(),
225                                EjbInterface.HOME.getJaccInterfaceName(),
226                                DeploymentInfo.BusinessRemoteHome.class.getName(),
227                                ejbModule.getClassLoader());
228                    }
229                    if (remoteBean.getBusinessLocal() != null && !remoteBean.getBusinessLocal().isEmpty()) {
230                        for (String businessLocal : remoteBean.getBusinessLocal()) {
231                            securityBuilder.addToPermissions(permissions,
232                                    remoteBean.getEjbName(),
233                                    EjbInterface.LOCAL.getJaccInterfaceName(),
234                                    businessLocal,
235                                    ejbModule.getClassLoader());
236                        }
237                        securityBuilder.addToPermissions(componentPermissions.getUncheckedPermissions(),
238                                remoteBean.getEjbName(),
239                                EjbInterface.LOCAL_HOME.getJaccInterfaceName(),
240                                DeploymentInfo.BusinessLocalHome.class.getName(),
241                                ejbModule.getClassLoader());
242                    }
243    
244                    String defaultRole = securityConfiguration.getDefaultRole();
245                    securityBuilder.addComponentPermissions(defaultRole,
246                            permissions,
247                            ejbModule.getEjbJar().getAssemblyDescriptor(),
248                            enterpriseBean.getEjbName(),
249                            remoteBean.getSecurityRoleRef(),
250                            componentPermissions);
251    
252                }
253                // RunAs subject
254                SecurityIdentity securityIdentity = enterpriseBean.getSecurityIdentity();
255                if (securityIdentity != null && securityIdentity.getRunAs() != null) {
256                    String runAsName = securityIdentity.getRunAs();
257                    if (runAsName != null) {
258                        gbean.setAttribute("runAsRole", runAsName);
259                    }
260                }
261    
262                gbean.setAttribute("securityEnabled", true);
263                gbean.setReferencePattern("RunAsSource", earContext.getJaccManagerName());
264            }
265        }
266    
267        public void buildEnc() throws DeploymentException {
268            //
269            // XMLBeans types must be use because Geronimo naming building is coupled via XMLBeans objects
270            //
271    
272            EjbJarType ejbJarType = (EjbJarType) ejbModule.getSpecDD();
273    
274            if (!ejbJarType.getMetadataComplete()) {
275                // Create a classfinder and populate it for the naming builder(s). The absence of a
276                // classFinder in the module will convey whether metadata-complete is set (or not)
277                ejbModule.setClassFinder(createEjbJarClassFinder(ejbModule));
278            }
279    
280            EnterpriseBeansType enterpriseBeans = ejbJarType.getEnterpriseBeans();
281            if (enterpriseBeans != null) {
282                for (SessionBeanType xmlbeansEjb : enterpriseBeans.getSessionArray()) {
283                    String ejbName = xmlbeansEjb.getEjbName().getStringValue().trim();
284                    GBeanData gbean = getEjbGBean(ejbName);
285                    ResourceRefType[] resourceRefs = xmlbeansEjb.getResourceRefArray();
286                    addEnc(gbean, xmlbeansEjb, resourceRefs);
287                }
288                for (MessageDrivenBeanType xmlbeansEjb : enterpriseBeans.getMessageDrivenArray()) {
289                    String ejbName = xmlbeansEjb.getEjbName().getStringValue().trim();
290                    GBeanData gbean = getEjbGBean(ejbName);
291                    ResourceRefType[] resourceRefs = xmlbeansEjb.getResourceRefArray();
292                    addEnc(gbean, xmlbeansEjb, resourceRefs);
293                }
294                for (EntityBeanType xmlbeansEjb : enterpriseBeans.getEntityArray()) {
295                    String ejbName = xmlbeansEjb.getEjbName().getStringValue().trim();
296                    GBeanData gbean = getEjbGBean(ejbName);
297                    ResourceRefType[] resourceRefs = xmlbeansEjb.getResourceRefArray();
298                    addEnc(gbean, xmlbeansEjb, resourceRefs);
299                }
300    
301            }
302    
303            if (!ejbJarType.getMetadataComplete()) {
304                ejbJarType.setMetadataComplete(true);
305                ejbModule.setOriginalSpecDD(ejbModule.getSpecDD().toString());
306            }
307        }
308    
309        private void addEnc(GBeanData gbean, XmlObject xmlbeansEjb, ResourceRefType[] resourceRefs) throws DeploymentException {
310            OpenejbGeronimoEjbJarType geronimoOpenejb = ejbModule.getVendorDD();
311    
312            //
313            // Build ENC
314            //
315    
316            // Geronimo uses a map to pass data to the naming build and for the results data
317            Map<Object, Object> buildingContext = new HashMap<Object, Object>();
318            buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, gbean.getAbstractName());
319            ((AnnotatedEjbJar) ejbModule.getAnnotatedApp()).setBean(xmlbeansEjb);
320    
321            namingBuilder.buildNaming(xmlbeansEjb,
322                    geronimoOpenejb,
323                    ejbModule, buildingContext);
324    
325            Map compContext = NamingBuilder.JNDI_KEY.get(buildingContext);
326            gbean.setAttribute("componentContextMap", compContext);
327    
328            //
329            // Process resource refs
330            //
331            GerResourceRefType[] gerResourceRefs = null;
332    
333            if (geronimoOpenejb != null) {
334                gerResourceRefs = geronimoOpenejb.getResourceRefArray();
335            }
336    
337            GBeanResourceEnvironmentBuilder refBuilder = new GBeanResourceEnvironmentBuilder(gbean);
338            resourceEnvironmentSetter.setResourceEnvironment(refBuilder, resourceRefs, gerResourceRefs);
339        }
340    
341        private ClassFinder createEjbJarClassFinder(EjbModule ejbModule) throws DeploymentException {
342    
343            try {
344                // Get the classloader from the module's EARContext
345                ClassLoader classLoader = ejbModule.getEarContext().getClassLoader();
346    
347                //----------------------------------------------------------------------------------------
348                // Find the list of classes from the ejb-jar.xml we want to search for annotations in
349                //----------------------------------------------------------------------------------------
350                List<Class> classes = new ArrayList<Class>();
351    
352                for (EnterpriseBean bean : ejbModule.getEjbJar().getEnterpriseBeans()) {
353                    classes.add(classLoader.loadClass(bean.getEjbClass()));
354                }
355    
356                return new ClassFinder(classes);
357            } catch (ClassNotFoundException e) {
358                throw new DeploymentException("Unable to load bean class.", e);
359            }
360        }
361    
362        private GBeanData getEjbGBean(String ejbName) throws DeploymentException {
363            GBeanData gbean = gbeans.get(ejbName);
364            if (gbean == null) throw new DeploymentException("EJB not gbean not found " + ejbName);
365            return gbean;
366        }
367    
368        private AbstractName createEjbName(EnterpriseBean enterpriseBean) {
369            String ejbName = enterpriseBean.getEjbName();
370            String type = null;
371            if (enterpriseBean instanceof SessionBean) {
372                SessionBean sessionBean = (SessionBean) enterpriseBean;
373                switch (sessionBean.getSessionType()) {
374                    case STATELESS:
375                        type = NameFactory.STATELESS_SESSION_BEAN;
376                        break;
377                    case STATEFUL:
378                        type = NameFactory.STATEFUL_SESSION_BEAN;
379                        break;
380                }
381            } else if (enterpriseBean instanceof EntityBean) {
382                type = NameFactory.ENTITY_BEAN;
383            } else if (enterpriseBean instanceof MessageDrivenBean) {
384                type = NameFactory.MESSAGE_DRIVEN_BEAN;
385            }
386            if (type == null) {
387                throw new IllegalArgumentException("Unknown enterprise bean type XXX " + enterpriseBean.getClass().getName());
388            }
389            return earContext.getNaming().createChildName(ejbModule.getModuleName(), ejbName, type);
390        }
391    
392        private static Class assureEJBObjectInterface(String remote, ClassLoader cl) throws DeploymentException {
393            return AbstractNamingBuilder.assureInterface(remote, "javax.ejb.EJBObject", "Remote", cl);
394        }
395    
396        private static Class assureEJBHomeInterface(String home, ClassLoader cl) throws DeploymentException {
397            return AbstractNamingBuilder.assureInterface(home, "javax.ejb.EJBHome", "Home", cl);
398        }
399    
400        public static Class assureEJBLocalObjectInterface(String local, ClassLoader cl) throws DeploymentException {
401            return AbstractNamingBuilder.assureInterface(local, "javax.ejb.EJBLocalObject", "Local", cl);
402        }
403    
404        public static Class assureEJBLocalHomeInterface(String localHome, ClassLoader cl) throws DeploymentException {
405            return AbstractNamingBuilder.assureInterface(localHome, "javax.ejb.EJBLocalHome", "LocalHome", cl);
406        }
407    }