Quantcast
RSS Entries RSS
RSS Subscribe by Email

Suppressing Compile Warnings with Java Annotations

If you’ve used Java 1.5 Generics much then you’re probably familiar with the following compile warning: “Type safety: The expression of type List needs unchecked conversion to conform to List<String>” or similar. It turns out there’s a rather simple solution with annotations to ignore this problem:

@SuppressWarnings(“unchecked”)

A couple other possible uses of the annotation that might be of interest are:

@SuppressWarnings(“deprecation”)
@SuppressWarnings(“serial”)

These are compiler specific, so you may want to check out the full Eclipse list, which is a bit lengthier than Sun’s 7 options (all, deprecation, unchecked, fallthrough, path, serial, and finally).

Also, multiple statements can be combined into one as follows:

@SuppressWarnings({“unchecked”, “deprecation”})

Comments (2)

Apache CXF Tutorial – WS-Security with Spring

This tutorial will cover adding an authentication component to your web service though WS-Security. If you need an overview of how to setup CXF then you may find our previous tutorial helpful. Another helpful resource is CXF’s own WS-Security tutorial. However, it does not include information on how to setup the client through Spring.

To begin with, make sure you have at least the following .jars in addition to the required base CXF .jars:

spring-beans-2.0.6.jar
spring-context-2.0.6.jar
spring-core-2.0.6.jar
spring-web-2.0.6.jar
wss4j-1.5.1.jar
xmlsec-1.3.0.jar

Now we will add a security interceptor to the server’s Spring configuration file, which we named cxf.xml in the last tutorial in order to match the CXF documentation.

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxws

                          http://cxf.apache.org/schemas/jaxws.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

  <jaxws:endpoint id="auth"
                  implementor="com.company.auth.service.AuthServiceImpl"
                  address="/corporateAuth">

    <jaxws:inInterceptors>
      <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
      <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
        <constructor-arg>
          <map>
            <entry key="action" value="UsernameToken" />
            <entry key="passwordType" value="PasswordText" />
            <entry key="passwordCallbackClass" value="com.company.auth.service.ServerPasswordCallback" />
          </map>
        </constructor-arg>
      </bean>
    </jaxws:inInterceptors>

  </jaxws:endpoint>

</beans>

You can change the action and passwordType to do more advanced authentication. In this example, we will simply require all authenticating clients to know a single password specified by the server. If you’d like each client to have it’s own password you can specify that in the callback, which is the next thing we must implement:

package com.company.auth.service;

import java.io.IOException;
import java.util.ResourceBundle;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class ServerPasswordCallback implements CallbackHandler {

    private static final String BUNDLE_LOCATION = "com.company.auth.authServer";
    private static final String PASSWORD_PROPERTY_NAME = "auth.manager.password";

    private static String password;
    static {
        final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
        password = bundle.getString(PASSWORD_PROPERTY_NAME);
    }

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // Set the password on the callback. This will be compared to the
        //     password which was sent from the client.
        // We can call pc.getIdentifer() right here to check the username
        //     if we want each client to have it's own password.
        pc.setPassword(password);
    }

}

The server is now setup to require a password. The password we are requiring is one that we specified in a properties file and then read in through a ResourceBundle. You may find it easier to simply hard code the password on the initial run and then replace it with your own means of authentication once the service is up and running.

If you are running on WebLogic 9, as I was, then you will get an error “java.lang.UnsupportedOperationException: This class does not support SAAJ 1.1“. In order to correct that, make sure your version of the SAAJ classes are being used by adding the following to your weblogic.xml descriptor file:

<container-descriptor>
    <prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>

You WebLogic folks must also then set two properties in your WebLogic JDK:

-Djavax.xml.soap.MessageFactory=com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl
-Djavax.xml.soap.SOAPConnectionFactory=weblogic.wsee.saaj.SOAPConnectionFactoryImpl

