EJB 3 Security

Exclusive offer: get 50% off this eBook here
EJB 3 Developer Guide

EJB 3 Developer Guide — Save 50%

Enterprise JavaBean 3 - a Practical Book and eBook Guide for developers and architects using the EJB Standard.

£14.99    £7.50
by Michael Sikora | September 2008 | Architecture & Analysis Java Open Source

Security is a wide ranging topic which operates on many levels and covers many technologies. The technologies involved include networks, operating systems, database systems, and application servers as well as manual procedures.

Application level security in the context of Java EE applications is provided by web and EJB containers. This article by Michael Sikora will be mostly concerned with EJB container security. However, as EJBs are often invoked from the web-tier we will take a brief look at web container security later in this article.

We will cover the following topics:

  • Java EE container security

Java EE Container Security

There are two aspects covered by Java EE container security: authentication and authorization. Authentication is the process of verifying that users are who they claim to be. Typically this is performed by the user providing credentials such as a password. Authorization, or access control, is the process of restricting operations to specific users or categories of users. The EJB specification provides two kinds of authorization: declarative and programmatic, as we shall see later in the article.

The Java EE security model introduces a few concepts common to both authentication and authorization. A principal is an entity that we wish to authenticate. The format of a principal is application-specific but an example is a username. A role is a logical grouping of principals. For example, we can have administrator, manager, and employee roles. The scope over which a common security policy applies is known as a security domain, or realm.

Authentication

For authentication, every Java EE compliant application server provides the Java Authentication and Authorization Service (JAAS) API. JAAS supports any underlying security system. So we have a common API regardless of whether authentication is username/password verification against a database, iris or fingerprint recognition for example. The JAAS API is fairly low level and most application servers provide authentication mechanisms at a higher level of abstraction. These authentication mechanisms are application-server specific however. We will not cover JAAS any further here, but look at authentication as provided by the GlassFish application server.

GlassFish Authentication

There are three actors we need to define on the GlassFish application server for authentication purposes: users, groups, and realms. A user is an entity that we wish to authenticate. A user is synonymous with a principal. A group is a logical grouping of users and is not the same as a role. A group's scope is global to the application server. A role is a logical grouping of users whose scope is limited to a specific application. Of course for some applications we may decide that roles are identical to groups. For other applications we need some mechanism for mapping the roles onto groups. We shall see how this is done later. A realm, as we have seen, is the scope over which a common security policy applies.

GlassFish provides three kinds of realms: file, certificate, and admin-realm. The file realm stores user, group, and realm credentials in a file named keyfile. This file is stored within the application server file system. A file realm is used by web clients using http or EJB application clients. The certificate realm stores a digital certificate and is used for authenticating web clients using https. The admin-realm is similar to the file realm and is used for storing administrator credentials. GlassFish comes pre-configured with a default file realm named file.

We can add, edit, and delete users, groups, and realms using the GlassFish administrator console. We can also use the create-file-user option of the asadmin command line utility. To add a user named scott to a group named bankemployee, in the file realm, we would use the command:

<target name="create-file-user">
<exec executable="${glassfish.home}/bin/asadmin"
failonerror="true"
vmlauncher="false">
<arg line="create-file-user --user admin
--passwordfile userpassword --groups bankemployee scott"/>
</exec>
</target>
  • --user specifies the GlassFish administrator username, admin in our example.
  • --passwordfile specifies the name of the file containing password entries. In our example this file is userpassword. Users, other than GlassFish administrators, are identified by AS_ADMIN_USERPASSWORD. In our example the content of the userpassword file is:
    AS_ADMIN_USERPASSWORD=xyz

     This indicates that the user's password is xyz.

  • --groups specifies the groups associated with this user (there may be more than one group). In our example there is just one group, named bankemployee. Multiple groups are colon delineated. For example if the user belongs to both the bankemployee and bankcustomer groups, we would specify:
  • --groups bankemployee:bankcustomer
  • The final entry is the operand which specifies the name of the user to be created. In our example this is scott.

