Wednesday, May 09, 2007

OpenJDK and JavaFX

Java is now completely open source. The JDK source code is now available through the OpenJDK project. According to the marketing manager of the OpenJDK project Rich Sands, Developers can,
... learn how the JDK is put together, fix that bug that's been 
driving you nuts, join the conversations in the mailing lists,
start or participate in projects to improve the implementation.
It's good to see that the governing body consists of not just Sun employees.

On a related note, Sun has announced the new JavaFX product family. Following Adobe's Flex, and Microsoft's Silverlight, JavaFX script is another product for building rich internet applicaitons. JavaFX Mobile is a complete mobile operating and application environment built around Java and Linux. JavaFX Mobile includes support for Java ME applications and other standard Java APIs.

Monday, May 07, 2007

Securing EJB 3.0 Beans

The Java EE 5 Security services are provided by the container and can be implemented using declarative or programmatic techniques. In addition to declarative and programmatic ways to implement security (in J2EE), Java EE 5 supports the use of metadata annotations for security. This post will describe how to secure EJB 3.0 beans. The post consists of a simple EJB, with a web client. In order to run the example, follow these steps.
Create Users in Glassfish
  1. Go to Configuration->Security->Realms->file in the Glassfish admin console.
  2. In the file realm, click on manage users.
  3. Add new users by clicking on add there.

The EJB Component
  1. Start with a Simple Java project in Eclipse.
  2. Remote Interface
    package ejb;

    import javax.ejb.Remote;

    @Remote
    public interface DABean {
    public String create();

    public String read();

    public String update();

    public String delete();
    }
    ejb/DABean.java
  3. The Bean:
    package ejb;

    import javax.annotation.security.DeclareRoles;
    import javax.annotation.security.RolesAllowed;
    import javax.ejb.Stateless;

    @Stateless (mappedName = "ejb/secureEJB")
    @DeclareRoles({"emp","guest"})

    public class SecureEJB implements DABean {

    @RolesAllowed({"emp","guest"})
    public String create() {
    return "create";
    }

    @RolesAllowed({"emp","guest"})
    public String read() {
    return "read";
    }

    @RolesAllowed("emp")
    public String update() {
    return "update";
    }

    @RolesAllowed("emp")
    public String delete() {
    return "delete";
    }

    }
    ejb/SecureEJB.java
    • The declaredRoles and RolesAllowed annotations take a string array as a parameter.
  4. Deployment descriptor:
    <?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>
    <security-role-mapping>
    <role-name>guest</role-name>
    <group-name>guest</group-name>
    </security-role-mapping>

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

    <enterprise-beans>
    <unique-id>0</unique-id>
    <ejb>
    <ejb-name>SecureEJB</ejb-name>
    <jndi-name>ejb/secureEJB</jndi-name>
    <gen-classes />
    </ejb>
    </enterprise-beans>
    </sun-ejb-jar>
    META-INF/sun-ejb-jar.xml