We now have to setup the client to supply a password. Firstly, we will create another Spring file at com/company/auth/service/cxfClient.xml to setup the application context for the client:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                      http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

  <bean id="proxyFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
    <property name="serviceClass" value="com.company.auth.service.AuthService"/>
    <property name="address" value="http://localhost:7001/authManager/services/corporateAuth"/>
    <property name="inInterceptors">
      <list>
        <ref bean="logIn" />
      </list>
    </property>
    <property name="outInterceptors">
      <list>
        <ref bean="logOut" />
        <ref bean="saajOut" />
        <ref bean="wss4jOut" />
      </list>
    </property>
  </bean>

  <bean id="client" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean" factory-bean="proxyFactory" factory-method="create" />

  <bean id="logIn" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
  <bean id="logOut" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
  <bean id="saajOut" class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
  <bean id="wss4jOut" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
    <constructor-arg>
      <map>
        <entry key="action" value="UsernameToken" />
        <entry key="user" value="ws-client" />
        <entry key="passwordType" value="PasswordText" />
        <entry key="passwordCallbackClass" value="com.company.auth.service.ClientPasswordCallback" />
      </map>
    </constructor-arg>
  </bean>    

</beans>

We then need to set the password for our message:

package com.company.auth.service;

import java.io.IOException;
import java.util.ResourceBundle;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class ClientPasswordCallback implements CallbackHandler {

    private static final String BUNDLE_LOCATION = "com.company.auth.authClient";
    private static final String PASSWORD_PROPERTY_NAME = "auth.manager.password";	

    private static String password;
    static {
        final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
        password = bundle.getString(PASSWORD_PROPERTY_NAME);
    }	

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // set the password for our message.
        pc.setPassword(password);
    }

}

Finally, we create the service factory, which is extremely easy since all the work was done in the Spring file:

package com.company.auth.service;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class AuthServiceFactory {

    private static final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
                "com/company/auth/service/cxfClient.xml"
            });

    public AuthServiceFactory() {
    }

    public AuthService getService() {
        return (AuthService) context.getBean("client");
    }
}

Congratulations. Your web service now utilizes a basic implementation of WS-Security. Hopefully, that will be enough background to get you on your way.

Comments (58)

Adding Links for Navigation to the WordPress Pages List

The category titled “Pages” on the right-hand side of this blog is an area where WordPress will allow a user to create wiki pages. However, I wanted to include some navigational links in that area as well, which isn’t immediately available through the WordPress admin screens.

The solution I found was to create a new blogroll category and use it for navigational links. First off, I modified the call to wp_list_pages by adding the argument “title_li=0″, which tells WordPress not to wrap it in <ul> tags, but to instead only output the <li> tags. Then I wrapped the call with my own <ul> tags. Finally, I called wp_list_bookmarks in order to display the category I had created, which had an id of 34. I again used the “title_li=0″ parameter and also had to add “categorize=0″, so that this parameter would not be ignored:

	<li id="pages">
		<h2><?php _e('Pages'); ?></h2>
		<ul>
			<?php wp_list_bookmarks('categorize=0&title_li=0&category=34'); ?>
			<?php wp_list_pages('title_li=0'); ?>
		</ul>
	</li>

Comments

hCard Microformat Example

What exactly is an hCard? It’s a format which specifies that some information on a web page is an online business card. The address for Lumidant in the page footer is an hCard. This means when someone with the Operator Firefox extension visits this page they will have the opportunity to do a one-click import of Lumidant’s address and URL into their contact book. And that can’t hurt business. Come Firefox 3, this functionality will be available without extensions. Creating an hCard was pretty simple. All I had to do was add specific class names to my HTML elements:

        <div id="address" class="vcard">
          <a class="fn org url" href="http://www.lumidant.com" title="Lumidant | Cleveland Web Design and Development">
            Lumidant LLC
          </a>
          ·
          <span class="adr">
            <span class="street-address">1220 West Sixth Street</span> |
            <span class="extended-address">Suite 506</span> |
            <span class="locality">Cleveland</span>,
            <span class="region">OH</span>
            <span class="postal-code">44113</span>
          </span>
          · <a href="blog/">Blog</a>
        </div>