There is a corresponding asadmin delete-file-user option to remove a user from the file realm.

Mapping Roles to Groups

The Java EE specification specifies that there must be a mechanism for mapping local application specific roles to global roles on the application server. Local roles are used by an EJB for authorization purposes. The actual mapping mechanism is application server specific. As we have seen in the case of GlassFish, the global application server roles are called groups. In GlassFish, local roles are referred to simply as roles. Suppose we want to map an employee role to the bankemployee group. We would need to create a GlassFish specific deployment descriptor, sun-ejb-jar.xml, with the following element:

<security-role-mapping>
<role-name>employee</role-name>
<group-name>bankemployee</group-name>
</security-role-mapping>

We also need to access the configuration-security screen in the administrator console. We then disable the Default Principal To Role Mapping flag. If the flag is enabled then the default is to map a group onto a role with the same name. So the bankemployee group will be mapped to the bankemployee role.

EJB 3 Security

We can leave the default values for the other properties on the configuration-security screen. Many of these features are for advanced use where third party security products can be plugged in or security properties customized. Consequently we will give only a brief description of these properties here.

  • Security Manager: This refers to the JVM security manager which performs code-based security checks. If the security manager is disabled GlassFish will have better performance. However, even if the security manager is disabled, GlassFish still enforces standard Java EE authentication/authorization.
  • Audit Logging: If this is enabled, GlassFish will provide an audit trail of all authentication and authorization decisions through audit modules. Audit modules provide information on incoming requests, outgoing responses and whether authorization was granted or denied. Audit logging applies for web-tier and ejb-tier authentication and authorization. A default audit module is provided but custom audit modules can also be created.
  • Default Realm: This is the default realm used for authentication. Applications use this realm unless they specify a different realm in their deployment descriptor. The default value is file. Other possible values are admin-realm and certificate. We discussed GlassFish realms in the previous section.
  • Default Principal: This is the user name used by GlassFish at run time if no principal is provided. Normally this is not required so the property can be left blank.
  • Default Principal Password: This is the password of the default principal.
  • JACC: This is the class name of a JACC (Java Authorization Contract for Containers) provider. This enables the GlassFish administrator to set up third-party plug in modules conforming to the JACC standard to perform authorization.
  • Audit Modules: If we have created custom modules to perform audit logging, we would select from this list.
  • Mapped Principal Class: This is only applicable when Default Principal to Role Mapping is enabled. The mapped principal class is used to customize the java.security.Principal implementation class used in the default principal to role mapping. If no value is entered, the com.sun.enterprise.deployment.Group implementation of java.security.Principal is used.

Authenticating an EJB Application Client

Suppose we want to invoke an EJB, BankServiceBean, from an application client. We also want the application client container to authenticate the client. There are a number of steps we first need to take which are application server specific. We will assume that all roles will have the same name as the corresponding application server groups. In the case of GlassFish we need to use the administrator console and enable Default Principal To Role Mapping.

Next we need to define a group named bankemployee with one or more associated users.

An EJB application client needs to use IOR (Interoperable Object Reference) authentication. The IOR protocol was originally created for CORBA (Common Object Request Broker Architecture) but all Java EE compliant containers support IOR. An EJB deployed on one Java EE compliant vendor may be invoked by a client deployed on another Java EE compliant vendor. Security interoperability between these vendors is achieved using the IOR protocol. In our case the client and target EJB both happen to be deployed on the same vendor, but we still use IOR for propagating security details from the application client container to the EJB container.

IORs are configured in vendor specific XML files rather than the standard ejb-jar.xml file. In the case of GlassFish, this is done within the <ior-security-config> element within the sun-ejb-jar.xml deployment descriptor file. We also need to specify the invoked EJB, BankServiceBean, in the deployment descriptor. An example of the sun-ejb-jar.xml deployment descriptor is shown below:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD
      Application Server 9.0 EJB 3.0//EN"
      "http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
  <enterprise-beans>
    <ejb>
      <ejb-name>BankServiceBean</ejb-name>
        <ior-security-config>
          <as-context>
             <auth-method>USERNAME_PASSWORD</auth-method>
             <realm>default</realm>
             <required>true</required>
          </as-context>
        </ior-security-config>
    </ejb>
  </enterprise-beans>