The Web Client
For a little bit more detail explanation on the Web Application, see the previous post Securing Java EE 5 Web Applications
  1. The EJB Client Jar file: When you deploy the EJB application in Glassfish, it creates a corresponding EJB Client jar file for the EJB component, which can be used in the clients. The file will created in the following directory.
    GLASSFISH_HOME\domains\DOMAIN_NAME/generated\xml/j2ee-modules/APPLICATION_NAME
  2. Selection page
    <html>
    <body>
    <h1>Home Page</h1>
    Anyone can view this page.

    <form action="securityServlet"><select name="method">
    <option value="create">create</option>
    <option value="read">read</option>
    <option value="update">update</option>
    <option value="delete">delete</option>
    </select> <input type="submit" name="submit" /></form>
    </body>
    </html>
    index.jsp
  3. Servlet
    package servlets;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.annotation.security.DeclareRoles;
    import javax.ejb.EJB;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import ejb.DABean;

    @DeclareRoles("emp")
    public class SecurityServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

    @EJB(name = "timerBean", mappedName = "corbaname:iiop:localhost:3700#ejb/secureEJB")
    private DABean daBean;

    public SecurityServlet() {
    super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    String method = request.getParameter("method");
    try {
    String result = "";
    if (method.equals("create")) {
    result = daBean.create();
    }
    if (method.equals("read")) {
    result = daBean.read();
    }

    if (method.equals("update")) {
    result = daBean.update();
    }

    if (method.equals("delete")) {
    result = daBean.delete();
    }

    out.println(request.getUserPrincipal() + " is an Authorized User");
    } catch (Exception e) {
    e.printStackTrace();
    out.println(request.getUserPrincipal() + " is not an Authorized to see this page.");
    }
    }
    }
    SecurityServlet.java
  4. Deployment descriptor
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5" 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">
    <display-name>Java5Security</display-name>

    <servlet>
    <description></description>
    <display-name>SecurityServlet</display-name>
    <servlet-name>SecurityServlet</servlet-name>
    <servlet-class>servlets.SecurityServlet</servlet-class>
    <security-role-ref>
    <role-name>emp</role-name>
    <role-link>emp</role-link>
    </security-role-ref>
    </servlet>
    <servlet-mapping>
    <servlet-name>SecurityServlet</servlet-name>
    <url-pattern>/securityServlet</url-pattern>
    </servlet-mapping>


    <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>file</realm-name>
    <form-login-config>
    <form-login-page>/login.jsp</form-login-page>
    <form-error-page>/error.jsp</form-error-page>
    </form-login-config>
    </login-config>

    <security-constraint>
    <web-resource-collection>
    <web-resource-name>Protected Area</web-resource-name>
    <url-pattern>/*</url-pattern>
    <http-method>PUT</http-method>
    <http-method>DELETE</http-method>
    <http-method>GET</http-method>
    <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
    <role-name>guest</role-name>
    <role-name>emp</role-name>
    </auth-constraint>
    </security-constraint>

    <security-constraint>
    <web-resource-collection>
    <web-resource-name>Protected Area</web-resource-name>
    <url-pattern>/secure/*</url-pattern>
    <http-method>PUT</http-method>
    <http-method>DELETE</http-method>
    <http-method>GET</http-method>
    <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
    <role-name>emp</role-name>
    </auth-constraint>
    </security-constraint>
    <!-- Security roles referenced by this web application -->
    <security-role>
    <role-name>guest</role-name>
    </security-role>
    <security-role>
    <role-name>emp</role-name>
    </security-role>

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    </web-app>
    web.xml
  5. Glassfish Deployment descriptor
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
    <sun-web-app>
    <context-root>/Java5Security</context-root>
    <security-role-mapping>
    <role-name>guest</role-name>
    <group-name>guest</group-name>
    </security-role-mapping>
    <security-role-mapping>
    <role-name>emp</role-name>
    <group-name>employee</group-name>

    </security-role-mapping>
    </sun-web-app>
    sun-web.xml
Environment: This example was run on Glassfish V2 Build 41 (Glassfish V2 Beta 2).

Tuesday, May 01, 2007

Java EE 5 on WebSphere

While the next version of WAS (v7.0) with Java EE 5, is yet to be released, you can now test some of the features such as EJB 3.0 with WebSphere software early programs. The new features are available as feature packs for the current WebSphere Application Server (v6.1).

Friday, April 13, 2007

Customize Acegi SecurityContext

This post describes how to modify the User details stored in the Acegi Security Context.

Acegi Security uses the SecurityContextHolder object to store details of the current security context of the application. The SecurityContext holds the details of the authenticated principal in an Authentication object. By default the SecurityContextHolder uses a ThreadLocal to store these details, so that they will be available all methods in the current thread of execution.In order to obtain the Principal, you would use the following line
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Usually, the principal is an object of the type UserDetails. The UserDetails object is sort of an adapter between the security database and Acegi SecurityContextHolder. Acegi uses a UserDetailsSevice to build the UserDetails object. In order to modify the data stored Security Context of Acegi, you either have to
  • setContext() method of the SecurityContextHolder. The SecurityContext object can be set to hold an Authentication object. This will mean that you have to add some additional security code to your application code.
  • Implement the UserDetailsService and UserDetails interfaces. This way you can keep security code seperated from the application code.
This post will describe how to implement UserDetailsService and UserDetails Objects. Here's what to do to implement a UserDetailsService.
  1. Start with the implementing acegi security posts part 1 and part 2.
  2. Create the UserDetailsService:
    package authentication;

    import java.util.HashMap;
    import java.util.Map;

    import org.acegisecurity.GrantedAuthority;
    import org.acegisecurity.userdetails.UserDetails;
    import org.acegisecurity.userdetails.UserDetailsService;
    import org.acegisecurity.userdetails.UsernameNotFoundException;
    import org.springframework.dao.DataAccessException;

    public class MyUserDetailsService implements UserDetailsService {

    private Map users = init();

    private Map init() {
    Map tUsers = new HashMap();

    tUsers.put("scott", new User("scott", "tiger", "ROLE_USER"));
    tUsers.put("harry", new User("harry", "potter", "ROLE_ADMIN"));
    tUsers.put("frodo", new User("frodo", "baggins", "ROLE_USER"));

    return tUsers;
    }

    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException, DataAccessException {
    User user = (User) users.get(s);
    GrantedAuthority authority = new MyGrantedAuthority(user.getRole());
    UserDetails userDetails = new MyUserDetails(new GrantedAuthority[] { authority }, user.getUserId(), user.getPassword(), "Additional Data");
    return userDetails;
    }

    }
    MyUserDetailsService.java

    Notice that all the user data is defined in this class. The UserDetailsService has to return a UserDetails object using the User name.
  3. Create the UserDetails class:
    package authentication;

    import org.acegisecurity.GrantedAuthority;
    import org.acegisecurity.userdetails.UserDetails;

    public class MyUserDetails implements UserDetails {


    private GrantedAuthority[] authorities = null;
    private String password = null;
    private String username = null;
    private String additionalData = null;


    public MyUserDetails(GrantedAuthority[] authorities, String password, String username, String additionalData) {
    super();
    this.authorities = authorities;
    this.password = password;
    this.username = username;
    this.additionalData = additionalData;
    }

    public GrantedAuthority[] getAuthorities() {
    return authorities;
    }

    public String getPassword() {
    return password;
    }

    public String getUsername() {
    return username;
    }

    public boolean isAccountNonExpired() {
    return true;
    }

    public boolean isAccountNonLocked() {
    return true;
    }

    public boolean isCredentialsNonExpired() {
    return true;
    }

    public boolean isEnabled() {
    return true;
    }
    }
    MyUserDetails.java
  4. The User class:
    package authentication;

    public class User {

    private String userId;

    private String password;

    private String role;

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public String getRole() {
    return role;
    }

    public void setRole(String role) {
    this.role = role;
    }

    public String getUserId() {
    return userId;
    }

    public void setUserId(String userId) {
    this.userId = userId;
    }

    public User(String userId, String password, String role) {
    super();
    this.userId = userId;
    this.password = password;
    this.role = role;
    }

    }
    User.java
  5. Modify the applicationContext.xml file: Modify the userDetailsService bean in the application context to as shown below
    <bean id="userDetailsService" class="authentication.MyUserDetailsService">
    </bean>
  6. The GrantedAuthority class: The MyGrantedAuthority class is typically used to store application roles.
    package authentication;

    import org.acegisecurity.GrantedAuthority;

    public class MyGrantedAuthority implements GrantedAuthority {

    private String authority = null;

    public MyGrantedAuthority(String authority) {
    this.authority = authority;
    }
    public String getAuthority() {
    return authority;
    }

    }
    MyGrantedAuthority.java

Tuesday, April 10, 2007

Native Queries with Hibernate Annotations

Hibernate EntityManager implements the programming interfaces and lifecycle rules as defined by the EJB3 persistence specification. Together with Hibernate Annotations, this wrapper implements a complete (and standalone) EJB3 persistence solution on top of the mature Hibernate core. In this post I will describe how map native queries (plain SQL) using Hibernate Annotations. Hibernate Annotations supports the use of Native queries through the @NamedNativeQuery and the @SqlResultSetMapping annotations.
  • @NamedNativeQuery: Specifies a native SQL named query.
  • @SqlResultSetMapping: Used to specify the mapping of the result of a native SQL query.
You will not need any EJB container support. At a minimum, you will need Hibernate core and Hibernate Annotations. The entire list of required Jar files is provided at the end.
  1. The Hibernate Configuration File: Nothing new here. Except that there are no mappings defined. I used programmatic declaration of mapping for this example as shown in the following steps.
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>
    <session-factory>
    <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
    <property name="connection.url">jdbc:oracle:thin:@localhost:1521/orcl</property>
    <property name="connection.username">scott</property>
    <property name="connection.password">tiger</property>
    <property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
    <property name="hibernate.current_session_context_class">thread</property>
    </session-factory>
    </hibernate-configuration>
    hibernate.cfg.xml
  2. The Entity class:
    package data;

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.EntityResult;
    import javax.persistence.FieldResult;
    import javax.persistence.Id;
    import javax.persistence.NamedNativeQuery;
    import javax.persistence.SqlResultSetMapping;

    @Entity
    @SqlResultSetMapping(name = "implicit", entities = @EntityResult(entityClass = data.Employee.class))
    @NamedNativeQuery(name = "implicitSample", query = "select e.empno empNumber, e.ename empName, e.job empJob, e.sal empSalary, salg.grade empGrade from emp e, salgrade salg where e.sal between salg.losal and salg.HISAL", resultSetMapping = "implicit")
    //@SqlResultSetMapping(name = "explicit", entities = { @EntityResult(entityClass = data.Employee.class, fields = {
    // @FieldResult(name = "empNumber", column = "empno"),
    // @FieldResult(name = "empName", column = "ename"),
    // @FieldResult(name = "empJob", column = "job"),
    // @FieldResult(name = "empSalary", column = "sal"),
    // @FieldResult(name = "empGrade", column = "grade") }) })
    //@NamedNativeQuery(name = "implicitSample",
    // query = "select e.empno empno, e.ename ename, e.job job, e.sal sal, salg.grade grade from emp e, salgrade salg where e.sal between salg.losal and salg.HISAL", resultSetMapping = "explicit")
    public class Employee {

    private String empNumber;

    private String empName;

    private String empJob;

    private Double empSalary;

    private int empGrade;

    @Column
    @Id
    public int getEmpGrade() {
    return empGrade;
    }

    public void setEmpGrade(int empGrade) {
    this.empGrade = empGrade;
    }

    @Column
    public String getEmpJob() {
    return empJob;
    }

    public void setEmpJob(String empJob) {
    this.empJob = empJob;
    }

    @Column
    public String getEmpName() {
    return empName;
    }

    public void setEmpName(String empName) {
    this.empName = empName;
    }

    @Column
    public String getEmpNumber() {
    return empNumber;
    }

    public void setEmpNumber(String empNumber) {
    this.empNumber = empNumber;
    }

    @Column
    public Double getEmpSalary() {
    return empSalary;
    }

    public void setEmpSalary(Double empSalary) {
    this.empSalary = empSalary;
    }

    }
    Employee.java
    • The implitic mapping (the uncommented @NamedNativeQuery and @SqlResultSetMapping declarations are used for implicitly mapping the ResultSet to the entity class. Note that the SQL column names match the field names in the class. If the names do not match then you can set the name attribute of the @Column annotation to the column name in the query.
    • The commented @NamedNativeQuery and the @SqlResultSetMapping declarations explicitly map the fields to the columns. This will come in handy when using joins and composite keys etc.
    • Note the package definitions refer to javax.persistence and not the hibernate packages. If the packages are not declared properly, you will most likely end up with some exceptions like the following
      org.hibernate.hql.ast.QuerySyntaxException: Employee is not mapped.
      While there are other causes for this exception, the package declarations did cause a little trouble for me.
  3. The Client:
    import java.util.List;

    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.AnnotationConfiguration;
    import org.hibernate.cfg.Configuration;

    import data.Employee;

    public class Client {

    public static void main(String[] args) {
    Configuration config = new AnnotationConfiguration().addAnnotatedClass(Employee.class).configure();

    SessionFactory sessionFactory = config.buildSessionFactory();
    Session session = sessionFactory.getCurrentSession();

    List result = null;
    try {
    session.beginTransaction();

    result = session.getNamedQuery("implicitSample").list();
    System.out.println("Result size : " + result.size());
    session.getTransaction().commit();
    } catch (Exception e) {
    e.printStackTrace();
    }
    System.out.println(result.size());
    }

    }
    Client.java
  4. Jar Files: The following jar files need to be included in the classpath
    hibernate3.jar
    commons-collections-2.1.1.jar
    antlr-2.7.6.jar
    commons-logging-1.0.4.jar
    hibernate-annotations.jar
    ejb3-persistence.jar
    hibernate-commons-annotations.jar
    dom4j-1.6.1.jar
    ojdbc14.jar
    jta.jar
    log4j-1.2.11.jar
    xerces-2.6.2.jar
    xml-apis.jar
    cglib-2.1.3.jar
    asm.jar
    All the Jar files will be available in the hibernate download. The hibernate-annotations.jar file is available in the hibernate annotations download.
This example was tested on Java 5 Update 9 with Hibernate version 3.2.3 and Hibernate Annotations Version: 3.3.0.GA.

Tuesday, April 03, 2007

Using JSON from Java

JSON (JavaScript Object Notation) is a lightweight data-interchange format based on a subset of the JavaScript Programming Language. JSON object structure is built on two structures:
  • A collection of name/value pairs: An associative array in Javascript which is analogous to the Java Map.
  • An ordered list of values: Analogous to a Java Array.
The rest of the datatypes (string, number, true/false etc.) have straight forward mappings to the Java datatypes.

Producing JSON from Java


Since JSON objects can be declared literally in JavaScript, it is possible to create these literals from Java classes. There are a couple of ways to achieve this
  • Using JSONObject: The source code for JSONObject and the supporting classes is available at http://www.json.org/java/index.html. Using this you can create JSONObject from a Java class as shown below
      JSONObject json = new JSONObject();
    json.put("name1",value1);
    json.put("name2",value2);
    The JSONObject class will appear as a Javascript Associate array which looks like this
     {"name1": "value1",
    "name2": "value2"}
  • Using JSON-lib: This is a Java library (available on sourceforge) that provides utility methods to convert different Java objects into JSONObject. This method is used in the example shown below.
The following Java class and JSP demonstrate how to use JSON with Java
  1. The Java class:
    package beans;

    import java.util.Map;
    import java.util.TreeMap;

    import net.sf.json.JSONObject;

    public class MapHolder {
    private Map map;

    public Map getMap() {
    return map;
    }

    public void setMap(Map map) {
    this.map = map;
    }

    public JSONObject getJsonObject() {
    JSONObject obj = JSONObject.fromMap(map);
    return obj;
    }

    public MapHolder() {
    map = new TreeMap();
    map.put("name1", "value1");
    map.put("name2", "value2");

    }
    }
    MapHolder.java
    • The getJsonObject() method uses JSON-lib to convert the Map to a JSONObject.
  2. The JSP:
    <%@page import="beans.MapHolder,net.sf.json.JSONObject"%>
    <html>
    <head>
    <title>Title</title>
    <script>
    function getInfo(o)
    {
    var obj=o;
    document.getElementById('test').innerHTML=obj.name1;
    }
    </script>
    </head>
    <body>
    <%
    JSONObject obj = (new MapHolder()).getJsonObject();
    %>
    <input type="button" value="submit" onclick='getInfo(<%=obj%>)' />

    <div id="test"></div>
    </body>
    </html>
    index.jsp
    • Note the "obj.name1" in the script to access the element from the associative array.

  3. The JAR files: In order to use JSON-lib, you will need to include the following jars in your classpath

Friday, March 30, 2007

Reverse Ajax with Direct Web Remoting (DWR)

Direct Web Remoting (DWR), is an open source Java library that can be used to implement Ajax in Java web applications with minimal Javascript coding. Using DWR, we can invoke server-side Java methods from Javascript in the browser. DWR 2.0 introduces a new feature, dubbed "Reverse Ajax", using which server-side Java can "push" updates to the browser. In this post, I tried to use a simplistic web application that will demonstrate the use of DWR for "Reverse Ajax".
In this example, I use a servlet that will be pushing information to the browser clients. Here is how to implement the example.
  1. Download DWR 2.0 from here, dwr.jar file has to be included in the classpath.
  2. Create the Service: This service generates messages which will be written to the browser. Here is the code for the Service.
    package utils;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;

    import org.directwebremoting.ServerContext;
    import org.directwebremoting.proxy.dwr.Util;

    public class Service {

    private int count = 0;

    public void update(ServerContext wctx) {
    List<Data> messages = new ArrayList<Data>();
    messages.add(new Data("testing" + count++));

    // Collection sessions = wctx.getAllScriptSessions();
    Collection sessions = wctx.getScriptSessionsByPage("/ReverseAjax/index.html");
    Util utilAll = new Util(sessions);
    utilAll.addOptions("updates", messages, "value");
    }
    }
    Service.java
  3. Create the Message Container: The message container is a simple Java bean that holds the message.
    package utils;

    public class Data {
    private String value;

    public Data() {
    }

    public Data(String value) {
    this.value = value;
    }

    public String getValue() {
    return value;
    }

    public void setValue(String value) {
    this.value = value;
    }
    }
    Data.java
  4. Create the Servlet: Here is the code for the Servlet.
    package servlets;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.directwebremoting.ServerContext;
    import org.directwebremoting.ServerContextFactory;

    import utils.Service;

    public class TestServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Service service = new Service();
    ServerContext wctx = ServerContextFactory.get(this.getServletContext());
    for (int i = 0; i < 10; i++) {
    service.update(wctx);
    try {
    Thread.sleep(1000);
    }
    catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    PrintWriter writer = response.getWriter();
    writer.println("Done");
    writer.close();

    }}
    TestServlet.java
    • The ServerContext is used by DWR to get information of the clients that have open sessions on the server.
  5. The Web Page: This is the code for the Web Page (index.html).
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>index</title>
    <script type='text/javascript' src='dwr/engine.js'></script>
    <script type='text/javascript' src='dwr/interface/Service.js'></script>
    <script type='text/javascript' src='dwr/util.js'></script>
    </head>
    <body onload="dwr.engine.setActiveReverseAjax(true);">
    <ul id="updates">
    </ul>

    </body>
    </html>
    index.html
    • engine.js handles all server communications.
    • util.js helps you alter web pages with the data you got from the server.
    • The path to the scripts is relative to the root of the web content. The DWR servlet (defined in the web.xml file) will provide these scripts.
    • dwr.engine.setActiveReverseAjax(true); is used to activate Reverse Ajax
    • The id of the list is the same as the parameter set in the Service.
  6. The Web Deployment Descriptor:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" 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/web-app_2_4.xsd">
    <display-name>ReverseAjax</display-name>

    <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
    <param-name>debug</param-name>
    <param-value>true</param-value>
    </init-param>
    <init-param>
    <param-name>activeReverseAjaxEnabled</param-name>
    <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>servlets.TestServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/testServlet</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    </web-app>
    WEB-INF/web.xml
    • The DWR Servlet has to be loaded on startup
    • Setting activeReverseAjaxEnabled to true sets Reverse Ajax to be active. In this case Reverse Ajax used will be through polling or comet requests (extended http requests). If this is false, then inactive Reverse Ajax (piggybacking) will be used. In this case, the server waits for requests from the client and piggybacks the updates with the response.
  7. The DWR Configuration: The DWR configuration is defined in the dwr.xml file
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd">
    <dwr>
    <allow>
    <create creator="new" javascript="Service" scope="application">
    <param name="class" value="utils.Service" />
    </create>
    <convert converter="bean" match="utils.Data" />
    </allow>
    </dwr>
    WEB-INF/dwr.xml
    • "create" is used to define a Java object as being available to Javascript code.
    • The "javascript" attribute is the one that will be used in case of invoking the server-side methods from Javascript.
    • The converter definition allows utils.Data to be used as a parameter.

  8. Environment: This example was tested using DWR 2.0 RC 3, on Tomcat 5.5

Popular Posts