Comments

Screenshots of Scrolling Web Pages

Have you ever wanted to take a screenshot of a web page that won’t fit on a single page?  I wanted to do that for a client today and found a handy utility that will do it for you.  TechSmith’s SnagIt makes the process far easier that taking multiple screenshots and stitching the panorama together in your favorite photo editor.  Unfortunately, it is not free, but there is a 30-day free trial.  If you’re a mac user there is a program called Paparazzi that
is free and will do the same thing.

Comments

Rounded Corners with JavaScript and CSS – No Images

Rounded corners can create nice presentational effects and are very popular in web design. They also are a bit of a pain to create because they usually require PhotoShop and the markup is not as straightforward as square corners. So I was excited to give Steffen Rusitschka’s method a try, which uses only JavaScript and CSS and is quite feature full. His ShadedBorders library looks quite nice in Firefox. However, my test did not perform as well in IE6. Strangely, once you mouse over the afflicted area, the problem corrects itself. Here are the before and after shots:

Buggy Rounded Corners in IE Properly Formatted Rounded Corners

Hopefully this problem will be fixed as I am quite interested in the library. I will be sure to test future versions and in the meantime may investigate potential workarounds for the problem.

Comments (4)

Gradient Web 2.0 Effects with GIMP

In my last post, I mentioned that I installed GIMP to read a Photoshop .psd file. If you’re not familiar with GIMP, it is an extremely high quality free alternative to Photoshop. Using GIMP, I have been able to create several graphical effects with little effort. In this post, I will show you how I created the logos for Lumidant and Moon Rock Media using GIMP.

Moon Rock came to us and wanted a “cliché web 2.0 design”. Basically that means they were asking for gradients, mirrored surfaces, reflections, and shiny or glossy images. To begin the logo, I created a gradient background. Select one color of gray as the foreground and another as the background. Then choose the gradient tool and drag it vertically from the top of the image to the bottom. Play around with this for a little to get a feel for the tool. Once that was completed, I used the text tool to write moon in blue and then rock in pink. I then chose the dodge/burn tool to alternately dodge and burn the pink letters:

Moonrock Logo - Text Only

Once I was certain the text was how I wanted, I stacked the two text layers to become a single layer, making them easier to work with. To mirror the text, you simply Duplicate Layer and then Flip Vertically. Position it below the original text. Now, under the transparency menu, Add Alpha Channel to the layer. This will allow us to make use of transparency. If the option was grayed out, then your layer already has an alpha channel, so you can just continue to the next step. The final step is to create another gradient effect. We want to use the gradient to hide the portion of the reflection we don’t want to see. I changed the foreground to black. Most importantly, you must click the picture of the gradient you’re creating in the gradient tool options and select “FG to Transparent”. Now drag the gradient tool up vertically over the text. This will hide most of the text with a black gradient:

Moonrock Logo with Black Gradient Hiding Reflection

Since we don’t want the black to show in the final logo, select Color to Alpha and choose black. Now the black will have disappeared leaving you with a finished reflection effect:

Moon Rock Media Logo

In the Lumidant logo on the Lumidant homepage, the lighthouse searchlight or spotlight was also created using gradient effects. To create a spotlight, first create a new layer. This is important because we will duplicate the layer later and only want the spotlight itself duplicated. Draw the outline of the light you’d like to create by using the paths tool. I created the light by drawing a long triangle. After you have drawn the third point and would like to connect back to the first, hold Ctrl and click on the first point. This will close the shape. Then hold Ctrl, click on the short side of the triangle, and drag it outwards. This will round the end of the light. Turn the shape into a selection by clicking “Selection from Path”:

Lumidant Logo with Searchlight Path Drawn

