Skip to content

By Programmer For Programmer

Here I lay down the useful tips, tricks and utilities for programmers like myself.

Archive

Tag: java

Every time I feel like I know all the Java syntax there is, I learn something new :)

Let’s say you have a class with a static constructor that returns a map, and that map is typed and does fancy things, but you still want to use generics from wherever you are using the Map.

public class MyClass {

    public static final <K,V> Map<K,V> createMyMap() {
        ...
        return map;
    }
}

If you are like me, you hate hate hate casting to generics, but you are forced to do that or have a big ugly @SuppressWarnings("unchecked") on the method that you access this method.

@SuppressWarnings("unchecked")
function Map<String, Object> getMyMap() {
    return MyClass.createMyMap();
}

Well not anymore!. Today I just learned (by looking through some Java source code) that you can assign generics on your static calls by using the following syntax.

function Map<String, Object> getMyMap() {
    return MyClass.<String, Object>createMyMap();
}

Just put the generics right after the period and before the method name. Easy.

I learned this while trying to figure out why java.util.Collections has static fields EMPTY_LIST, EMPTY_SET, and EMPTY_MAP, and also static methods emptyList(), emptySet, and emptyMap. It says right in the code, “Unlike this method, the field does not provide type safety”.

So recently I had to build a web service, and had a really hard time finding documentation or instructions on how to make web services work in Tomcat using the web service annotations from JSR 181 (@WebService, @WebMethod, @WebParam, etc).  So I created this post as a way to help someone else in the same situation.  I’m sure there are many ways to do this, but this is one way.

My goal was to create java files annotated with web service annotations and deploy the application to Tomcat, and just have everything magically work.  I didn’t want to use the wsgen tool.  I wanted it to work like it does in JBoss, Weblogic, and Glassfish, where you just deploy the application and the web service endpoints are created automatically.

Environment

Here is the environment that I was working with:

  • Java 1.6.0_16
  • Tomcat 6.0.18
  • Eclipse 3.4
  • Building a WAR and dropping into webapps folder in Tomcat

Example Class

MyWebService.java:

package com.example.webservice;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

/**
 * An example web service implementation
 * @author Sean Adkinson
 */
@WebService(targetNamespace = "http://com.example.webservice", name = "mywebservice")
public class MyWebService
{

    /**
     * Says hello to the given name
     * @param name A name
     * @return Hello, name
     */
    @WebMethod(operationName = "hello")
    @WebResult(name = "return")
    public String hello(@WebParam(name = "name") String name)
    {
        return "Hello, " + name;
    }

}

Instructions

Follow these instructions to create a web service from our example class above:

  1. Download Metro 1.5 binary (or latest point release) at https://metro.dev.java.net/1.5/
  2. Run java -jar metro-1_5.jar to unpack the contents of the jar
  3. Take all the lib/webservices-*.jar files from the expanded Metro installation, and copy them into your web application’s WEB-INF/lib folder. Note that I chose to put these in my application’s library, but you can also put them in the server’s library at tomcat.home/lib.
  4. Create a sun-jaxws.xml file right next to web.xml under WEB-INF. This is where you specify where to find the classes that have your web service annotations. Here is a sample file:

    sun-jaxws.xml:

    <endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
        <endpoint
            name='mywebservice'
            implementation='com.example.webservice.MyWebService'
            url-pattern='/mywebservice' />
    </endpoints>
    

    Here, name can be anything, implementation is the classpath reference to the class with the web service annotations that should have a web service created from it, and url-pattern is the path after your web application’s URL that will hit this web service (so if your application is at http://localhost:8080/MyApplication, this web service will be available at http://localhost:8080/MyApplication/mywebservice).

  5. Edit your web.xml to map the URL pattern above to class com.sun.xml.ws.transport.http.servlet.WSServlet, and add a listener for class com.sun.xml.ws.transport.http.servlet.WSServletContextListener. Here is an example:
    <listener>
        <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>myws</servlet-name>
        <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>myws</servlet-name>
        <url-pattern>/mywebservice</url-pattern>
    </servlet-mapping>
    
  6. Now deploy your webapp, start Tomcat, and go to http://localhost:8080/MyApplication/mywebservice, and you should see details about the endpoint that was created, with a link to the generated WSDL file.
  7. From here you should be able point whatever client code generating utilities you have at the WSDL and talk with the web service from a client. I use built-in Eclipse Axis2 code generation for this, but check out wsimport in the Metro /bin folder for another option.

That’s it! Hope this helps someone who had as hard a time as I did with getting this working in Tomcat.

Given a class that is mapped in Hibernate, you can get the table name that it is mapped to with the following piece of code.

import org.hibernate.SessionFactory;
import org.hibernate.persister.entity.Joinable;

public String getTableName(SessionFactory sessionFactory, Class<?> mappedClass)
{
    ClassMetadata cmd = sessionFactory.getClassMetadata(mappedClass);

    //check that the class is mapped to something with a table name
    if (cmd == null || !Joinable.class.isInstance(cmd))
        return null;

    return Joinable.class.cast(cmd).getTableName();
}