</sun-ejb-jar>
  • The as in <as-context> stands for the IOR authentication service. This specifies authentication mechanism details.
  • The <auth-method> element specifies the authentication method. This is set to USERNAME_PASSWORD which is the only value for an application client.
  • The <realm> element specifies the realm in which the client is authenticated.
  • The <required> element specifies whether the above authentication method is required to be used for client authentication.

When creating the corresponding EJB JAR file, the sun-ejb-jar.xml file should be included in the META-INF directory, as follows:

<target name="package-ejb" depends="compile">
    <jar jarfile="${build.dir}/BankService.jar">
        <fileset dir="${build.dir}">
             <include name="ejb30/session/**" />
                          <include name="ejb30/entity/**" />
              </fileset>
              <metainf dir="${config.dir}">
            <include name="persistence.xml" />
                         <include name="sun-ejb-jar.xml" />
        </metainf>
    </jar>
</target>

As soon as we run the application client, GlassFish will prompt with a username and password form, as follows:

EJB 3 Security

If we reply with the username scott and password xyz the program will run. If we run the application with an invalid username or password we will get the following error message:

javax.ejb.EJBException: nested exception is: java.rmi.AccessException: 
CORBA NO_PERMISSION 9998 .....

EJB Authorization

Authorization, or access control, is the process of restricting operations to specific roles. In contrast with authentication, EJB authorization is completely application server independent. The EJB specification provides two kinds of authorization: declarative and programmatic. With declarative authorization all security checks are performed by the container. An EJB's security requirements are declared using annotations or deployment descriptors. With programmatic authorization security checks are hard-coded in the EJBs code using API calls. However, even with programmatic authorization the container is still responsible for authentication and for assigning roles to principals.

Declarative Authorization

As an example, consider the BankServiceBean stateless session bean with methods findCustomer(), addCustomer() and updateCustomer():

package ejb30.session;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import ejb30.entity.Customer;
import javax.persistence.PersistenceContext;
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.PermitAll;
import java.util.*;
@Stateless
@RolesAllowed("bankemployee")
public class BankServiceBean implements BankService {
@PersistenceContext(unitName="BankService")
private EntityManager em;
private Customer cust;
@PermitAll
public Customer findCustomer(int custId) {
return ((Customer) em.find(Customer.class, custId));
}
public void addCustomer(int custId, String firstName,
String lastName) {
cust = new Customer();
cust.setId(custId);
cust.setFirstName(firstName);
cust.setLastName(lastName);
em.persist(cust);
}
public void updateCustomer(Customer cust) {
Customer mergedCust = em.merge(cust);
}
}

We have prefixed the bean class with the annotation:

@RolesAllowed("bankemployee")

This specifies the roles allowed to access any of the bean's method. So only users belonging to the bankemployee role may access the addCustomer() and updateCustomer() methods. More than one role can be specified by means of a brace delineated list, as follows:

@RolesAllowed({"bankemployee", "bankcustomer"})

We can also prefix a method with @RolesAllowed, in which case the method annotation will override the class annotation. The @PermitAll annotation allows unrestricted access to a method, overriding any class level @RolesAllowed annotation.

As with EJB 3 in general, we can use deployment descriptors as alternatives to the @RolesAllowed and @PermitAll annotations.

Denying Authorization

Suppose we want to deny all users access to the BankServiceBean.updateCustomer() method. We can do this using the @DenyAll annotation:

@DenyAll
public void updateCustomer(Customer cust) {
Customer mergedCust = em.merge(cust);
}

