Wednesday Nov 17, 2010

TOTD #150: Collection of GlassFish, NetBeans, JPA, JSF, JAX-WS, EJB, Jersey, MySQL, Rails, Eclipse, and OSGi tips

This is the 150th tip published on this blog so decided to make it a collection of all the previous ones. Here is a tag cloud (created from wordle.net/create) from title of all the tips:

As expected GlassFish is the most prominent topic. And then there are several entries on NetBeans, JRuby/Rails, several Java EE 6 technologies like JPA, JAX-WS, JAX-RS, EJB, and JSF, and more entries on Eclipse, OSGi and some other tecnhologies too. Here is a complete collection of all the tips published so far:

Just for fun, here is another tag cloud:

You can access all the tips here. And keep those suggestions coming!

Technorati: totd glassfish netbeans jpa jsf jaxws jersey mysql rails osgi eclipse

Monday Apr 26, 2010

TOTD #130: Invoking a OSGi service from a JAX-WS Endpoint - OSGi and Enterprise Java

Sahoo blogged about JAX-WS Web service in an OSGi bundle. This Tip Of The Day (TOTD) provides complete steps to create such an application from scratch.

We will create an OSGi service, a JAX-WS compliant Web service as hybrid application, and a JAX-WS Web service client.

Lets create an OSGi service first.

  1. Create a simple OSGi service as explained in TOTD #36. Generate the maven project as:
    mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes \\
    -DgroupId=org.glassfish.samples.osgi.hello.service -DartifactId=osgi-pure
    


    The updated directory structure looks like:

    osgi-pure
    osgi-pure/pom.xml
    osgi-pure/src
    osgi-pure/src/main
    osgi-pure/src/main/java
    osgi-pure/src/main/java/org
    osgi-pure/src/main/java/org/glassfish
    osgi-pure/src/main/java/org/glassfish/samples
    osgi-pure/src/main/java/org/glassfish/samples/osgi
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/Hello.java
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/impl
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/impl/App.java
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/impl/HelloImpl.java
    


    "Hello.java" is the OSGi service interface and looks like:

    public interface Hello {
        public String sayHello(String name);
    }
    


    "HelloImpl.java" is a trivial implementation of the service and looks like:

    public class HelloImpl implements Hello {
        public String sayHello(String name) {
            return "Hello " + name;
        }
    }
    


    "App.java" is the Bundle Activator and looks like:

    import org.glassfish.samples.osgi.hello.service.impl.HelloImpl;
    import java.util.Properties;
    import org.glassfish.samples.osgi.hello.service.Hello;
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    
    /\*\*
     \* Hello world!
     \*/
    public class App implements BundleActivator {
    
        public void start(BundleContext bc) throws Exception {
            bc.registerService(Hello.class.getName(), new HelloImpl(), new Properties());
        }
    
        public void stop(BundleContext bc) throws Exception {
            bc.ungetService(bc.getServiceReference(Hello.class.getName()));
        }
    }
    

    The "start" method registers the OSGi service using the name "Hello" and "stop" method un-registers the service.

  2. The updated "maven-bundle-plugin" from TOTD #36 looks like:

    <plugin>
      <groupId>org.apache.felix</groupId>
      <artifactId>maven-bundle-plugin</artifactId>
      <extensions>true</extensions>
      <configuration>
        <instructions>
          <Export-Package>${pom.groupId}</Export-Package>
          <Bundle-Activator>${pom.groupId}.impl.App</Bundle-Activator>
        </instructions>
      </configuration>
    </plugin>
    

    Notice, only the package that contains the service interface, i.e. "org.glassfish.samples.osgi.hello.service", is exported and the activator and service implementation are in a different package.

  3. Create the OSGi service bundle as:

    mvn install
    

    This also installs the OSGi bundle in the local maven repository. Deploy this bundle in GlassFish v3 by copying to "glassfish/domains/domain1/autodeploy/bundles" directory. Make sure GlassFish is running or start it as:

    asadmin start-domain --verbose
    