Now, we get to use the gradient tool again. Having created the spotlight-shaped selection, we can draw inside the selection and nothing outside of it will be affected. Select white and “FG to Transparent”. Drag the gradient tool from the point of the triangle to the end of the rounded section. Now Duplicate Layer. Add a 4px Gaussian Blur filter to one of the layers. In the “Layers, Channels, Paths, Undo” menu bar (referred to as a dialog by GIMP), select the layer that you blurred and move the opacity down to 80. Now choose the original spotlight layer and move the opacity down to 20. Hooray! You’ve just created an awesome looking spotlight.

Comments (16)

Opening Photoshop .psd Files without Photoshop

A client sent me a Photoshop .psd file today containing a mock-up of a site he’s interested in having me design. Unfortunately, at the time I received the email, I was on a computer which did not have Photoshop installed, so I had no way to read or view the file. I downloaded GIMP (GNU Image Manipulation Program) and it worked perfectly. The only con with GIMP is that it’s not exactly lightweight if you just want to view a single .psd file, but I don’t mind having it installed as I may find other opportunities to use it.

In other unrelated software news, I heard today that a game called Guitar Rising is in development, which is basically a Guitar Hero knockoff except that you can plug in a real guitar!

Comments (1)

Showdown – Java HTML Parsing Comparison

I had to do some HTML parsing today, but unfortunately most HTML on the web is not well-formed like any markup I’d create. Missing end tags and other broken syntax throws a wrench into the situation. Luckily, others have already addressed this issue. Many times over in fact, leaving many to wonder which solution to implement.

Once you parse HTML, you can do some cool stuff with it like transform it or extract some information. For that reason it is sometimes used for screen scraping. So, to test the parsing libraries, I decided to do exactly that and see if I could parse the HTML well enough to extract links from it using an XQuery. The contenders were NekoHTML, HtmlCleaner, TagSoup, and jTidy. I know that there are many others I could have chosen from as well, but this seemed to be a good sampling and there’s only so much time in the day. I also chose 10 URLs to parse. Being a true Clevelander I picked the sites of a number of local attractions. I’m right near all of the stadiums, so the Quicken Loans Arena website was my first target. I sometimes jokingly refer to my city as the “Mistake on the Lake” and the pure awfulness of the HTML from my city did not fail me. The ten URLs I chose are:

http://www.theqarena.com

http://cleveland.indians.mlb.com

http://www.clevelandbrowns.com

http://www.cbgarden.org

http://www.clemetzoo.com

http://www.cmnh.org

http://www.clevelandart.org

http://www.mocacleveland.org

http://www.glsc.org

http://www.rockhall.com

I gave each library an InputStream created from a URL (referred to as urlIS in the code samples below) and expected an org.w3c.dom.Node in return once the parse operation was completed. I implemented each library in its own class extending from an AbstractScraper implementing a Scraper interface I created. This was a design tip fresh in my mind from reading my all-time favorite technical book: Effective Java by Josh Bloch. The implementation specific code for each library is below:

NekoHTML:

final DOMParser parser = new DOMParser();
try {
	parser.parse(new InputSource(urlIS));
	document = parser.getDocument();
} catch (SAXException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
}

TagSoup:

final Parser parser = new Parser();
SAX2DOM sax2dom = null;
try {
	sax2dom = new SAX2DOM();
	parser.setContentHandler(sax2dom);
	parser.setFeature(Parser.namespacesFeature, false);
	parser.parse(new InputSource(urlIS));
} catch (Exception e) {
	e.printStackTrace();
}
document = sax2dom.getDOM();

jTidy:

final Tidy tidy = new Tidy();
tidy.setQuiet(true);
tidy.setShowWarnings(false);
tidy.setForceOutput(true);
document = tidy.parseDOM(urlIS, null);

HtmlCleaner:

final HtmlCleaner cleaner = new HtmlCleaner(urlIS);
try {
	cleaner.clean();
	document = cleaner.createDOM();
} catch (Exception e) {
	e.printStackTrace();
}