Of course if you have access to source code you could simply delete the method in question rather than using @DenyAll. However suppose you do not have access to the source code and have received the EJB from a third party. If you in turn do not want your clients accessing a given method then you would need to use the <exclude-list> element in the ejb-jar.xml deployment descriptor:

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
            http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>BankServiceBean</ejb-name>
</session>
</enterprise-beans>
<assembly-descriptor>
<exclude-list>
<method>
<ejb-name>BankServiceBean</ejb-name>
<method-name>updateCustomer</method-name>
</method>
</exclude-list>
</assembly-descriptor>
</ejb-jar>

EJB Security Propagation

Suppose a client with an associated role invokes, for example, EJB A. If EJB A then invokes, for example, EJB B then by default the client's role is propagated to EJB B. However, you can specify with the @RunAs annotation that all methods of an EJB execute under a specific role.

For example, suppose the addCustomer() method in the BankServiceBean EJB invokes the addAuditMessage() method of the AuditServiceBean EJB:

@Stateless
@RolesAllowed("bankemployee")
public class BankServiceBean implements BankService {
private @EJB AuditService audit;
....
     public void addCustomer(int custId, String firstName,
                                                         String lastName) {
             cust = new Customer();
             cust.setId(custId);
             cust.setFirstName(firstName);
             cust.setLastName(lastName);
             em.persist(cust);
             audit.addAuditMessage(1, "customer add attempt");
     }
     ...
}

Note that only a client with an associated role of bankemployee can invoke addCustomer(). If we prefix the AuditServiceBean class declaration with @RunAs("bankauditor") then the container will run any method in AuditServiceBean as the bankauditor role, regardless of the role which invokes the method. Note that the @RunAs annotation is applied only at the class level, @RunAs cannot be applied at the method level.

@Stateless
@RunAs("bankauditor")
public class AuditServiceBean implements AuditService {
@PersistenceContext(unitName="BankService")
private EntityManager em;
@TransactionAttribute(
TransactionAttributeType.REQUIRES_NEW)
public void addAuditMessage (int auditId,
String message) {
Audit audit = new Audit();
audit.setId(auditId);
audit.setMessage(message);
em.persist(audit);
}
}

Programmatic Authorization

With programmatic authorization the bean rather than the container controls authorization. The javax.ejb.SessionContext object provides two methods which support programmatic authorization: getCallerPrincipal() and isCallerInRole(). The getCallerPrincipal() method returns a java.security.Principal object. This object represents the caller, or principal, invoking the EJB. We can then use the Principal.getName() method to obtain the name of the principal. We have done this in the addAccount() method of the BankServiceBean as follows:

Principal cp = ctx.getCallerPrincipal();
System.out.println("getname:" + cp.getName());

The isCallerInRole() method checks whether the principal belongs to a given role. For example, the code fragment below checks if the principal belongs to the bankcustomer role. If the principal does not belong to the bankcustomer role, we only persist the account if the balance is less than 99.

if (ctx.isCallerInRole("bankcustomer")) {
    em.persist(ac);
} else if (balance < 99) {
           em.persist(ac);
  }

When using the isCallerInRole() method, we need to declare all the security role names used in the EJB code using the class level @DeclareRoles annotation:

@DeclareRoles({"bankemployee", "bankcustomer"})

The code below shows the BankServiceBean EJB with all the programmatic authorization code described in this section:

package ejb30.session;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import ejb30.entity.Account;
import javax.persistence.PersistenceContext;
import javax.annotation.security.RolesAllowed;
import java.security.Principal;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.annotation.security.DeclareRoles;
import java.util.*;
@Stateless
@DeclareRoles({"bankemployee", "bankcustomer"})
public class BankServiceBean implements BankService {
@PersistenceContext(unitName="BankService")
private EntityManager em;
private Account ac;
@Resource SessionContext ctx;
@RolesAllowed({"bankemployee", "bankcustomer"})
public void addAccount(int accountId, double balance,
String accountType) {
ac = new Account();
ac.setId(accountId);
ac.setBalance(balance);
ac.setAccountType(accountType);
Principal cp = ctx.getCallerPrincipal();
System.out.println("getname:" + cp.getName());
if (ctx.isCallerInRole("bankcustomer")) {
em.persist(ac);
} else if (balance < 99) {
em.persist(ac);
}
}
.....
}