Lets create a hybrid application that consists of a JAX-WS compliant Web service, queries the OSGi service registry, invokes the OSGi service and return a response to the client.

  1. Create the Maven project as:
    mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes \\
    -DgroupId=org.glassfish.samples.osgi.jaxws.webservice -DartifactId=helloservice
    
    
  2. Change the generated "src/main/java/org/glassfish/samples/osgi/jaxws/webservice/App.java" such that it looks like:

    import javax.jws.WebService;
    import org.glassfish.samples.osgi.hello.service.Hello;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.BundleReference;
    import org.osgi.framework.ServiceReference;
    
    /\*\*
     \* Hello world!
     \*/
    @WebService
    public class App {
        public String sayHello(String name) {
            Hello service = getService(Hello.class);
            return service.sayHello(name);
        }
    
    
        /\*\*
         \* This method looks up service of given type in OSGi service registry and returns if found.
         \* Returns null if no such service is available,
         \*/
       private static <T> T getService(Class<T> type) 
           BundleContext ctx = BundleReference.class.cast(
               App.class.getClassLoader()).getBundle().getBundleContext();
           ServiceReference ref = ctx.getServiceReference(type.getName());
           return ref != null ? type.cast(ctx.getService(ref)) : null;
       }
    }
    

    The "getService" method queries the OSGI service registry and returns the service reference. The "sayHello" method looks for the "Hello" service and invokes a method on it. The name "Hello" is the same as registered during the OSGi bundle creation earlier.

  3. In the generated "pom.xml":
    1. Change the packaging to "war".
    2. Add the following repository:

      <repositories>
        <repository>
          <id>java.net</id>
          <name>GlassFish Maven Repository</name>
          <url>http://maven.glassfish.org/content/groups/glassfish</url>
        </repository>
      </repositories>
      

      so that Java EE API dependency can be resolved.

    3. Add the following dependencies in "provided" scope:

      <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>6.0</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>org.osgi</groupId>
        <artifactId>org.osgi.core</artifactId>
        <version>4.2.0</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>org.glassfish.samples.osgi.hello.service</groupId>
        <artifactId>osgi-pure</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>provided</scope>
      </dependency>
      

      Notice "osgi-pure" bundle is specified as dependency as that is used to invoke the service.
    4. Add the following plugins to "pom.xml" to create the hybrid application:

      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.0.2</version>
          <configuration>
            <source>1.5</source>
            <target>1.5</target>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.felix</groupId>
          <artifactId>maven-bundle-plugin</artifactId>
          <configuration>
            <supportedProjectTypes>
              <supportedProjectType>war</supportedProjectType>
              <supportedProjectType>bundle</supportedProjectType>
              <supportedProjectType>jar</supportedProjectType>
            </supportedProjectTypes>
            <instructions>
              <Import-Package>javax.jws; version=2.0, \*</Import-Package>
              <Web-ContextPath>${pom.artifactId}</Web-ContextPath>
            </instructions>
          </configuration>
          <executions>
            <execution>
              <id>bundle-manifest</id>
              <phase>process-classes</phase>
              <goals>
                <goal>manifest</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        <plugin> <!-- Need to use this plugin to build war files -->
          <artifactId>maven-war-plugin</artifactId>
          <version>2.1-beta-1</version>
          <configuration>
            <archive>
              <!-- add bundle plugin generated manifest to the war -->
              <manifestFile>
                ${project.build.outputDirectory}/META-INF/MANIFEST.MF
              </manifestFile>
              <!-- For some reason, adding Bundle-ClassPath in maven-bundle-plugin
                   confuses that plugin and it generates wrong Import-Package, etc.
                   So, we generate it here.
              -->
              <manifestEntries>
                <Bundle-ClassPath>WEB-INF/classes/</Bundle-ClassPath>
              </manifestEntries>
            </archive>
            <failOnMissingWebXml>false</failOnMissingWebXml>
          </configuration>
        </plugin>
      </plugins>
      

      The "maven-bundle-plugin" is used to generate the appropriate OSGi metadata and "maven-war-plugin" is used to bundle the WAR file.

  4. Generate the deployable archive as:

    mvn clean package
    

    This generates "target/helloservice-1.0-SNAPSHOT.war". The generated manifest in "target/helloservice-1.0-SNAPSHOT/WEB-INF/classes/META-INF/MANIFEST.MF" looks like:

    Manifest-Version: 1.0
    Export-Package: org.glassfish.samples.osgi.jaxws.webservice;uses:="org
     .glassfish.samples.osgi.hello.service,javax.jws,org.osgi.framework";v
     ersion="1.0.0.SNAPSHOT"
    Bundle-Version: 1.0.0.SNAPSHOT
    Tool: Bnd-0.0.357
    Bundle-Name: helloservice
    Bnd-LastModified: 1272315464139
    Created-By: 1.6.0_17 (Apple Inc.)
    Bundle-ManifestVersion: 2
    Bundle-SymbolicName: org.glassfish.samples.osgi.jaxws.webservice.hello
     service
    Web-ContextPath: helloservice
    Import-Package: javax.jws;version="2.0",org.glassfish.samples.osgi.hel
     lo.service,org.glassfish.samples.osgi.jaxws.webservice;version="1.0",
     org.osgi.framework;version="1.5"
    
  5. Now this archive is a hybrid application, i.e. its a WAR file and an OSGi bundle. So lets deploy this file by copying the file to "domains/domain1/autodeploy/bundles" and see a message like:

    WS00018: Webservice Endpoint deployed App listening at address at http://localhost:8080/helloservice/AppService
    
  6. Accessing "http://localhost:8080/helloservice/AppService" in a browser window shows the following page:

Lets create the client project now to invoke this Web service.

  1. Create a new directory "client" and invoke the following command to generate the client-side artifacts:
    wsimport -keep http://localhost:8080/helloservice/AppService?wsdl
    
  2. Create a new directory "client" and a new file "HelloClient" in that directory as:
    package client;
    
    import org.glassfish.samples.osgi.jaxws.webservice.\*
    
    public class HelloClient {
            public static void main(String[] args) throws Exception {
                    App port = new AppService().getAppPort();
                    System.out.println(port.sayHello("Duke"));
            }
    }
    
    
    This "main" method gets a reference to the generated service class, gets the port from it, and then invokes the method by passing an argument.
  3. Compile the client code as:
    javac -d . -cp . client/HelloClient.java
    
  4. Invoke the client as:
    java -cp . client.HelloClient
    

    to see the result as:
    Hello Duke

    This result is coming from the OSGi service implementation.

All the three projects explained above are available in this download.

Using similar concept, a pure OSGi client can invoke a pure OSGi service which can then delegate the actual business method implementation to a JAX-WS endpoint and then use all the goodness of the underlying stack. This way, the benefits of JAX-WS are extended to a pure OSGi client and vice versa.

Also see other OSGi entries on this blog.

Technorati: totd jaxws osgi glassfish v3 webservice

Thursday Feb 18, 2010

TOTD #124: Using CDI + JPA with JAX-RS and JAX-WS

This is a follow up blog to TOTD #120 and TOTD #123. These two blogs together have created a simple Java EE 6 application and showed the following features so far:

  • No-interface view for EJB
  • EJBs packaged in a WAR file
  • Optional "faces-config.xml" for Java Server Faces
  • FacesServlet registered using Servlet 3.0 programmatic registration APIs
  • Java Server Faces navigation rules using convention-over-configuration
  • Optional "web.xml" for Servlets 3.0
  • Add database access using Java Persistence API 2.0
  • Show type-safe Criteria API from JPA 2.0
  • Use Context & Dependency Injection for JSF managed beans
  • Add Ajax effects from Java Server Faces 2.0
  • Add Bean Validation to the JSF managed bean

