6.4. Deploying EJBsAs with web components (servlets and JavaServer Pages), EJB components in the J2EE environment are packaged into jar files, and the components are described and configured within an application server using deployment descriptors that are included in these jar files. These deployment descriptors are based on an XML DTD that is published as part of the EJB specification. An overview of J2EE deployment concepts can be found in Chapter 2. In this section, we discuss some EJB-specific deployment details that complement the material in Chapter 2. Once you've written the home and remote interfaces and the implementation of your enterprise bean, you need to deploy your beans in an EJB container, which involves the following steps:
As shown in Figure 6-2, the EJB container generates a set of classes that are needed to deploy your EJB object. It's up to the EJB container to provide a tool or tools for generating these classes. Some may be command-line tools that read a standard EJB deployment descriptor and generate the needed classes while others may be GUI tools that let you control the deployment options of your bean using a visual interface. 6.4.1. EJB Deployment DescriptorsAll EJB deployment descriptors include a listing of the Java classes that serve as the home, client, and implementation classes for a particular EJB. They also include various metadata about the bean, such as a description, a display name for the bean (useful in application server management interfaces), and so on. Appendix A provides a complete reference for EJB deployment descriptors. We'll simply give a short overview here. It might be useful to refer back to the schema structure diagrams in "Enterprise JavaBeans (ejb-jar.xml)" in Appendix A as you read through this section. The general structure for an EJB deployment descriptor is as follows: <? xml version="1.0" encoding="UTF-8" ?> <ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <!-- Metadata about the contents of the ejb-jar file ... --> <!-- Provide an enterprise-bean element for each EJB component contained in the jar --> <enterprise-beans> <!-- Describe a session bean --> <session>...</session> <!-- Describe an entity bean --> <entity>...</entity> <!-- Describe a message-driven bean --> <message-driven>...</message-driven> </enterprise-beans> <!-- Any additional metadata (inter-EJB relationships, assembly info, etc.) --> <relationships>...</relationships> <assembly-descriptor>...</assembly-descriptor> <ejb-client-jar>...</ejb-client-jar> ... </ejb-jar> The EJB descriptor starts with the usual XML preface. The top-level element in the deployment descriptor is an <ejb-jar> element, and it references the standard XML schema for EJB deployment descriptors, in this case referencing the schema from the java.sun.com server (see Chapter 7 for more details on XML schema and namespaces). The <ejb-jar> root element contains an <enterprise-beans> element that lists each EJB in the jar. Each entry within the <enterprise-beans> element contains the deployment information for a single EJB, including the various classes that make up the EJB's implementation, runtime management parameters, and so on. A complete deployment descriptor that describes our ProfileManager EJB is shown in Example 6-5. Example 6-5. Deployment descriptor for the profile server EJB<? xml version="1.0" encoding="UTF-8" ?> <ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <!-- Description of contents --> <description>Introductory EJB Example: Stateless ProfileManager Bean</description> <display-name>ProfileManager Bean Example (stateless)</display-name> <enterprise-beans> <!-- A stateless session profile server EJB --> <session> <display-name>ProfileManager Bean</display-name> <ejb-name>ProfileManagerBean</ejb-name> <!-- Remote home interface --> <home> com.oreilly.jent.ejb.stateless.ProfileManagerHome </home> <!-- Remote bean interface --> <remote> com.oreilly.jent.ejb.stateless.ProfileManager </remote> <!-- Local home interface --> <local-home> com.oreilly.jent.ejb.stateless.ProfileManagerLocalHome </local-home> <!-- Local bean interface --> <local> com.oreilly.jent.ejb.stateless.ProfileManagerLocal </local> <!-- Bean implementation class --> <ejb-class> com.oreilly.jent.ejb.stateless.ProfileManagerBean </ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> <ejb-client-jar>ProfileManagerClient.jar</ejb-client-jar> </ejb-jar> We're using the <description> and <display-name> elements to provide some text descriptions of the contents within the top-level <ejb-jar> element. The information contained in these elements might be used by a management console on an application server or within an EJB-enabled IDE. The <enterprise-beans> element contains one element for each EJB contained in an EJB jar. There are three possible subelements of <enterprise-beans>, one for each type of EJB: <session>, <entity>, and <message-driven>. Our ProfileManager EJB is a session bean, so we use the <session> element here. Within the <session> element, we provide a display name for the bean itself and an <ejb-name> element that provides a name that can be used to refer to this bean from other J2EE components. Then we specify all of the classes that make up our EJB: the remote home and client interfaces (<home> and <remote>), the local home and client interfaces (<local-home> and <local>), and the EJB implementation class itself (<ejb-class>). At the end of the <session> element comes configuration information that is specific to session EJBs. In this case, we are using the <session-type> element to specify that this is a stateless session bean and the <transaction-type> element to indicate that we want transactions managed by the EJB container for this EJB. This information is used primarily by the EJB container to determine how the EJB should be managed at runtime. The information that is specified in this section of the deployment descriptor depends on the type of EJB being described (session, entity, message-driven). Examples of the kind of metadata required for the various types of EJBs are provided later in this chapter. 6.4.2. Security-Related Deployment AttributesSecurity management is another important service provided by the EJB container for your components. Essentially, the EJB container needs to provide some means for mapping a client identity to a named user or role defined on the EJB server itself. Then you, the bean provider, specify which users and/or roles can access each method on your bean. This approach to specifying security details keeps the access details out of the bean implementation code and allows the same bean to be deployed in different EJB containers, using different underlying security systems and access settings that suit the local context. If you do need to resort to programmatic security checks in your EJB components, the EJBContext interface provides access to the user's identity and roles. Refer to "EJB Component Security" in Chapter 10 for further details on programmatic security in the EJB context. 6.4.2.1. Client security rolesThe EJB specification allows you to specify security roles and associated access control levels in the EJB deployment descriptor. Suppose that we are deploying our EJB-based profile service and we want any client at all to be able to read profile entries, but we want only clients (individual users or client applications) identified as administrators to be able to set profile entries. To do this, we first have to define an appropriate security role in our EJB deployment descriptor. This is done in the <assembly-descriptor> element, which follows the <enterprise-beans> and <relationships> elements in the root <ejb-jar> element. Within the <assembly-descriptor>, you define a security role using the <security-role> element: ... <assembly-descriptor> ... <security-role> <description> Profile administrators, allowed to update entries </description> <role-name>profile-admin</role-name> </security-role> ... </assembly-descriptor> ... In order for these security roles to actually mean anything, they need to be assigned to concrete principals and/or groups defined within the security realms of whatever J2EE application server you are using to deploy your EJBs. The specific details of how this is done depends on the particular vendor you are using. In BEA WebLogic, you would use the WebLogic administration interface to create new realms that refer to existing LDAP, RDBMS, native OS, and/or custom authentication services and then map your security roles to principals defined in these realms. Other application servers will provide other schemes for authenticating users and tying their identities into the EJB context. 6.4.2.2. Method permissionsSo far we've declared some application-specific security roles, but we haven't given these roles any rights to invoke methods on our EJBs. To do this, we have to add one or more <method-permission> elements to the <assembly-descriptor>. A <method-permission> element gives a particular role rights to access one or more methods on an EJB. Suppose we defined a Profile EJB, with an implementation class called BMPProfileBean, representing persistent data about a profile. This is an entity EJB version of the serializable Profile bean that we used in our stateless session example earlier (this EJB will be shown in detail in later sections). We might have a setEntry( ) method on this EJB that's used to alter entry values on the Profile, and we might want to give only the profile-admin role the rights to execute the setEntry( ) method on our Profile bean. If so, we would use a <method-permission> element such as: ... <assembly-descriptor> ... <method-permission> <description> Only allow profile administrators to set entries on profiles </description> <role-name>profile-admin</role-name> <method> <ejb-name>BMPProfileBean</ejb-name> <method-name>setEntry</method-name> </method> </method-permission> ... </assembly-descriptor> ... This allows any user who has the profile-admin role to invoke the setEntry( ) method on the Profile, while all other client methods on the bean are accessible to everyone. The <role-name> indicates which role should have access to this method, and the method element(s) indicates the EJB and method that this role can access. Here, we have a single method specified: any method named setEntry on the BMPProfileBean EJB defined in this deployment descriptor. If setEntry( ) is overloaded on our bean, we can be more specific by including the method parameters of the single method we want made accessible: ... <assembly-descriptor> ... <method-permission> <description> Only allow profile administrators to set entries on profiles </description> <role-name>profile-admin</role-name> <method> <ejb-name>BMPProfileBean</ejb-name> <method-name>setEntry</method-name> <method-params> <method-param>java.lang.String</method-param> <method-param>java.lang.String</method-param> </method-params> </method> </method-permission> ... </assembly-descriptor> ... You can also give access to all methods on an EJB by using an asterisk (*) as the value of the <method-name> element. By default, all methods on all EJBs are accessible to everyone. Any methods not specified in a <method-permission> element in the deployment descriptor maintain this global accessibility. 6.4.2.3. Propagating identitiesIt's also possible in EJB to specify how identities are propagated from your EJB to other EJBs and resources, by indicating which role your EJB will assume when its methods are executed. The identity used to execute the EJB methods will be passed forward when the EJB makes calls to other EJBs or accesses other J2EE resources. This identity will be used to determine access rights to these resources in the same way that the caller's credentials are used to control access to the EJB itself and so on down the call chain. To specify the runtime identity used to execute your EJB methods, use the <security-identity> element in the <session>, <entity>, and <message-driven> sections of the deployment descriptor. The <security-identity> element allows you to specify either that the methods on the bean will be invoked using the identity of the calling client or that a specific named security role will be used to invoke the bean methods. The first option simply passes on the identity of the calling client when an EJB method invokes other EJBs or resources. To specify this option, include the following in the EJB's deployment descriptor section (a session bean is used as the example here): ... <enterprise-beans> ... <session> ... <security-identity> <use-caller-identity /> </security-identity> ... </session> ... </enterprise-beans> ... This scheme is most appropriate when the client operates in a context in which it can be authenticated against a shared authority (e.g., a servlet is the user entry point, and the user is challenged with an HTTP authentication prompt), and the user's identity can be propagated to the EJB container. Note that the <use-caller-identity> option can't be used with message-driven beans since there is no path for a user's credentials to be propagated to the EJB container via message destinations. A message-driven bean has to assume its own role when handling messages. The other alternative is to specify a role under which all invocations of your EJB will be executed. For this, use the <run-as> option in the <security-identity>: ... <enterprise-beans> ... <session> ... <security-identity> <run-as> <role-name>profile-admin</role-name> </run-as> </security-identity> ... </session> ... </enterprise-beans> ... Here, we're specifying that all method invocations on our EJB will run under the profile-admin role and will assume that role's access rights when invoking other EJBs and resources. 6.4.3. Packaging EJBsAn ejb-jar file is the standard packaging format for Enterprise JavaBeans. It is a normal Java archive (jar) file, created using the standard jar utility, but it contains specific files that provide all the information needed for an EJB container to deploy the beans that are contained in the jar file. An ejb-jar file can contain one or more EJBs. An ejb-jar file contains two types of files:
An ejb-jar file is a compact, efficient way to package a set of EJBs for deployment in an EJB container; it contains all the information an EJB container requires to manage your EJBs. The compiled Java classes that make up your EJBs are included in the ejb-jar file in package-specific directories, just as in a regular jar file. The deployment descriptor that describes and configures the EJBs must be included in the ejb-jar file as META-INF/ejb-jar.xml. Some EJB container vendors include a utility to facilitate the creation of ejb-jar files from your bean classes. Ant also includes a task, ejbjar, that can be used to create ejb-jar files from your Ant buildfiles (see Chapter 17 for more details). It's a simple matter, however, to create an ejb-jar using the jar utility provided with the JDK. Assuming that you have created a valid ejb-jar.xml deployment descriptor file, such as the one shown earlier, simply compile all of your relevant classes into a given directory, create a META-INF directory in this same area, and put the ejb-jar.xml file in it, then use jar to create your ejb-jar file: % jar cvf ProfileManager.jar com/oreilly/jent/ejb/*.class META-INF added manifest adding: com/oreilly/jent/ejb/ProfileManager.class(in = 344) (out= 225)(deflated 34%) adding: com/oreilly/jent/ejb/ProfileManagerBean.class(in = 1254) (out= 598)(deflated 52%) adding: com/oreilly/jent/ejb/ProfileManagerHome.class(in = 317) (out= 208)(deflated 34%) adding: com/oreilly/jent/ejb/ProfileManagerLocal.class(in = 327) (out= 214)(deflated 34%) adding: com/oreilly/jent/ejb/ProfileManagerLocalHome.class(in = 305) (out= 198) (deflated 35%) ignoring entry META-INF/ adding: META-INF/ejb-jar.xml(in = 0) (out= 0)(stored 0%) This command creates an ejb-jar file named ProfileManager.jar in the current directory. Note that this ejb-jar file doesn't contain any container-generated classes; these need to be created using tools provided by the EJB vendor, as described in the next section. 6.4.3.1. Generating the container classesSo far we've seen the basic elements that make up an EJB (home interface(s), client interface(s), and a bean implementation class), how the configuration parameters for one or more beans are specified in an XML deployment descriptor, and how all of these can be packaged into a standard ejb-jar file. In order for an EJB container to deploy your EJBs, it still needs to generate the container-specific classes depicted in Figure 6-2. How this is done depends on the EJB container you are using. If you are using BEA's WebLogic application server, for example, you can use the appc tool that is provided with the server to generate the runtime support classes needed by WebLogic. If we've packaged our EJB classes and deployment descriptor into an ejb-jar file named ProfileManager.jar, we can use the appc tool like so (for a Unix environment): > java -classpath $WL_HOME/lib/weblogic.jar:$CLASSPATH weblogic.appc -output wl-ProfileManager.jar ProfileManager.jar If the EJB(s) contained in the specified ejb-jar file check out (the interfaces, implementation classes, and deployment descriptor meet the EJB specification requirements), this will generate a new ejb-jar file, wl-ProfileManager.jar, that contains all the classes needed to run your EJB(s) within the WebLogic server. This ejb-jar file can be directly deployed to the server. Each EJB container will have its own scheme for generating support classes for your EJBs. Consult your EJB vendor's documentation for specific details. 6.4.3.2. Delivering the packageFinally, after writing, configuring, and packaging one or more EJB components, you need to actually deliver the EJBs to the server running the EJB container. Again, this procedure is not specified by the EJB or J2EE specificationseach server implementation is free to do this in whatever way is most appropriate for it. In some cases, EJB components are deployed by simply placing the ejb-jar file in a specific directory on the server. In other cases, a server management tool (web-based or otherwise) needs to be used to upload the ejb-jar file to the server to deploy it. |