Finally, to judge the ability to parse the HTML, I ran the XQuery “//a” to grab all the <a> tags from the document. The only one of these parsing libraries I had used before was jTidy. It was able to extract the links from 5 of the 10 documents. However, the clear winner was HtmlCleaner. It was the only library to successfully clean 10/10 documents. Most of the others were not able to make it past even the very first link I provided, which was to Quicken Loans Arena site. HtmlCleaner’s full results:

Found 87 links at http://www.theqarena.com/
Found 156 links at http://cleveland.indians.mlb.com/
Found 96 links at http://www.clevelandbrowns.com/
Found 106 links at http://www.cbgarden.org/
Found 70 links at http://www.clemetzoo.com/
Found 23 links at http://www.cmnh.org/site/
Found 27 links at http://www.clevelandart.org/
Found 51 links at http://www.mocacleveland.org/
Found 27 links at http://www.glsc.org/
Found 90 links at http://www.rockhall.com/

One disclaimer that I will make is that I did not go out of my way to improve the performance of any of these libraries. Some of them had additional options that could be set to possibly improve performance. I did not delve into wading through the documentation to figure out what these options were and simply used the plain vanilla incantations. HtmlCleaner seems to offer me everything I need and was quick and easy to implement.  One drawback to HtmlCleaner is that it’s not available in a Maven repository.  Sometimes NekoHTML may be easier to use for this reason.  Note also that at the time of writing the last released version of jTidy was from 2000.  A newer .jar has since been made available that will likely perform better.

Comments (36)

Web Services Tutorial with Apache CXF

I created a web service today with CXF and wanted to share the steps it took to get it up and running in this quick tutorial. Apache CXF was created by the merger of the Celtix and XFire projects. I chose to implement my service in CXF because some colleagues had been using XFire and would likely want to upgrade at some point. I am using the latest version, which is 2.0.4. While the library itself seems to be of high quality, the documentation is still a work in progress. However, do not fret because this CXF tutorial will get you up and running in no time. I will be creating a simple web service that will allow the retrieval of employee information. The service will return this simple POJO (Plain Old Java Object) bean with matching getters and setters:

package com.company.auth.bean;

import java.io.Serializable;
import java.util.Set;

public class Employee implements Serializable {

	private static final long serialVersionUID = 1L;
	private String gid;
	private String lastName;
	private String firstName;
	private Set<String> privileges;

	public Employee() {}

	public Set<String> getPrivileges() {
		return privileges;
	}

	public void setPrivileges(Set<String> privileges) {
		this.privileges = privileges;
	}	

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getGid() {
		return gid;
	}

	public void setGid(String gid) {
		this.gid = gid;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public boolean isUserInRole(String role) {
		if(privileges == null) { return false; }
		else { return privileges.contains(role); }
	}

}

First off, you need to download Apache CXF and drop the necessary .jars in your WEB-INF/lib directory:

aopalliance-1.0.jar
commons-logging-1.1.jar
cxf-2.0-incubator.jar
geronimo-activation_1.1_spec-1.0-M1.jar (or Sun’s Activation jar)
geronimo-annotation_1.0_spec-1.1.jar (JSR 250)
geronimo-javamail_1.4_spec-1.0-M1.jar (or Sun’s JavaMail jar)
geronimo-servlet_2.5_spec-1.1-M1.jar (or Sun’s Servlet jar)
geronimo-ws-metadata_2.0_spec-1.1.1.jar (JSR 181)
jaxb-api-2.0.jar
jaxb-impl-2.0.5.jar
jaxws-api-2.0.jar
jetty-6.1.5.jar
jetty-util-6.1.5.jar
neethi-2.0.jar
saaj-api-1.3.jar
saaj-impl-1.3.jar
spring-core-2.0.4.jar
spring-beans-2.0.4.jar
spring-context-2.0.4.jar
spring-web-2.0.4.jar
stax-api-1.0.1.jar
wsdl4j-1.6.1.jar
wstx-asl-3.2.1.jar
XmlSchema-1.2.jar
xml-resolver-1.2.jar

The first thing which needed to be done was to create the service interface. The service interface defines which methods the web service client will be able to call. It’s pretty standard Java with just two JWS (Java Web Service) annotations thrown in:

package com.company.auth.service;

import javax.jws.WebService;
import javax.jws.WebParam;
import com.company.auth.bean.Employee;

@WebService
public interface AuthService {
    Employee getEmployee(@WebParam(name="gid") String gid);
}

The @WebParam annotation is in fact optional, but highly recommended since it will make like easier for the end consumers of your service. Without it, your parameter would be named arg0 making it less clear what parameters your service actually takes.

Implementing the actual service comes next:

package com.company.auth.service;

import javax.jws.WebService;

import com.company.auth.bean.Employee;
import com.company.auth.dao.EmployeeDAO;

@WebService(endpointInterface = "com.company.auth.service.AuthService", serviceName = "corporateAuthService")
public class AuthServiceImpl implements AuthService {