GlassFish v3 is the Java EE 6 Reference Implementation and comes bundled with a complete SOAP Web services stack (Metro/JAX-WS) and a RESTful stack (JAX-RS/Jersey). This blog will update the previously created Maven project with:

  • A SOAP Web service using JAX-WS
  • A RESTful Web service using JAX-RS
  • Use Context & Dependency Injection with JAX-WS and JAX-RS
  • Query the database using JPA 2 based upon criteria from the Web service invocation

Lets get started!

  1. Use the Maven project from TOTD #123 and update the directory structure as follows:
    src
    src/main
    src/main/java
    src/main/java/org
    src/main/java/org/glassfish
    src/main/java/org/glassfish/samples
    src/main/java/org/glassfish/samples/ActorResource.java
    src/main/java/org/glassfish/samples/RESTApplication.java
    src/main/java/org/glassfish/samples/SakilaBean.java
    src/main/java/org/glassfish/samples/SimpleBean.java
    src/main/java/org/glassfish/samples/SimpleEJB.java
    src/main/java/org/glassfish/samples/SimpleServlet.java
    src/main/java/org/glassfish/samples/SOAPService.java
    src/main/resources
    src/main/webapp
    src/main/webapp/index.jsp
    src/main/webapp/index.xhtml
    src/main/webapp/sakila.xhtml
    src/main/webapp/show.xhtml
    src/main/webapp/WEB-INF
    src/main/webapp/WEB-INF/beans.xml
    src/main/webapp/WEB-INF/web.xml
    

    The changes are:
    • "ActorResource.java" is added for the RESTful representation of Actor table.
    • "SOAPSevice.java" is added to invoke the SOAP-based Web service.
    • "SakilaBean.java" is updated to query the database for an Actor identified by "id".
  2. The updated files are explained below.
    • A new method is added to SakilaBean.java as shown below:
       public Actor findActorById(int id) {
          EntityManager em = emf.createEntityManager();
      
          CriteriaBuilder cb = emf.getCriteriaBuilder();
          CriteriaQuery<Actor> criteria = cb.createQuery(Actor.class);
      
          // FROM clause
          Root<Actor> actor = criteria.from(Actor.class);
      
          // SELECT clause
          criteria.multiselect(actor.<Short>get("actorId"),
                               actor.<String>get("firstName"),
                               actor.<String>get("lastName"));
      
          // WHERE clause
           criteria.where(cb.equal(actor.<Short>get("actorId"), id));
      
          Query q = em.createQuery(criteria);
          ((org.eclipse.persistence.jpa.JpaQuery)q).getDatabaseQuery().dontMaintainCache();
      
          return (Actor)q.getResultList().get(0);
      }
      

      This method queries the database for an actor by his id and uses the typesafe Criteria API to achieve the purpose. The FROM, SELECT, and WHERE clause are highlighted in the code. A cast to EclipseLink specific class is required because of the bug #303205.
    • SOAPService.java
      package org.glassfish.samples;
      
      import javax.inject.Inject;
      import javax.jws.WebService;
      import sakila.Actor;
      
      @WebService
      public class SOAPService {
          @Inject SakilaBean bean;
      
          public String sayHello(int id) {
              Actor a = bean.findActorById(id);
              return "Hello " + a.getFirstName();
          }
      }
      

      The key points in the code are:
      • Standard JAX-WS annotations from "javax.jws.\*" package are used to represent the Web service.
      • The Web service has only one method "sayHello" that concatenates the string "Hello" with the first name of "Actor" identified by "id".
      • No deployment descriptor modifications are required to publish this Web service.
      • "SakilaBean" is injected using @Inject annotation and used to query the database. This allows to encapsulate all the database details in one class and injected in a typesafe manner.
    • RESTApplication.java
      package org.glassfish.samples;
      
      import javax.ws.rs.ApplicationPath;
      import javax.ws.rs.core.Application;
      
      @ApplicationPath("/sakila")
      public class RESTApplication extends Application {
      }
      

      This is a marker class to inform Jersey of the root resource to be registered. By default, all classes with @Path and @Provider annotations are included. It also specifies the base path at which all resources are accessible.

      An alternative to this class is to specify the required information in "web.xml" as:
      <servlet>
           <servlet-name>Jersey Web Application</servlet-name>
           <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
       </servlet>
      
       <servlet-mapping>
           <servlet-name>Jersey Web Application</servlet-name>
           <url-pattern>/sakila/\*</url-pattern>
       </servlet-mapping>
      

      So only one of RESTApplication.java or changes in "web.xml" are required.
    • ActorResource.java
      package org.glassfish.samples;
      
      import javax.enterprise.context.RequestScoped;
      import javax.inject.Inject;
      import javax.ws.rs.GET;
      import javax.ws.rs.Path;
      import javax.ws.rs.Produces;
      import javax.ws.rs.PathParam;
      import sakila.Actor;
      
      @Path("/actor/{id}")
      @RequestScoped
      public class ActorResource {
          @Inject SakilaBean sakila;
      
          @GET
          @Produces("application/json")
          public Actor getActor(@PathParam("id") int id) {
              return sakila.findActorById(id);
          }
      }
      
      The key points in the code are:
      • Standard JAX-RS annotations from "javax.ws.rs" package are used to represent the RESTful resource.
      • "getActor" method is invoked when the resource is accessed using HTTP GET.
      • The resource is accessible at "/actor/{id}" URL where "{id}" is mapped to the "id" parameter of "getActor" method.
      • SakilaBean is injected in a typesafe manner using @Inject annotation. This bean is then used to query the database using the "id" parameter.
      • "getActor" method produces JSON representation, as defined by the "@Produces" annotation. This is easily achieved by updating our Persistence Unit (PU) created in TOTD #122 and adding "@javax.xml.bind.annotation.XmlRootElement" as the class level annotation on "sakila.Actor" class. Make sure to install the updated PU to your local Maven repository.