Where we have a choice declarative authorization is preferable to programmatic authorization. Declarative authorization avoids having to mix business code with security management code. We can change a bean's security policy by simply changing an annotation or deployment descriptor instead of modifying the logic of a business method. However, some security rules, such as the example above of only persisting an account within a balance limit, can only be handled by programmatic authorization. Declarative security is based only on the principal and the method being invoked, whereas programmatic security can take state into consideration.

Java EE Web Container Security

Because an EJB is typically invoked from the web-tier by a servlet, JSP page or JSF component, we will briefly mention Java EE web container security. The web-tier and EJB tier share the same security model. So the web-tier security model is based on the same concepts of principals, roles and realms.

EJB 3 Developer Guide Enterprise JavaBean 3 - a Practical Book and eBook Guide for developers and architects using the EJB Standard.
Published: May 2008
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

Web-Tier Authorization

Web-tier authorization is similar to EJB authorization but with a few differences. Both programmatic and declarative authorization is supported for the web-tier. For programmatic authorization, the EJB methods of getCallerPrincipal() and isCallerInRole() have their equivalents of getUserPrincipal() and isUserInRole() in the HttpServletRequest interface. These methods would then be used in a servlet or JSP page. To use these methods we would need to add the @DeclareRoles annotation or the <security-role> deployment descriptor element as with EJBs. The @RunAs annotation can also be used at servlet class level to specify that a servlet runs as a specified role.

