Mit ‘Spring’ getaggte Artikel

“Code First, Contract Last” Soap Web Services mit Spring

Donnerstag, 14. August 2008

Möchte man einen Web Service veröffentlichen, der von Springs Dependency Injection Mechanismus Gebrauch macht, so muss der Web Service auf den Spring Application Context zugreifen können.

Spring liefert ein eigenes Web Services Framework “Spring-WS” mit. Allerdings ist dieses Framework darauf ausgerichtet, dass die Web Services dem “Contract First, Code Last” – Ansatz nach entwickelt werden. D.h. man schreibt zunächst das XML Schema und das WSDL-Dokument, welche den Web Service definieren und implementiert anschließend, den aufgestellten Vorschriften entsprechend, die Service-Klassen. Für diesen Weg bietet Spring-WS einige Unterstützung. So kann man sich z.B. aus einem definierten XML-Schema eine WSDL generieren lassen.

Hat man aber bereits die Service-Klassen implementiert und möchte deren Funktionalität nun als Web Services veröffentlichen, so bieten sich andere Hilfsmittel dafür an. Im Folgenden wird beschrieben, wie man mit XFire und JSR 181 Annotations den “Code First, Contract Last” – Ansatz zur Entwicklung von Spring Web Services verfolgen kann. Dazu wird ein einfacher “Hello World” – Web Service erstellt, der von Spring verwaltete Beans verwendet.

Zunächst schreibt man sich eine Java-Klasse, die die helloWorld-Methode implementiert.

package de.baderjene.backend.webServiceAPI;
 
import org.springframework.stereotype.Component;
 
@Component("helloWorldCore")
public class HelloWorldCore {
 
  public String helloWorld() {
    return "hello world!";
  }
}

Anschließend implementiert man die annotierte Web Service Klasse, die die Klasse HelloWorldCore per Dependency Injection erhält.

package de.baderjene.backend.webServiceAPI;
 
import javax.jws.WebMethod;
import javax.jws.WebResult;
import javax.jws.WebService;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@WebService(name = "ExampleWebService", targetNamespace = "http://pegasus/services/ExampleWebService")
@Component("exampleWebService")
public class ExampleWebService {
 
  @Autowired
  HelloWorldCore helloWorldCore;
 
  @WebMethod(operationName = "helloWorld", action = "urn:helloWorld")
  @WebResult(name = "result")
  public String helloWorld() {
    return helloWorldCore.helloWorld();
  }
}

Nun schreibt man noch die benötigten Konfigurationsdateien:
1. Spring Application Context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
 <context:component-scan base-package="de.baderjene.backend"/>
 
<context:annotation-config/>
 
</beans>

2. xfire-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <import resource="backendContext.xml"/>
    <bean id="webAnnotations"
        class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations" />
    <bean id="jsr181HandlerMapping"
        class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping">
        <property name="typeMappingRegistry">
            <ref bean="xfire.typeMappingRegistry" />
        </property>
        <property name="xfire" ref="xfire" />
        <property name="webAnnotations" ref="webAnnotations" />
    </bean>
</beans>

3. web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:org/codehaus/xfire/spring/xfire.xml
            /WEB-INF/xfire-servlet.xml
        </param-value>
    </context-param>
 
   <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
       </listener-class>
    </listener>
 
    <servlet>
        <servlet-name>XFireServlet</servlet-name>
        <servlet-class>
            org.codehaus.xfire.spring.XFireSpringServlet
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>XFireServlet</servlet-name>
        <url-pattern>/servlet/XFireServlet/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>XFireServle</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
</web-app>

Zum Ausprobieren kann man nun ein War-Packet erstellen, dieses z.B. in das webapps-Verzeichnis vom Tomcat legen und den Tomcat starten.


Christian Borkowski


Klassenvergleich bei der Verwendung von CGLIB

Montag, 09. Juni 2008

Wird die Java Bibliothek CGLIB zur dynamischen Kapselung von Klassen verwendet, kann es dazu kommen, dass beim Vergleich der Klassen zweier Instanzen ein Fehler auftritt.

Beispiel soll hier eine überschriebene Equals-Methode sein:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public boolean equals(Object obj) {
    boolean isEqual = false;
    if (obj == null) {
        isEqual = false;
    } else if (this == obj) {
        isEqual = true;
    } else if (!(obj instanceof Superklasse)) {
        isEqual = false;
    /* Problematischer Vergleich */
    } else if (!this.getClass().equals(obj.getClass())) {
        isEqual = false;
    } else {
/* weitere Tests, z.B. mit einem EqualsBuilder */
    }
    return isEqual;
}

Der direkte Klassevergleich funtioniert nun nicht mehr, da es sich bei obj um eine Instanz der CGLIB-generierten Subklasse handelt.

Ein Möglichkeit ist die Aufweichung der Klassenbedingung:

!this.getClass().isAssignableFrom(obj.getClass())

Eine bessere die Verwendung der ClassUtils aus dem Spring Framework:

!this.getClass().equals(ClassUtils.getUserClass(obj))

Ersatzweise hier auch der benutzte Code aus der ClassUtils-Klasse:

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
public abstract class ClassUtils {
    /** The CGLIB class separator character "$$" */
    public static final String CGLIB_CLASS_SEPARATOR = "$$";
 
    /**
     * Return the user-defined class for the given instance: usually simply
     * the class of the given instance, but the original class in case of a
     * CGLIB-generated subclass.
     * @param instance the instance to check
     * @return the user-defined class
     */
    public static Class getUserClass(Object instance) {
        return getUserClass(instance.getClass());
    }
 
    /**
     * Return the user-defined class for the given class: usually simply the given
     * class, but the original class in case of a CGLIB-generated subclass.
     * @param clazz the class to check
     * @return the user-defined class
     */
    public static Class getUserClass(Class clazz) {
        return (clazz != null && clazz.getName().indexOf(CGLIB_CLASS_SEPARATOR) != -1 ?
                clazz.getSuperclass() : clazz);
    }
 
}

Hendrik Lange