Package and deploy the application as:
mvn clean package
./bin/asadmin deploy --force=true ~/samples/javaee6/simplewebapp/target/simplewebapp-1.0-SNAPSHOT.war

Now the SOAP web service is accessible at "http://localhost:8080/simplwebapp-1.0-SNAPSHOT/SOAPServiceService" and looks like:


Notice, the URL in your case may be different if the Web service class name was different. The default URL is "http://<HOST>:<PORT>/<CONTEXT ROOT><WEB SERVICE CLASS NAME>Service".

This Web service can be easily tested by using the in-built tester accessible at "http://localhost:8080/simplwebapp-1.0-SNAPSHOT/SOAPServiceService?tester" and looks like:

The WSDL describing the Web service can be seen by clicking on the "WSDL File" link. The Web service method can be invoked by entering a value ("id" of the Actor) in the text box and clicking on "sayHello" button. Here is a sample run:

Clicking on "Submit" invokes the Web service which then uses the injected "SakilaBean" to query the database using the parameter specified. The first name from the response from the database is then extracted, concatenated with the string "Hello" and returned as Web service response.

The RESTful resource is accessible at "http://localhost:8080/simplwebapp-1.0-SNAPSHOT/sakila/actor/5" and looks like:

As in the SOAP-based Web service, the "5" in the URL is mapped to a parameter in the "ActorResource.java", the injected "SakilaBean" is then used to query the database and returns the JSON representation. Specifying a different number in the URL will show the RESTful JSON representation for that particular actor.

More Java EE 6 features will be covered in subsequent blogs. Are you interested in any particular ones ?

Technorati: jaxws metro webservices jaxrs rest jersey glassfish v3 cdi jsr299 weld

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today