There is no annotation equivalent for web-tier declarative authorization; we have to use the web.xml deployment descriptor instead. In the code fragment below, the bankemployee and bankcustomer roles have access to all URLs in the application (specified by the URL pattern of /*). Of course we can extend this to allow given roles access to specific URLs.

<security-constraint>
<web-resource-collection>
<web-resource-name>bank service example</web-resource-name>
<url-pattern>/*<url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>bankemployee</role-name>
<role-name>bankcustomer</role-name>
</auth-constraint>
</security-constraint>

Recall that the mechanism for mapping local application specific roles to global roles (or roles to groups) on the application server is application server specific. In the case of GlassFish we would use the <security-role-mapping> element in the sun-web.xml deployment descriptor as we did in the sun-ejb.xml deployment descriptor for an EJB application client.

Transport Layer Security

Before we move on to web-tier authentication, we will briefly describe transport layer security, as we refer to this when we configure web-tier authentication. The transport layer is characterized by the Secure Sockets Layer (SSL) protocol and its successor the Transport Layer Security (TLS) protocol. Messages are encrypted using public-key encryption, and users are authenticated using digital certificates. The application server presents a certificate to the client's web browser. The purpose of the certificate is to verify that the site is what it claims to be. This is known as server-side authentication. The server may request the client to present their digital certificate. This is known as client-side authentication. Mutual authentication occurs when both server-side and client-side authentication are enabled. The SSL or TLS layers are typically used underneath http. In this case the https URL scheme is used to indicate a secure http connection.

The SSL and TLS protocols correspond to the transport layer of the Open System Interconnection (OSI) communication model.

Web-Tier Authentication

We have seen that an EJB application client uses IOR authentication and only one authentication mechanism, USERNAME_PASSWORD, is available. Web applications have a number of authentication mechanisms: basic, form, and client certificate. Before we can authenticate a web application user we must have set up valid usernames, passwords and roles on the web or application server. This process is vendor specific. We have seen how to do this for the GlassFish application server.

Basic authentication requires a username and password to be entered on a login form supplied by the web container. The username and password are transmitted as part of an http request and authenticated by the web container. This mechanism is not secure. If the transmission is intercepted, the username and password can easily be decoded. Basic authentication is specified in the <auth-method> element within the <login-config> element in the web.xml deployment descriptor as follows:

<login-config>
<auth-method>BASIC</auth-method>
</login-config>

Form authentication is similar to basic authentication except that the login and error screens can be customized. Form authentication takes places over http and like basic authentication, is not secure. Form authentication is specified in the web.xml deployment descriptor as follows:

<login-config>
<auth-method>FORM</auth-method>
</login-config>

Client certificate authentication requires the web server to authenticate a client using the client's public key certificate. The authentication uses https (http over SSL) so is more secure than either basic or form authentication. Client certificate authentication is specified in the web.xml deployment descriptor as follows:

<login-config>
<auth-method>CLIENT</auth-method>
</login-config>

Example of Web-Tier Authentication and Authorization

We will take the example of AddCustomerServlet, which adds a customer to the database. In this example we will see that only users belonging to the bankemployee role can access the servlet using basic authentication. Basic authentication requires a username and password to be entered on a login form supplied by the web container before any web component such as a servlet can be accessed.

The only modification we need to make is in the web.xml file. We have listed the entire web.xml file below with the security modifications highlighted:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>AddCustomerServlet</servlet-name>
<servlet-class>ejb30.AddCustomerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddCustomerServlet</servlet-name>
<url-pattern>/addCustomer</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>bank service</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>bankemployee</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>
</web-app>

To run the servlet enter http://localhost:8080/BankService/index.html in a browser. The address http://localhost:8080 is the URL for web applications as shown by GlassFish on startup. If GlassFish provides a port other than 8080 then modify the URL accordingly. As soon as the URL has been entered, the web container will present a login form requesting the username and password. If a user belonging to the bankemployee role has been correctly entered we proceed to the servlet, otherwise the web container will generate an error page.{/literal}

Summary

We have seen that there are two aspects to Java EE container security: authentication and authorization. Authentication is the process of verifying that users are who they claim to be. Authorization is the process of restricting operations to specific users or categories of users. The Java EE security model deals with principals, roles and realms. A principal is an entity that we wish to authenticate. A role is a logical grouping of principals. A realm is the scope over which a common security policy applies.

Defining principals, roles, and realms is application server specific; we saw how to do this for GlassFish.

We saw how to configure the GlassFish application client container to authenticate an EJB application client.

There are two kinds of EJB authorization mechanisms: declarative and programmatic. With declarative authorization, security is managed by the container. The EJB specifies the desired access control using annotations. With programmatic authorization, the EJB controls authorization.

Finally we had a brief look at web-tier authorization and authentication.

EJB 3 Developer Guide Enterprise JavaBean 3 - a Practical Book and eBook Guide for developers and architects using the EJB Standard.
Published: May 2008
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

About the Author :


Michael Sikora

Michael Sikora is an enterprise architect with a background in the Unified Process and JEE. He has a particular interest in object oriented and database technology. He has worked for many large UK clients such as ICL Fujitsu, Mercury Communications, Vodafone and BUPA. He has used Java since 2000. Before that he spent a decade designing and developing database and datawarehouse systems. He has experience of Oracle, PL/SQL and C. In the 1980s he worked for Shell developing exploration software. He graduated in Mathematics from Essex University and has Masters degrees from London University and Imperial College.

Michael currently resides in London, England and enjoys mountaineering and still hopes to complete the seven summits. His website is www.ejbconsultants.com whilst he also maintains a blog at http://msikora.typepad.com

Contact Michael Sikora

Books From Packt

EJB 3 Developer Guide
EJB 3 Developer Guide

RESTful PHP Web Services
RESTful PHP Web Services

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Object-Oriented JavaScript
Object-Oriented JavaScript

JBoss Tools 3 Developers Guide
JBoss Tools 3 Developers Guide

SOA Governance
SOA Governance

Django 1.0 Website Development
Django 1.0 Website Development

Oracle SOA Suite Developer's Guide
Oracle SOA Suite Developer's Guide


 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software