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