Enterprise Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Enterprise Java Technologies Tech Tips for September 29, 2007. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java Platform, Enterprise Edition (Java EE). This issue covers: * Using Type Substitution With Web Services * Improving JSF Security Configuration With Secured Managed Beans These tips were developed using an open source reference implementation of Java EE 5 called GlassFish, and the open source NetBeans IDE 5.5.1. You can download GlassFish from the GlassFish Community Downloads page http://java.sun.com/javaee/glassfish/getit.jsp. You can download the NetBeans IDE 5.5.1 from the NetBeans page http://www.netbeans.org/. You can view this issue of the Tech Tips on the Web at http://java.sun.com/mailers/techtips/enterprise/2007/TechTips_Sept07.html You can download the sample archive for the tip Using Type Substitution With Web Services at: http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007TypeSub.zip. You can download the sample archive for the tip Improving JSF Security Configuration With Secured Managed Beans at: http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007FacesSec.zip. Any use of this code and/or information below is subject to the license terms at http://developers.sun.com/dispatcher.jsp?uid=6910008. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING TYPE SUBSTITUTION WITH WEB SERVICES By Doug Kohlert Java Architecture for XML Binding (JAXB) 2.1 http://jcp.org/en/jsr/detail?id=222 introduced a new annotation, @XmlSeeAlso, that you can use to make JAXB aware of additional types. Java API for XML-Based Web Services (JAX-WS) 2.1 http://jcp.org/en/jsr/detail?id=224 also uses the @XmlSeeAlso annotation to allow use of abstract classes in a service endpoint interface (SEI). JAX-WS 2.1 allows you to specify the @XmlSeeAlso annotation on a SEI. JAX-WS reads this annotation at runtime making sure to pass all of the classes referenced by the annotation to JAXB via the JAXBContext. The use of the @XmlSeeAlso annotation in JAXB and JAX-WS enables support for type substitution, a subclassing concept that complements inheritance. This tip will show you how to develop a simple web service that uses type substitution as well a client that consumes the web service. You'll see how to build the web service from a Java class and from a WSDL file. A sample application http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007TypeSub.zip accompanies this tip. The code examples in the tip are taken from the source code of the sample application. Using Type Substitution in a Web Service Suppose you want to build a web service that manages the inventory for a store that sells wakeboards and related equipment. Wakeboards are short boards made of buoyant material that are used to ride over the surface of a body of water, typically behind a boat or with a cable-skiing apparatus. For simplicity, let's assume that the store sells only three types items: wakeboards, bindings, and towers for boats. You want the web service to be fairly simple to use and have a minimal amount of exposed operations. So to keep things simple, the web service uses an abstract Item class in its operations instead of using type-specific operations. The following Item class can be used to model any inventory object that you might want to expose through your web service: public abstract class Item implements Serializable { private long id; private String brand; private String name; private double price; ... } Extending the Item class, you can define the following Wakeboard, WakeboardBinding and Tower classes: public class Wakeboard extends Item { private String size; } public class WakeboardBinding extends Item { private String size; } public class Tower extends Item { private Fit fit; private String tubing; public static enum Fit { Custom, Exact, Universal }; } Because this example is about type substitution, let's make the inheritance hierarchy a little more interesting by introducing a Wearable abstract class. Wearable holds the size attribute for both the Wakeboard and WakeboardBinding classes. The Wearable class is defined as follows: public abstract class Wearable extends Item { protected String size; } And the resulting Wakeboard and WakeboardBinding classes are: public class Wakeboard extends Wearable { } public class WakeboardBinding extends Wearable { } Also, because the web service manages inventory, you'll want the inventory items to be persisted to a database using the Java Persistence API (sometimes referred to as JPA). To do this, you need to add an @Entity annotation to each of the classes that will be persisted. The only class that you probably don't want to persist is the Wearable class. You can add the @MappedSuperclass annotation to this class so that the JPA will use the attributes of this class for persisting subclasses. Next, you need to add the @Id and the @GeneratedValue(strategy = GenerationType.AUTO) annotations to the Item.Id field. As a result, the field will be used as the primary key in the database and the Id will be automatically generated if not provided. Finally, because you might add new types of Items into the system at a later time, you should add the @Inheritance(strategy=InheritanceType.JOINED) annotation to the Item class. This will store each subclass in its own database table. The final data classes look like the following: @Entity @Inheritance(strategy=InheritanceType.JOINED) public abstract class Item implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String brand; private String itemName; private double price; // Getters & setters ... } @MappedSuperclass public abstract class Wearable extends Item { protected String size; ... } @Entity public class Wakeboard extends Wearable {} @Entity public class WakeboardBinding extends Wearable {} @Entity public class Tower extends Item { private Fit fit; private String tubing; public static enum Fit { Custom, Exact, Universal }; ... } Now that you defined the data model for the application, you can now define the web service interface. Because the application manages information about wakeboard equipment, let's call the web service WakeRider and let's expose four operations in the web service: addItem, updateItem, removeIem, and getItems. Here is what the WakerRider class looks like: @WebService() public class WakeRider { ... public List getItems() {...} public boolean addItem(Item item) {...} public boolean updateItem(Item item) {...} public boolean removeItem(Item item) {...} } If you deployed this web service and then looked at the generated WSDL and schema, you would notice that only the Item type is defined -- there is no mention of Wearable, Wakeboard, WakeboardBinding, or Tower. This is because when JAX-WS introspects the WakeRider class there is no mention of the other classes. To remedy that you can use the new @XmlSeeAlso annotation and list the other classes that you want to expose through the WakeRider web service. Here is what the WakeRider class looks like with the @XmlSeeAlso annotation: @WebService() @XmlSeeAlso({Wakeboard.class, WakeboardBinding.class, Tower.class}) public class WakeRider { ... } Now when you deploy the WakeRider service and look at the generated schema, you will see types for Item, Wearable, Wakeboard, WakeboardBinding, and Tower as well as some other types used internally by JAX-WS and JAXB. Starting From WSDL You can use type substitution in a web service that is built from a WSDL file. What's particularly nice about this is that using type substitution when starting from WSDL is totally transparent. When you import a WSDL file with JAX-WS 2.1, the generated proxy class is required to have the appropriate @XmlSeeAlso annotation. For example, the imported WakeRider proxy from the web service example in the previous section would have an @XmlSeeAlso annotation like the following: @WebService(name="WakeRider", targetNamespace="http://wakerider/") @XmlSeeAlso({ObjectFactory.class}) public interface WakeRider { ... } Notice that the @XmlSeeAlso annotation in the proxy contains the ObjectFactory.class instead of listing the classes. The ObjectFactory class is a JAXB required class that provides information about all of the Java types that JAXB needs to be aware of in the given package. In this example, the ObjectFactory class will have references to the Item, Wearable, Wakeboard, WakeboardBinding and Tower classes. There is nothing that you need to do to enable type substitution when starting from WSDL. The WakeRider Client Invoking the WakeRider web service from a client is the same as invoking any other web service using JAX-WS. All you need to do is get a WakeRider proxy from the generated WakeRider web service and invoke the operations on the proxy. The sample application that accompanies this tip contains a NetBeans 5.5.1 project for a Java Platform, Standard Edition (Java SE) application named wrmanager. You can use the application to add, remove, or edit items in the WakeRider web service inventory. There is also a NetBeans 5.5.1 project for a JavaServer Faces (JSF) technology application named wrviewer. The application uses the WakeRider web service to view the current inventory. Both of these client applications contain code similar to the following for invoking an operation on the WakeRider web service: WakeRiderService service = new WakeRiderService(); port = service.getWakeRiderPort(); List items = port.getItems(); for (Item item : items) { if (item instanceof Wakeboard) { ... } else if (Item instance of WakeboardBinding) { ... } else if (Item instance of Tower) { ... } } Running the Sample Code The sample code for this tip is available as three NetBeans projects: o wrservice. Defines the WakeRider endpoint. o wrviewer. A JSF page for viewing the WakeRider inventory o wrmanager. A Java SE application for adding, removing, and editing items in the WakeRider inventory. You can build and run the sample code using the NetBeans 5.5.1 IDE as follows: 1. If you haven't already done so, download and install the NetBeans 5.5.1 IDE (http://www.netbeans.info/downloads/index.php). 2. If you haven't already done so, download and install GlassFish V2 RC 4 or later (https://glassfish.dev.java.net/public/downloadsindex.html). 3. Download the sample application for the tip http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007TypeSub.zip and extract its contents. You should now see the newly extracted directory as /wakerider, where is the directory where you installed the sample application. For example, if you extracted the contents to C:\ on a Windows machine, then your newly created directory should be at C:\wakerider. The wakerider directory contains one directory for each of the NetBeans projects: wrservice, wrviewer, and wrmanager. 4. Start the NetBeans IDE. Run NetBeans with JDK 5.0. You can also use JDK 6, however in that case, you will also need to follow the instructions in "Running on top of JDK 6" https://jax-ws.dev.java.net/nonav/2.1.2rc1/docs/ReleaseNotes.html#Running_on_top_of_JDK_6. 5. Add GlassFish V2 to the NetBeans Application Servers as follows: o Right click on Servers node in the Runtime window. o Select Add Server. o Leave the Server as Sun Java System Application Server. o Click the Next button. o Click the Browse button and browse to the location that you installed GlassFish V2. o Click the Choose button. o Click the Next button. o Set the Admin Password to the default, adminadmin, unless you chose a different password for GlassFish. o Click the Finish button. 6. Open the wrservice project as follows: o Select Open Project from the File menu. o Browse to the wrservice directory from the sample application download. o Click the Open Project Folder button. o If you are alerted to a "Missing Server Problem", resolve it by right clicking on the wrservice node in the Projects window and selecting Resolve Missing Server Problem. Then select Sun Java System Application Server. 7. Deploy the wrservice project as follows: o Right click the wrservice node in the Projects window. o Select Deploy Project. 8. Open the wrviewer project as follows: o Select Open Project from the File menu. o Browse to the wrviewer directory from the sample application download. o Click the Open Project Folder button. o You may need to resolve a missing server problem as described in step 6. 9. Run wrviewer as follows: o Right click on the wrviewer node in the Projects window. o Select Run Project. This should open a window in your web browser that displays the current WakeRider inventory. The inventory should be empty the first time you run wrviewer. http://java.sun.com/mailers/techtips/enterprise/2007/images/wake_rider_inv1.jpg 10. Open the wrmanager project as follows: o Select Open Project from the File menu. o Browse to the wrmanager directory from the sample application download. o Click the Open Project Folder button. 11. Run wrmanager as follows: o Right client on the wrmanager node in the Projects window. o Select Run Project. This should open the WakeRider Inventory Manager application. http://java.sun.com/mailers/techtips/enterprise/2007/images/wake_rider_inv2.jpg 12. Add, delete, edit, or view inventory items as follows: o To add an item, click the Add button in the WakeRider Inventory Manager application, fill in the Add Item dialog and click the OK button. o To edit an item, select the item in the appropriate inventory window in the WakeRider Inventory Manager application and click the Edit button. Modify the contents of the Edit Item dialog and click the OK button. o To delete an item, select the item in the appropriate inventory window in WakeRider Inventory Manager application and click the Remove button. o To view current inventory items in the wrmanager application, view or refresh the wrviewer page in your browser. http://java.sun.com/mailers/techtips/enterprise/2007/images/wake_rider_inv3.jpg About the Author Doug Kohlert is a senior staff engineer in the Web Technologies and Standards division of Sun Microsystems where he is the specification lead for JAX-WS. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMPROVING JSF SECURITY CONFIGURATION WITH SECURED MANAGED BEANS By Vinicius Senger Java EE allows you to protect web resources through declarative security, but this approach doesn’t allow you to protect local beans used by servlets and JavaServer Pages (JSPs). Also, although you can protect JavaServer Faces technology (JSF) pages using declarative security, this is often not sufficient. This tip will show you a way to extend JSF security configuration beyond web pages using managed bean methods. Introduction Java EE allows you to protect web pages and other web resources such as files, directories, and servlets through declarative security. In this approach you declare in a web.xml file specific web resources and the security roles that can access those resources. For example, based on the following declarations in a web.xml file, only authenticated users who are assigned the admin security role can access the secured resources identified by the URL pattern /members.jsf: Sample members /members.jsf GET POST admin admin Notice that you identify the resources you want to protect by specifying their URLs in a element. Unfortunately, because local beans used by servlets and JavaServer Pages (JSP) cannot be mapped to a element, you can't use declarative security to protect local beans. Also, although you can protect JSF pages using declarative security, this is often not sufficient. For example, you might want a JSF application to present the same page to users with different roles, but only allow some of those roles to perform specific operations. For instance, you might allow users with all of those roles to read and update data, but allow users with specific roles to create and delete data. In that case, you need a way to extend JSF security beyond web pages. Additionally, declarative security doesn’t check roles during the request processing commonly used by MVC frameworks and JSF. As a result, a managed bean can return any view id even if it's for a protected resource. This can potentially expose protected resources to a role that should not have access to them. One solution is to use JBoss Seam Web Beans or JSR 299: Web Beans (http://www.jcp.org/en/jsr/detail?id=299). Web Beans allow you to configure page security, component security, and even Java Persistence Architecture entity security. However, many companies are adopting simpler security solutions without Seam, Spring, EJB, or security-specific frameworks. The technique covered in this tip demonstrates a simple approach that extends JSF security using annotations in managed beans methods. A sample application http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007FacesSec.zip accompanies this tip. The code examples in the tip are taken from the source code of the sample application. Declare the Extended JSF ActionListener and NavigationHandler To provide managed bean method protection you need to declare the extended JSF ActionListener and NavigationHandler. These custom classes analyze each user action and check for authentication and authorization. To enable the classes, you declare the following elements inside the faces-config.xml file: br.com.globalcode.jsf.security.SecureActionListener br.com.globalcode.jsf.security.SecureNavigationHandler SecureActionListener intercepts calls to managed bean methods and checks for annotated method permissions. NavigationHandler forwards the user to a requested view if the user has the required credentials and roles. For example, the following code renders a JSF page with a View button and a Delete button. When the user clicks on the Delete button, a call is made to the CustomerCRUD.delete method. The method includes an annotation that declares a required role for the method. public class CustomerCRUD { public String view() { return "view-customer"; } @SecurityRoles("customer-admin-adv, root") public String delete() { System.out.println("I'm a protected method!"); return "delete-customer"; } ... SecureActionListener intercepts calls to CustomerCRUD.delete and checks for the customer-admin-adv and root permissions. NavigationHandler forwards the user to a requested view if the user has the required credentials and roles. Set Up User Object Providers By adding a context parameter into web.xml, you can set up different user object providers, as follows: o ContainerUserProvider: Integrate with container/declarative security. o SessionUserProvider: Look up Http session for object named "user". o Your Provider: Implement the UserProvider interface: jsf-security-user-provider YourClassImplementsUserProvider Set Up the ContainerUserProvider The web container provider approach is integrated with declarative security, so it can be used with applications that already use declarative security. Add the following context parameter to set up the default container user provider: jsf-security-user-provider br.com.globalcode.jsf.security.usersession.ContainerUserProvider Here is what the default web container user provider class looks like: public class ContainerUserProvider implements UserProvider { ContainerUser user = new ContainerUser(); public User getUser() { if(user.getLoginName()==null || user.getLoginName().equals("")) { return null; } else { return user; } } ContainerUserProvider references the ContainerUser class. Here's what the ContainerUser class looks like (some of the code lines are cut to fit the width of the page): public class ContainerUser implements User { public String getLoginName() { if(FacesContext.getCurrentInstance().getExternalContext(). getUserPrincipal()==null) return null; else return FacesContext.getCurrentInstance(). getExternalContext().getUserPrincipal().toString(); } public boolean isUserInRole(String roleName) { return FacesContext.getCurrentInstance().getExternalContext(). isUserInRole(roleName); } Using a SessionUser Provider If your solution uses a custom security authentication and authorization process, you can provide a user class adapter that implements the given user interface and bind a user object instance into the HTTP Session with the key name "user". This approach works well for legacy Java EE or J2EE applications that don't use declarative security. Follow these steps to set up your application to use a SessionUserProvider: 1. Add the following context parameter to the web.xml file to set up the user provider to lookup the HTTP Session for the "user" object: jsf-security-user-provider br.com.globalcode.jsf.security.usersession.SessionUserProvider 2. Create your User class adapter implementation: package model; public class MyUser implements br.com.globalcode.jsf.security.User { //Your user instance object public String getLoginName() { //your user bridge return "me"; } public boolean isUserInRole(String roleName) { //your user roles bridge return true; } } 3. Provide page login with a navigation case called login: //Login page