	public Employee getEmployee(String gid) {
		EmployeeDAO dao = new EmployeeDAO();
		return dao.getEmployee(gid);
	}

}

I then had to tell Spring (which is used by CXF) where to find my Java classes. I created the following cxf.xml file inside my package directory (com/company/auth/service):

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxws

 					http://cxf.apache.org/schemas/jaxws.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
  <jaxws:endpoint id="auth"
                  implementor="com.company.auth.service.AuthServiceImpl"
                  address="/cxfAuth"/>
</beans>

Finally, I updated my WEB-INF/web.xml file to let CXF know where my cxf.xml file was and define the CXF servlet:

<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <display-name>Auth Manager</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:com/company/auth/service/cxf.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
  <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <servlet-class>
        org.apache.cxf.transport.servlet.CXFServlet
    </servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
</web-app>

The most frustrating portion of getting the CXF web service up and running was the following exception:
Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: javax.jws.WebService.portName()Ljava/lang/String;

This is due to the fact that I am running WebLogic 9.2, which contains a library with an older version of JSR (Java Specification Request) 181. The quickest solution for me was to prepend geronimo-ws-metadata_2.0_spec-1.1.1.jar to the WebLogic classpath. This will likely not be the solution I choose to implement in the end, but it got me back up and running. It seemed I also had to clear my WebLogic cache for this fix to take effect. Because I often find instances where this seems necessary, I have created a Windows script to clear the Weblogic Cache. If you run into app server related issues, this was one area I found the Apache CXF docs to be helpful.

Also, if you are running Hibernate, you may encounter ASM incompatibilities between Hibernate’s CGLib and the version of ASM which CXF requires. But do not fret because this is easy enough to solve.

Once this was solved, the list of services was available. The context root for my web project is authManager, so my regular web index page is available at http://localhost:7001/authManager/. The list of web services is then automagically generated by CXF at http://localhost:7001/authManager/services/.

The final step is to build the client. This was very easy when compared to getting the server up and running because I was able to simply swipe the code from the Apache CXF documentation. So, without further ado:

package com.company.auth.client;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import com.company.auth.bean.Employee;
import com.company.auth.service.AuthService;

public final class Client {

    private Client() {
    } 

    public static void main(String args[]) throws Exception {

    	JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

    	factory.getInInterceptors().add(new LoggingInInterceptor());
    	factory.getOutInterceptors().add(new LoggingOutInterceptor());
    	factory.setServiceClass(AuthService.class);
    	factory.setAddress("http://localhost:7001/authManager/services/cxfAuth");
    	AuthService client = (AuthService) factory.create();

    	Employee employee = client.getEmployee("0223938");
    	System.out.println("Server said: " + employee.getLastName() + ", " + employee.getFirstName());
    	System.exit(0);

    }

}

Wow! Wasn’t that cool? (Yes, I’m a dork :o ) You should now be up and running with a CXF web service.

If you are looking for something to learn next then may I suggest our tutorial on adding security to your web service.  That tutorial will also show you how to setup the client using Spring, which you may find helpful as well.

Comments (78)