login /login.xhtml 4. Write a login managed bean that checks the user credentials and puts (or not) the user object into the HTTP session. public class LoginMB { private String userName; private String password; @SecurityLogin public void login() { //Your login process here... MyUser user = new MyUser(); HttpSession session = (HttpSession) FacesContext.getCurrentInstance(). getExternalContext().getSession(false); session.setAttribute("user", user); } } Running the Sample Code A sample package http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007FacesSec.zip accompanies this tip. This sample runs with a SessionUserProvider and has a very simple user and login page. To install and run the sample: 1. Download the sample package and extract its contents. You should now see a newly extracted directory /facesannotations-glassfish, where is the directory where you installed the sample package. For example, if you extracted the contents to C:\ on a Windows machine, then your newly created directory should be at C:\facesannotations-glassfish. Notice that the faces-config.xml file in the expanded sample package contains the declarations for the SecureActionListener and SecureNavigationHandler. 2. Start the NetBeans IDE. 3. Open the facesannotations-glassfish project as follows: o Select Open Project from the File menu. o Browse to the facesannotations-glassfish directory from the sample application download. o Click the Open Project Folder button. 4. Run facesannotations-glassfish as follows: o Right click on the facesannotations-glassfish node in the Projects window. o Select Run Project. o Open your browser to the following URL: http://localhost:8080/facesannotations-glassfish/index.jsf You should see a page that contains two buttons: one button invokes an unprotected method. The other button invokes a protected method. http://java.sun.com/mailers/techtips/enterprise/2007/images/jsf_sec1.jpg Click on both buttons and see what happens. You'll see that you can run the unprotected method, but the protected method requires you to have a special role. http://java.sun.com/mailers/techtips/enterprise/2007/images/jsf_sec2.jpg About the Author Vinicius Senger is a performance researcher, Java EE architect, and instructor. He started his career at Sun Microsystems and Oracle as independent consultant and official instructor, and later founded Globalcode, a leading Java-related training company in Brazil. Vinicius is a member of the JSF 2.0 http://www.jcp.org/en/jsr/detail?id=314 Expert Group, the leader of the Global Education and Learning Community https://edu-gelc.dev.java.net/, a NetBeans Dream Team Member, and project leader of JAREF http://globalcode.dev.java.net, an educational and research framework. He is also a Sun Certified Enterprise Architect and Programmer P1. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DEVELOPER ASSISTANCE Need programming advice on Java EE? Try Developer Expert Assistance http://developers.sun.com/services/expertassistance/ . . . . . . . . . . . . . . . . . . . . . . . Please read our Terms of Use and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://developers.sun.com/dispatcher.jsp?uid=6910008 PRIVACY STATEMENT: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your e-mail preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe. * FEEDBACK Comments? Send your feedback on the Enterprise Java Technologies Tech Tips to: Enterprise_TechTips@sun.com * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Core Java Technologies Tech Tips. Get tips on using core Java technologies and APIs, such as those in the Java 2 Platform, Standard Edition (J2SE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the Sun Developer Network - Subscriptions page, https://softwarereg.sun.com/registration/developer/en_US/subscriptions, choose the newsletters you want to subscribe to and click "Submit". - To unsubscribe, go to the Subscriptions page, https://softwarereg.sun.com/registration/developer/en_US/subscriptions, uncheck the appropriate checkbox, and click "Submit". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Enterprise Java Technologies Tech Tips archives at: http://java.sun.com/developer/EJTechTips/index.html - COPYRIGHT Copyright 2007 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Enterprise Java Technologies Tech Tips September 29, 2007 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.