CXF webservice(rest&soap)
Different Types Of Services
CXF support three major types of services:
- SOAP – this page summarizes the options for creating SOAP services.
- REST-ful – REST support is described here.
- CORBA
Soap 方法
JAX-WS Annotated Services from Java
The JAX-WS APIs include a set of annotations which allow you to build services using annotated classes. These services are based on a single class which contains a set of operations.
Here\’s a simple example:
@WebService public class Hello {
public String sayHi(String name) {
return "Hello " + name;
}
} |
JAX-WS includes many more annotations as well such as:
- @WebMethod – allows you to customize the operation name, exclude the operation from inclusion in the service, etc
- @WebParam – allows you to customize a parameter\’s name, namespace, direction (IN or OUT), etc
- @WebResult – allows you to customize the return value of the web service call
Data is marshalled from XML to Java and vice versa via the JAXB data-binding.
Services are publish via one of two means:
- The JAX-WS standard Endpoint APIs
- CXF\’s XML configuration format – i.e. <jaxws:endpoint … />
More Information: A simple JAX-WS service, Developing a JAX-WS Service (goes into much more depth), Writing a service with Spring
JAX-WS Annotated Services from WSDL
If you have existing WSDLs for your service or wish to write your WSDL first and then generate classes, CXF has many tools to help you do this.
The WSDL2Java tool will generate a JAX-WS annotated service and server stub from your WSDL. You can run it one of three ways:
- The command line
- The Maven Plugin
- With the WSDL2Java API
Note that CXF generally restricts WSDL support to WSI-BP, not the full WSDL 1.1 specification.
There is also a Simple Frontend that allows you to create services without usage of Java annotations, using XML configuration files instead.
JAX-WS Providers
JAX-WS Providers allow you to create services which work at the message level – as opposed to the operation level as with annotated classes. The have a single operation “invoke” which receives either the message payload (i.e. the SOAP Body) or the whole message itself (i.e. the SOAP Envelope).
Here\’s a simple example:
@WebServiceProvider public class HelloProvider {
public Source invoke(Source request) {
return ....;
}
} |
Services are publish via one of two means:
- The JAX-WS standard Endpoint APIs
- CXF\’s XML configuration format – i.e. <jaxws:endpoint … />
Developing a Consumer with CXF
Generating the Stub Code
The starting point for developing a service consumer (or client) in CXF is a WSDL contract, complete with port type, binding, and service definitions. You can then use the wsdl2java utility to generate the Java stub code from the WSDL contract. The stub code provides the supporting code that is required to invoke operations on the remote service.
For CXF clients, the wsdl2java utility can generate the following kinds of code:
- Stub code – supporting files for implementing a CXF client.
- Client starting point code – sample client code that connects to the remote service and invokes every operation on the remote service.
- Ant build file – a build.xml file intended for use with the ant build utility. It has targets for building and for running the sample client application.
Basic HelloWorld WSDL contract
The below shows the HelloWorld WSDL contract. This contract defines a single port type, Greeter, with a SOAP binding, Greeter_SOAPBinding, and a service, SOAPService, which has a single port, SoapPort.
<?xml version= "1.0" encoding= "UTF-8" ?>
<wsdl:types>
elementFormDefault= "qualified" >
<simpleType name= "MyStringType" >
<restriction base= "string" >
<maxLength value= "30" />
</restriction>
</simpleType>
<element name= "sayHi" >
<complexType/>
</element>
<element name= "sayHiResponse" >
<complexType>
<sequence>
<element name= "responseType" type= "string" />
</sequence>
</complexType>
</element>
<element name= "greetMe" >
<complexType>
<sequence>
<element name= "requestType" type= "tns:MyStringType" />
</sequence>
</complexType>
</element>
<element name= "greetMeResponse" >
<complexType>
<sequence>
<element name= "responseType" type= "string" />
</sequence>
</complexType>
</element>
<element name= "greetMeOneWay" >
<complexType>
<sequence>
<element name= "requestType" type= "string" />
</sequence>
</complexType>
</element>
<element name= "pingMe" >
<complexType/>
</element>
<element name= "pingMeResponse" >
<complexType/>
</element>
<element name= "faultDetail" >
<complexType>
<sequence>
<element name= "minor" type= "short" />
<element name= "major" type= "short" />
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
<wsdl:message name= "sayHiRequest" >
<wsdl:part element= "x1:sayHi" name= "in" />
</wsdl:message>
<wsdl:message name= "sayHiResponse" >
<wsdl:part element= "x1:sayHiResponse" name= "out" />
</wsdl:message>
<wsdl:message name= "greetMeRequest" >
<wsdl:part element= "x1:greetMe" name= "in" />
</wsdl:message>
<wsdl:message name= "greetMeResponse" >
<wsdl:part element= "x1:greetMeResponse" name= "out" />
</wsdl:message>
<wsdl:message name= "greetMeOneWayRequest" >
<wsdl:part element= "x1:greetMeOneWay" name= "in" />
</wsdl:message>
<wsdl:message name= "pingMeRequest" >
<wsdl:part name= "in" element= "x1:pingMe" />
</wsdl:message>
<wsdl:message name= "pingMeResponse" >
<wsdl:part name= "out" element= "x1:pingMeResponse" />
</wsdl:message>
<wsdl:message name= "pingMeFault" >
<wsdl:part name= "faultDetail" element= "x1:faultDetail" />
</wsdl:message>
<wsdl:portType name= "Greeter" >
<wsdl:operation name= "sayHi" >
<wsdl:input message= "tns:sayHiRequest" name= "sayHiRequest" />
<wsdl:output message= "tns:sayHiResponse" name= "sayHiResponse" />
</wsdl:operation>
<wsdl:operation name= "greetMe" >
<wsdl:input message= "tns:greetMeRequest" name= "greetMeRequest" />
<wsdl:output message= "tns:greetMeResponse" name= "greetMeResponse" />
</wsdl:operation>
<wsdl:operation name= "greetMeOneWay" >
<wsdl:input message= "tns:greetMeOneWayRequest"
name= "greetMeOneWayRequest" />
</wsdl:operation>
<wsdl:operation name= "pingMe" >
<wsdl:input name= "pingMeRequest" message= "tns:pingMeRequest" />
<wsdl:output name= "pingMeResponse" message= "tns:pingMeResponse" />
<wsdl:fault name= "pingMeFault" message= "tns:pingMeFault" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name= "Greeter_SOAPBinding" type= "tns:Greeter" >
<soap:binding style= "document"
<wsdl:operation name= "sayHi" >
<soap:operation soapAction= "" style= "document" />
<wsdl:input name= "sayHiRequest" >
<soap:body use= "literal" />
</wsdl:input>
<wsdl:output name= "sayHiResponse" >
<soap:body use= "literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name= "greetMe" >
<soap:operation soapAction= "" style= "document" />
<wsdl:input name= "greetMeRequest" >
<soap:body use= "literal" />
</wsdl:input>
<wsdl:output name= "greetMeResponse" >
<soap:body use= "literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name= "greetMeOneWay" >
<soap:operation soapAction= "" style= "document" />
<wsdl:input name= "greetMeOneWayRequest" >
<soap:body use= "literal" />
</wsdl:input>
</wsdl:operation>
<wsdl:operation name= "pingMe" >
<soap:operation style= "document" />
<wsdl:input>
<soap:body use= "literal" />
</wsdl:input>
<wsdl:output>
<soap:body use= "literal" />
</wsdl:output>
<wsdl:fault name= "pingMeFault" >
<soap:fault name= "pingMeFault" use= "literal" />
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name= "SOAPService" >
<wsdl:port binding= "tns:Greeter_SOAPBinding" name= "SoapPort" >
<soap:address
</wsdl:port>
</wsdl:service>
</wsdl:definitions> |
The Greeter port type defines the following WSDL operations:
- sayHi – has a single output parameter, of xsd:string.
- greetMe – has an input parameter, of xsd:string, and an output parameter, of xsd:string.
- greetMeOneWay – has a single input parameter, of xsd:string. Because this operation has no output parameters, CXF can optimize this call to be a oneway invocation (that is, the client does not wait for a response from the server).
- pingMe – has no input parameters and no output parameters, but it can raise a fault exception.
This WSDL also defines a binding, Greeter_SOAPBinding, for the SOAP protocol. In practice, the binding is normally generated
automatically – for example, by running either of the CXF wsdl2soap or wsdl2xml utilities. Likewise, the SOAPService service can be generated automatically by running the CXF wsdl2service utility.
Generating the stub code
After defining the WSDL contract, you can generate client code using the CXF wsdl2java utility. Enter the following command at a command-line prompt:
wsdl2java -ant -client -d ClientDir hello_world.wsdl
Where ClientDir is the location of a directory where you would like to put the generated files and
hello_world.wsdl is a file containing the contract shown in the WSDL above. The -ant option generates an ant build.xml file, for use with the ant build utility. The -client option generates starting point code for a client main() method.
The preceding wsdl2java command generates the following Java packages:
-
org.apache.hello_world_soap_http
This package name is generated from the http://apache.org/hello_world_soap_http target namespace. All of the WSDL entities defined in this target namespace (for example, the Greeter port type and the SOAPService service) map to Java classes in the corresponding Java package. -
org.apache.hello_world_soap_http.types
This package name is generated from the http://apache.org/hello_world_soap_http/types target namespace. All of the XML types defined in this target namespace (that is, everything defined in the wsdl:types element of the HelloWorld contract) map to Java classes in the corresponding Java package.
The stub files generated by the wsdl2java command fall into the following categories:
- Classes representing WSDL entities (in the org.apache.hello_world_soap_http package) – the following classes are generated to represent WSDL entities:
- Greeter is a Java interface that represents the Greeter WSDL port type. In JAX-WS terminology, this Java interface is a service endpoint interface.
- SOAPService is a Java class that represents the SOAPService WSDL service element.
- PingMeFault is a Java exception class (extending java.lang.Exception) that represents the pingMeFault WSDL fault element.
- Classes representing XML types (in the org.apache.hello_world_soap_http.types package) – in the HelloWorld example, the only generated types are the various wrappers for the request and reply messages. Some of these data types are useful for the
asynchronous invocation model.
Implementing a CXF Client
This section describes how to write the code for a simple Java client, based on the WSDL contract above. To implement the client, you need to use the following stub classes:
- Service class (that is, SOAPService).
- Service endpoint interface (that is, Greeter).
Generated service class
The below shows the typical outline for a generated service class, ServiceName, which extends the javax.xml.ws.Service base class.
public class ServiceName extends javax.xml.ws.Service
{ ...
public ServiceName(URL wsdlLocation, QName serviceName) { }
public ServiceName() { }
public Greeter getPortName() { }
.
.
.
} |
The ServiceName class defines the following methods:
- Constructor methods – the following forms of constructor are defined:
- ServiceName(URL wsdlLocation, QName serviceName) constructs a service object based on the data in the serviceName service in the WSDL contract that is obtainable from wsdlLocation.
- ServiceName() is the default constructor, which constructs a service object based on the service name and WSDL contract that were provided at the time the stub code was generated (for example, when running the CXF wsdl2java command). Using this constructor presupposes that the WSDL contract remains available at its original location.
- get_PortName_() methods – for every PortName port defined on the ServiceName service, CXF generates a corresponding get_PortName_() method in Java. Therefore, a wsdl:service element that defines multiple ports will generate a service class with multiple get_PortName_() methods.
Service endpoint interface
For every port type defined in the original WSDL contract, you can generate a corresponding service endpoint interface in Java. A service endpoint interface is the Java mapping of a WSDL port type. Each operation defined in the original WSDL port type maps to a corresponding method in the service endpoint interface. The operation\’s parameters are mapped as follows:
- The input parameters are mapped to method arguments.
- The first output parameter is mapped to a return value.
- If there is more than one output parameter, the second and subsequent output parameters map to method arguments (moreover, the values of these arguments must be passed using Holder types).
For example, the below shows the Greeter service endpoint interface, which is generated from the Greeter port type defined in Example1. For simplicity, Example3 omits the standard JAXB and JAX-WS annotations.
/* Generated by WSDLToJava Compiler. */ package org.objectweb.hello_world_soap_http;
...
public interface Greeter
{ public java.lang.String sayHi();
public java.lang.String greetMe(java.lang.String requestType);
public void greetMeOneWay(java.lang.String requestType);
public void pingMe() throws PingMeFault;
} |
Client main function
Here is Java code that implements the HelloWorld client. In summary, the client connects to the SoapPort port on the SOAPService service and then proceeds to invoke each of the operations supported by the Greeter port type.
package demo.hw.client;
import java.io.File;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.hello_world_soap_http.Greeter;
import org.apache.hello_world_soap_http.PingMeFault;
import org.apche.hello_world_soap_http.SOAPService;
public final class Client {
private static final QName SERVICE_NAME =
"SOAPService" );
private Client()
{
}
public static void main(String args[]) throws Exception
{
if (args.length == 0 )
{
System.out.println( "please specify wsdl" );
System.exit( 1 );
}
URL wsdlURL;
File wsdlFile = new File(args[ 0 ]);
if (wsdlFile.exists())
{
wsdlURL = wsdlFile.toURL();
}
else
{
wsdlURL = new URL(args[ 0 ]);
}
System.out.println(wsdlURL);
SOAPService ss = new SOAPService(wsdlURL, SERVICE_NAME);
Greeter port = ss.getSoapPort();
String resp;
System.out.println( "Invoking sayHi..." );
resp = port.sayHi();
System.out.println( "Server responded with: " + resp);
System.out.println();
System.out.println( "Invoking greetMe..." );
resp = port.greetMe(System.getProperty( "user.name" ));
System.out.println( "Server responded with: " + resp);
System.out.println();
System.out.println( "Invoking greetMeOneWay..." );
port.greetMeOneWay(System.getProperty( "user.name" ));
System.out.println( "No response from server as method is OneWay" );
System.out.println();
try {
System.out.println( "Invoking pingMe, expecting exception..." );
port.pingMe();
} catch (PingMeFault ex) {
System.out.println( "Expected exception: PingMeFault has occurred." );
System.out.println(ex.toString());
}
System.exit( 0 );
}
} |
The Client.main() function proceeds as follows:
- The CXF runtime is implicitly initialized – that is, provided the CXF runtime classes are loaded. Hence, there is no need to call a special function in order to initialize CXF.
- The client expects a single string argument that gives the location of the WSDL contract for HelloWorld. The WSDL location is stored in wsdlURL.
- A new port object (which enables you to access the remote server endpoint) is created in two steps, as shown in the following code fragment:
SOAPService ss =
new
SOAPService(wsdlURL, SERVICE_NAME);
Greeter port = ss.getSoapPort();
To create a new port object, you first create a service object (passing in the WSDL location and service name) and then call the appropriate get PortName () method to obtain an instance of the particular port you need. In this case, the SOAPService service supports only the SoapPort port, which is of Greeter type.
- The client proceeds to call each of the methods supported by the Greeter service endpoint interface.
- In the case of the pingMe() operation, the example code shows how to catch the PingMeFault fault exception.
Setting Connection Properties with Contexts
You can use JAX-WS contexts to customize the properties of a client proxy. In particular, contexts can be used to modify connection properties and to send data in protocol headers. For example, you could use contexts to add a SOAP header, either to a request message or to a response message. The following types of context are supported on the client side:
- Request context – on the client side, the request context enables you to set properties that affect outbound messages. Request context properties are applied to a specific port instance and, once set, the properties affect every subsequent operation invocation made on the port, until such time as a property is explicitly cleared. For example, you might use a request context property to set a connection timeout or to initialize data for sending in a header.
- Response context – on the client side, you can access the response context to read the property values set by the inbound message from the last operation invocation. Response context properties are reset after every operation invocation. For example, you might access a response context property to read header information received from the last inbound message.
Setting a request context
To set a particular request context property, ContextPropertyName, to the value, PropertyValue, use the code shown here:
// Set request context property. java.util.Map<String, Object> requestContext = ((javax.xml.ws.BindingProvider)port).getRequestContext();
requestContext.put(ContextPropertyName, PropertyValue); // Invoke an operation. port.SomeOperation(); |
You have to cast the port object to javax.xml.ws.BindingProvider in order to access the request context. The request context itself is of type, java.util.Map<String, Object>, which is a hash map that has keys of String and values of arbitrary type. Use java.util.Map.put() to create a new entry in the hash map.
Reading a response context
To retrieve a particular response context property, ContextPropertyName, use the code shown here:
// Invoke an operation. port.SomeOperation(); // Read response context property. java.util.Map<String, Object> responseContext = ((javax.xml.ws.BindingProvider)port).getResponseContext();
PropertyType propValue = (PropertyType) responseContext.get(ContextPropertyName); |
The response context is of type, java.util.Map<String, Object>, which is a hash map that has keys of type String and values of an arbitrary type. Use java.util.Map.get() to access an entry in the hash map of response context properties.
Supported contexts
CXF supports the following context properties:
Context Property Name | Context Property Type |
---|---|
org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES | org.apache.cxf.ws.addressing.AddressingProperties |
Asynchronous Invocation Model
In addition to the usual synchronous mode of invocation, CXF also supports two forms of asynchronous invocation, as follows:
- Polling approach – in this case, to invoke the remote operation, you call a special method that has no output parameters, but returns a javax.xml.ws.Response instance. The Response object (which inherits from the javax.util.concurrency.Future interface) can be polled to check whether or not a response message has arrived.
- Callback approach – in this case, to invoke the remote operation, you call another special method that takes a reference to a callback object (of javax.xml.ws.AsyncHandler type) as one of its parameters. Whenever the response message arrives at the client, the CXF runtime calls back on the AsyncHandler object to give it the contents of the response message.
Both of these asynchronous invocation approaches are described here and illustrated by code examples.
Contract for asynchronous example
The following example shows the WSDL contract that is used for the asynchronous example. The contract defines a single port type, GreeterAsync, which contains a single operation, greetMeSometime.
<wsdl:definitions xmlns= "http://schemas.xmlsoap.org/wsdl/"
name= "HelloWorld" >
<wsdl:types>
elementFormDefault= "qualified" >
<element name= "greetMeSometime" >
<complexType>
<sequence>
<element name= "requestType" type= "xsd:string" />
</sequence>
</complexType>
</element>
<element name= "greetMeSometimeResponse" >
<complexType>
<sequence>
<element name= "responseType" type= "xsd:string" />
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
<wsdl:message name= "greetMeSometimeRequest" >
<wsdl:part name= "in" element= "x1:greetMeSometime" />
</wsdl:message>
<wsdl:message name= "greetMeSometimeResponse" >
<wsdl:part name= "out" element= "x1:greetMeSometimeResponse" />
</wsdl:message>
<wsdl:portType name= "GreeterAsync" >
<wsdl:operation name= "greetMeSometime" >
<wsdl:input name= "greetMeSometimeRequest"
message= "tns:greetMeSometimeRequest" />
<wsdl:output name= "greetMeSometimeResponse"
message= "tns:greetMeSometimeResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name= "GreeterAsync_SOAPBinding" type= "tns:GreeterAsync" >
<wsdl:operation name= "greetMeSometime" >
<soap:operation style= "document" />
<wsdl:input>
<soap:body use= "literal" />
</wsdl:input>
<wsdl:output>
<soap:body use= "literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name= "SOAPService" >
<wsdl:port name= "SoapPort" binding= "tns:GreeterAsync_SOAPBinding" >
<soap:address
</wsdl:port>
</wsdl:service>
</wsdl:definitions> |
Generating the asynchronous stub code
The asynchronous style of invocation requires extra stub code (for example, dedicated asychronous methods defined on the service endpoint interface). This special stub code is not generated by default, however. To switch on the asynchronous feature and generate the requisite stub code, you must use the mapping customization feature from the WSDL 2.0 specification.
Customization enables you to modify the way the wsdl2java utility generates stub code. In particular, it enables you to modify the WSDL-to-Java mapping and to switch on certain features. Here, customization is used to switch on the asynchronous invocation feature. Customizations are specified using a binding declaration, which you define using a jaxws:bindings tag (where the jaxws prefix is tied to the http://java.sun.com/xml/ns/jaxws namespace). There are two alternative ways of specifying a binding declaration:
- External binding declaration – the jaxws:bindings element is defined in a file separately from the WSDL contract. You specify the location of the binding declaration file to the wsdl2java utility when you generate the stub code.
- Embedded binding declaration – you can also embed the jaxws:bindings element directly in a WSDL contract, treating it as a WSDL extension. In this case, the settings in jaxws:bindings apply only to the immediate parent element.
This section considers only the first approach, the external binding declaration. The template for a binding declaration file that switches on asynchronous invocations is shown next:
<bindings xmlns:xsd= "http://www.w3.org/2001/XMLSchema"
wsdlLocation= "@WSDL_LOCATION@/hello_world_async.wsdl"
<bindings node= "wsdl:definitions" >
<enableAsyncMapping> true </enableAsyncMapping>
</bindings>
</bindings> |
Where AffectedWSDLContract specifies the URL of the WSDL contract that is affected by this binding declaration. The AffectedNode is an XPath value that specifies which node (or nodes) from the WSDL contract are affected by this binding declaration. You can set AffectedNode to wsdl:definitions, if you want the entire WSDL contract to be affected. The {jaxws:enableAsyncMapping}} element is set to true to enable the asynchronous invocation feature.
For example, if you want to generate asynchronous methods only for the GreeterAsync port type, you could specify <bindings node="wsdl:definitions/wsdl:portType[@name=\'GreeterAsync\']"> in the preceding binding declaration.
Assuming that the binding declaration is stored in a file, async_binding.xml, you can generate the requisite stub files with asynchronous support by entering the following wsdl2java command:
wsdl2java -ant -client -d ClientDir -b async_binding.xml hello_world.wsdl
When you run the wsdl2java command, you specify the location of the binding declaration file using the -b option. After generating the stub code in this way, the GreeterAsync service endpoint interface (in the file GreeterAsync.java) is defined as shown below:
/* Generated by WSDLToJava Compiler. */ package org.apache.hello_world_async_soap_http;
... import java.util.concurrent.Future;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
... public interface GreeterAsync {
public Future<?> greetMeSometimeAsync(
String requestType,
AsyncHandler<org.myorg.types.GreetMeSometimeResponse> asyncHandler
);
public Response<org.myorg.types.GreetMeSometimeResponse> greetMeSometimeAsync(
String requestType);
public java.lang.String greetMeSometime(
String requestType
);
} |
In addition to the usual synchronous method, greetMeSometime(), two asynchronous methods are also generated for the greetMeSometime operation, as follows:
- greetMeSometimeAsync() method with Future<?> return type and an extra javax.xml.ws.AsyncHandler parameter – call this method for the callback approach to asynchronous invocation.
- greetMeSometimeAsync() method with Response<GreetMeSometimeResponse> return type – call this method for the polling approach to asynchronous invocation.
The details of the callback approach and the polling approach are discussed in the following subsections.
Implementing an asynchronous client with the polling approach
The below sample illustrates the polling approach to making an asynchronous operation call. Using this approach, the client invokes the
operation by calling the special Java method, _OperationName_Async(), that returns a javax.xml.ws.Response<T> object, where T is the type of the operation\’s response message. The Response<T> object can be polled at a later stage to check whether the operation\’s response message has arrived.
package demo.hw.client;
import java.io.File;
import java.util.concurrent.Future;
import javax.xml.namespace.QName;
import javax.xml.ws.Response;
import org.apache.hello_world_async_soap_http.GreeterAsync;
import org.apache.hello_world_async_soap_http.SOAPService;
import org.apche.hello_world_async_soap_http.types.GreetMeSometimeResponse;
public final class Client {
private static final QName SERVICE_NAME
"SOAPService" );
private Client() {}
public static void main(String args[]) throws Exception {
...
// Polling approach:
Response<GreetMeSometimeResponse> greetMeSomeTimeResp =
port.greetMeSometimeAsync(System.getProperty( "user.name" ));
while (!greetMeSomeTimeResp.isDone()) {
Thread.sleep( 100 );
}
GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
...
System.exit( 0 );
}
} |
The greetMeSometimeAsync() method invokes the greetMeSometimes operation, transmitting the input parameters to the remote service and returning a reference to a javax.xml.ws.Response<GreetMeSometimeResponse> object. The Response class is defined by extending the standard java.util.concurrency.Future<T> interface, which is specifically designed for polling the outcome of work performed by a concurrent thread. There are essentially two basic approaches to polling using the Response object:
-
Non-blocking polling – before attempting to get the result, check whether the response has arrived by calling the non-blocking
Response<T>.isDone() method. For example:Response<GreetMeSometimeResponse> greetMeSomeTimeResp = ...;
if
(greetMeSomeTimeResp.isDone()) {
GreetMeSometimeResponse reply = greetMeSomeTimeResp.get();
}
-
Blocking polling – call Response<T>.get() right away and block until the response arrives (optionally specifying a timeout). For example, to poll for a response, with a 60 second timeout:
Response<GreetMeSometimeResponse> greetMeSomeTimeResp = ...;
GreetMeSometimeResponse reply = greetMeSomeTimeResp.get(
60L,
java.util.concurrent.TimeUnit.SECONDS
);
Implementing an asynchronous client with the callback approach
An alternative approach to making an asynchronous operation invocation is to implement a callback class, by deriving from the
javax.xml.ws.AsyncHandler interface. This callback class must implement a handleResponse() method, which is called by the CXF runtime to notify the client that the response has arrived. The following shows an outline of the AsyncHandler interface that you need to implement.
package javax.xml.ws;
public interface AsyncHandler<T>
{ void handleResponse(Response<T> res);
} |
In this example, a callback class, TestAsyncHandler, is defined as shown below.
package demo.hw.client;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
import org.apache.hello_world_async_soap_http.types.GreetMeSometimeResponse;
public class TestAsyncHandler implements AsyncHandler<GreetMeSometimeResponse> {
private GreetMeSometimeResponse reply;
public void handleResponse(Response<GreetMeSometimeResponse> response) {
try {
reply = response.get();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public String getResponse() {
return reply.getResponseType();
}
} |
The implementation of handleResponse() shown in Example11 simply gets the response data and stores it in a member variable, reply. The extra getResponse() method is just a convenience method that extracts the sole output parameter (that is, responseType) from the response.
Example12 illustrates the callback approach to making an asynchronous operation call. Using this approach, the client invokes the operation by calling the special Java method, _OperationName_Async(), that returns a java.util.concurrency.Future<?> object and takes an extra parameter of AsyncHandler<T>.
package demo.hw.client;
import java.io.File;
import java.util.concurrent.Future;
import javax.xml.namespace.QName;
import javax.xml.ws.Response;
import org.apache.hello_world_async_soap_http.GreeterAsync;
import org.apache.hello_world_async_soap_http.SOAPService;
import org.apache.hello_world_async_soap_http.types.GreetMeSometimeResponse;
public final class Client {
private static final QName SERVICE_NAME
private Client() {}
public static void main(String args[]) throws Exception {
...
// Callback approach
TestAsyncHandler testAsyncHandler = new TestAsyncHandler();
System.out.println( "Invoking greetMeSometimeAsync using callback object..." );
Future<?> response = port.greetMeSometimeAsync(System.getProperty( "user.name" ), testAsyncHandler);
while (!response.isDone()) {
Thread.sleep( 100 );
}
resp = testAsyncHandler.getResponse();
...
System.exit( 0 );
}
} |
The Future<?> object returned by greetMeSometimeAsync() can be used only to test whether or not a response has arrived yet – for example, by calling response.isDone(). The value of the response is only made available to the callback object, testAsyncHandler.
RESTful Services
There are different ways to build RESTful services with CXF.
- JAX-RS: CXF has an implementation of JAX-RS 1.1 and JAX-RS 1.0 (JSR-311): Java API for RESTful Web Services. This provides a more standard way to build RESTful services in JAVA.
- JAX-WS Provider and Dispatch: It is possible to create RESTful services with the JAX-WS Provider and Dispatch APIs.
-
HTTP Binding: The HTTP binding provides a flexible way of creating resources and mapping them to operations in your service. This can currently be done via annotations or a convention based mapping.
Note : HTTP Binding has been removed from CXF 2.6.0.
JAX-RS (JSR-339)
- Introduction
- Project setup and configuration
- What is New
- Getting Started with JAX-RS
- Understanding the Basics
- Support for Data Bindings
- How Request URI is Matched
- Client API
- Bean Validation
- Filters, Interceptors and Invokers
- Service listings and WADL support
- Configuring JAX-RS services
- Testing
- Debugging
- Logging
- Advanced Features
- Multiparts
- Secure JAX-RS services
- Failover and Load Distribution Features
- Redirection
- XSLT and XPath
- Complex Search Queries
- Model-View-Controller support
- Combining JAX-WS and JAX-RS
- Integration with Distributed OSGi
- Other Advanced Features
- Maven Plugins
- Deployment
- Third-party projects
- References
- How to contribute
JAX-RS:
Introduction
JAX-RS: Java API for RESTful Web Services is a Java programming language API that provides support in creating web services according to the Representational State Transfer (REST) architectural style.
CXF supports the Java API for RESTful Web Services: JAX-RS 2.0 (JSR-339) and JAX-RS 1.1 (JSR-311).
CXF 3.0.0 completely implements JAX-RS 2.0 including new Client API and has been fully tested against the first JAX-RS 2.0 TCK which became available to Apache. Existing JAX-RS 1.1 applications can be run with CXF 3.0.0.
CXF 2.7.0 supports most of the new features introduced in JAX-RS 2.0 (excluding 2.0 Client API for now – but note that CXF client API has been retrofitted to support new filters, interceptors, exception classes and Response API, plus the asynchronous client invoker API).
CXF 2.6.x supports JSR-311 API 1.1 and is JAX-RS TCK 1.1 compliant.
JAX-RS related demos are located under the samples/jax_rs directory.
This documentation will refer to JAX-RS 2.0 (JSR-339) API.
Outstanding JAX-RS JIRA issues can be found here.
Project setup and configuration
Migration
From JAX-RS 1.1 to 2.0
JAX-RS 2.0 is backward compatible with JAX-RS 1.1. Please see JAX-RS Basics for more information about JAX-RS 2.0.
From CXF 2.7.x to CXF 3.0.0
Please check the CXF 3.0.0 Migration Guide for the information about all the changes
in CXF 3.0.0. Here are more details on the changes specifically affecting JAX-RS users:
1. CXF RequestHandler and ResponseHandler filters have been removed.
These legacy CXF filters are still supported in 2.7.x but no longer in 3.0.0. Please use ContainerRequestFilter and ContainerResponseFilter instead. Also, ReaderInterceptor and WriterInterceptor can be used too.
Note, CXF filters had org.apache.cxf.message.Message available in the signature. If CXF Message is used in the existing CXF RequestHandler or ResponseHandler then use “org.apache.cxf.phase.PhaseInterceptorChain.getCurrentMessage()” or “org.apache.cxf.jaxrs.util.JAXRSUtils.getCurrentMessage()” to get a Message which has all the contextual information available.
For example, instead of
public class CustomRequestHandler implements RequestHandler {
public Response handleRequest(Message message, ClassResourceInfo cri) {
}
} public class CustomResponseHandler implements ResponseHandler {
public Response handleResponse(Message message, OperationResourceInfo ori, Response response) {
}
} |
do
public class CustomRequestFilter implements ContainerRequestHandler {
public void filter(ContainerRequestContext context) {
Message message = JAXRSUtils.getCurrentMessage();
ClassResourceInfo cri = message.getExchange().get(OperationResourceInfo. class ).getResourceClass();
// or consider using JAX-RS 2.0 ResourceInfo context
// finally use context.abortWith(Response) if you need to block the request
}
} public class CustomResponseFilter implements ContainerResponseFilter {
public void filter(Message message, OperationResourceInfo ori, Response response) {
public class CustomRequestFilter implements ContainerRequestHandler {
public void filter(ContainerRequestContext inContext, ContainerResponseContext outContext) {
Message message = JAXRSUtils.getCurrentMessage();
OperationResourceInfo cri = message.getExchange().get(OperationResourceInfo. class );
// or consider using JAX-RS 2.0 ResourceInfo context
// finally, work with ContainerResponseContext to modify specific Response properties
}
} |
The above is only needed to ease the migration of the existing RequestHandler or ResponseHandler implementations. Prefer writing portable JAX-RS 2.0 filter implementations if possible. CXF interceptors can be used to do the CXF specific code if needed.
2. CXF org.apache.cxf.jaxrs.ext.form.Form has been dropped, please use JAX-RS 2.0 Form instead. For example, use:
import javax.ws.rs.core.Form;
Form form = new Form().param( "a" , "b" );
|
instead of
import org.apache.cxf.jaxrs.ext.form.Form;
Form form = new Form().set( "a" , "b" );
|
3. CXF WebClient and proxy code has been moved to a new cxf-rt-rs-client module.
Also, jaxrs:client elements for injecting proxies have had the namespace changed from from “http://cxf.apache.org/jaxrs” to “http://cxf.apache.org/jaxrs-client”.
Please see JAX-RS Client API page for more information.
4. WADL Auto Generator code has been moved to a new cxf-rt-rs-service-description module.
5. CXF ParameterHandler has been dropped. Please use ParameterConverterProvider instead, it can be used both on the server and client sides.
6. JAX-RS 2.0 introduces a controversial requirement that the default built-in JAX-RS MessageBodyWriter and JAX-RS MessageBodyReader providers are preferred to custom providers supporting the same types unless the custom providers are precisely typed, for example, if you have a custom InputStream reader properly implementing isReadable:
public class MyStreamProvider implements MessageBodyReader<Object> {
public boolean isReadable(Class<?> cls, ...) {
return InputStream. class .isAssignableFrom(cls) || Reader. class .isAssignableFrom(cls);
}
// other methods
} |
then the runtime will ignore it and choose a default InputStream/Reader reader because MyStreamProvider is typed on Object. This was done to deal with the cases where well-known JSON/etc providers are blindly supporting all types in their isReadable methods by always returning \’true\’ and then failing when asked to actually read the incoming stream into InputStream/etc directly. In case of MyStreamProvider, it will need to be split into MyInputStreamProvider and MyReaderProvider typed on InputStream and Reader respectively.
At CXF level, the users which depend on CXF MultipartProvider to have InputStream or String references to multipart attachments will be affected unless they use @Multipart annotation. For example, if we have a multipart payload with a single part/attachment only then the following code:
@POST @Consumes ( "multipart/form-data" )
public void upload(InputStream is) {
} |
which in CXF 2.7.x or earlier will return a pointer to first/single individual part, will actually return a stream representing the complete unprocessed multipart payload. Adding a @Multipart marker will keep the existing code working as expected:
@POST @Consumes ( "multipart/form-data" )
public void upload( @Multipart InputStream is) {
} |
7. If the custom code throws JAX-RS WebApplicationException with Response containing a non-null entity then custom WebApplicationException mappers will be bypassed – another problematic requirement, for example, the custom mappers doing the logging will miss on such exceptions.
Set CXF “support.wae.spec.optimization” property to false to disable it.
From CXF 2.6.x to CXF 2.7.x
Please check the CXF 2.7 Migration Guide for the information about all the changes affecting the JAX-RS users
Maven dependencies
CXF 3.0.0
The cxf-rt-frontend-jaxrs dependency is required:
< dependency >
< groupId >org.apache.cxf</ groupId >
< artifactId >cxf-rt-frontend-jaxrs</ artifactId >
< version >3.0.0-milestone1</ version >
</ dependency >
|
This will in turn pull other CXF modules such cxf-core and cxf-rt-transports-http, check the pom for more information.
javax.ws.rs/javax.ws.rs-api/2.0 dependency provides JAX-RS 2.0 Final API.
Existing JAX-RS 1.1 applications can run in CXF 3.0.0.
CXF 2.7.0
javax.ws.rs/javax.ws.rs-api/2.0-m10 replaces javax.ws.rs/jsr311-api/1.1.1. This is very close to JSR-339 Public Release API level. Users can expect very minor differences in the Final Release of API.
Existing JAX-RS 1.1 applications can run in CXF 2.7.x.
CXF 2.6.x
Please check the CXF 2.6 Migration Guide for the information about all the changes affecting the JAX-RS users. Typically adding the frontend jaxrs dependency should be enough.
1. javax.ws.rs/jsr311-api/1.1.1
Optional providers (including the default JSONProvider) are located in this module:
< dependency >
< groupId >org.apache.cxf</ groupId >
< artifactId >cxf-rt-rs-extension-providers</ artifactId >
< version >2.6.0</ version >
</ dependency >
|
The Search extension is now located in
< dependency >
< groupId >org.apache.cxf</ groupId >
< artifactId >cxf-rt-rs-extension-search</ artifactId >
< version >2.6.0</ version >
</ dependency >
|
Setting up the classpath
If Maven is not used then the following JARs will need to be available at the runtime classpath.
For CXF 3.0.0:
TODO
For CXF 2.7.x:
TODO
CXF JAX-RS bundle
Note CXF JAX-RS bundle has been removed in CXF 3.0.0. Prefer depending on the JAX-RS frontend directly. In CXF 3.0.0 a complete CXF all-inclusive bundle can still be used if really needed.
Only in CXF 2.7.x or earlier:
A standalone JAX-RS bundle is available which may be of interest to users doing the JAX-RS work only.
Please note that this bundle has a transitive Maven dependency on the Jetty server modules. If you are using Maven and working with other servlet containers such as Tomcat then please add the following exclusion:
< dependency >
< groupId >org.apache.cxf</ groupId >
< artifactId >cxf-bundle-jaxrs</ artifactId >
< version >${cxf.version}</ version >
< exclusions >
< exclusion >
< groupId >org.eclipse.jetty</ groupId >
< artifactId >jetty-server</ artifactId >
</ exclusion >
</ exclusions >
</ dependency >
|
What is New
- Complete support for JAX-RS 2.0, please see JAX-RS Basics for more information
- Bean Validation 1.1 Support
- Swagger Feature
Getting Started with JAX-RS
Understanding the Basics
You are encouraged to read JSR-339 specification to find out information not covered by this documentation. The specification introduces many terms such as root resources, resource methods, sub-resources and sub-resource locators, message body readers and writers. JAX-RS 2.0 additionally introduces filters, interceptors, new client API, features, new exception classes, server-side support for asynchronous invocations.
Please see the JAX-RS Basics page for more information.
Support for Data Bindings
JAX-RS MessageBodyReader and MessageBodyWriter can be used to create data bindings for reading and writing data in a number of different formats. Compliant JAX-RS implementations are expected to support JAXB-annotated beans, JAXP Source objects, InputStreams, etc.
In addition, CXF JAX-RS lets users reuse existing CXF DataBindings for working with JAXB, XBeans, Aegis and SDO.
Please see the JAX-RS Data Bindings page for more information.
How Request URI is Matched
Lets assume you have a web application called \’rest\’. CXFServlet\’s url-pattern is “/test/*”. Finally, jaxrs:server\’s address is “/bar”.
Requests like /rest/test/bar or /rest/test/bar/baz will be delivered to one of the resource classes in a given jaxrs:server endpoint. For the former request to be handled, a resource class with @Path(“/”) should be available, in the latter case – at least @Path(“/”) or a more specific @Path(“/baz”).
The same requirement can be expressed by having a CXFServlet with “/*” and jaxrs:server with “/test/bar”.
When both CXFServlet and jaxrs:server use “/” then it\’s a root resource class which should provide a @Path with at least “/test/bar” for the above requests to be matched.
Generally, it can be a good idea to specify the URI segments which are more likely to change now and then with CXFServlets or jaxrs:server.
Client API
CXF 3.0.0 implements JAX-RS 2.0 Client API.
CXF 2.7.x or earlier provides a comprehensive support for developing RESTful clients by supporting 3 flavors of the client API: proxy-based, HTTP-centric and XML-centric. CXF-specific client API is supported alongside new JAX-RS 2.0 Client API in CXF 3.0.0.
Please see the JAX-RS Client API page for more information.
Bean Validation
Bean Validation 1.1 is supported since CXF 3.0.0-milestone1. Please see the JAX-RS Validation for more information.
Filters, Interceptors and Invokers
It is possible to intercept and modify the inbound and outbound calls with the help of CXF JAX-RS filters and/or CXF interceptors. Additionally, custom invokers offer an option to intercept a call immediately before a service bean is invoked.
Please see the JAX-RS Filters page for more information.
Please see the JAX-RS Basics page for more information about new JAX-RS 2.0 filters and interceptors available in CXF 2.7.x and 3.0.0.
Service listings and WADL support
New: Swagger feature has been introduced.
CXF JAX-RS supports WADL. CXF JAX-RS service endpoints can be listed in the service listings page and users can check the WADL documents.
Please see the JAXRS Services Description page for more information.
Configuring JAX-RS services
JAX-RS services can be configured programmatically, using Blueprint, Spring or CXFNonSpringJAXRSServlet.
Please see the JAXRS Services Configuration page for more information.
Testing
JAX-RS services can be easily tested using the embedded Jetty or CXF Local Transport.
Please see the JAXRS Testing page for more information.
Debugging
One may want to use a browser to test how a given HTTP resource reacts to different HTTP Accept or Accept-Language header values and request methods. For example, if a resource class supports a “/resource” URI then one can test the resource class using one of the following queries :
> GET /resource.xml
> GET /resource.en
The runtime will replace \’.xml\’ or \’.en\’ with an appropriate header value. For it to know the type or language value associated with a given URI suffix, some configuration needs to be done. Here\’s an example of how it can be done with Spring:
< jaxrs:server id = "customerService" address = "/" >
< jaxrs:serviceBeans >
< bean class = "org.apache.cxf.jaxrs.systests.CustomerService" />
</ jaxrs:serviceBeans >
< jaxrs:extensionMappings >
< entry key = "json" value = "application/json" />
< entry key = "xml" value = "application/xml" />
</ jaxrs:extensionMappings >
< jaxrs:languageMappings >
< entry key = "en" value = "en-gb" />
</ jaxrs:languageMappings >
</ jaxrs:server >
|
CXF also supports a _type query as an alternative to appending extensions like \’.xml\’ to request URIs:
{{ > GET /resource?_type=xml}}
Overriding a request method is also easy:
> GET /resource?_method=POST
Alternatively, one can specify an HTTP header X-HTTP-Method-Override:
> POST /books
> X-HTTP-Method-Override : PATCH
For example, at the moment the http-centric client API does not support arbitrary HTTP verbs except for those supported
by Java HTTPUrlConnection. When needed, X-HTTP-Method-Override can be set to overcome this limitation.
Finally, a “_ctype” query allows for overriding Content-Type.
Please see the Debugging and Logging page for more information on how to debug and log service calls in CXF.
Logging
Many of the existing CXF features can be applied either to jaxrs:server or jaxrs:client. For example, to enable logging of requests and responses, simply do:
xsi:schemaLocation="http://cxf.apache.org/core
< jaxrs:server >
< jaxrs:features >
< cxf:logging />
</ jaxrs:features >
< jaxrs:server >
</ beans >
|
Please make sure the http://cxf.apache.org/core namespace is in scope.
Starting from CXF 2.3.0 it is also possible to convert log events into Atom entries and either push them to receivers or make them available for polling.
Please see the Debugging and Logging page for more information.
Advanced Features
Multiparts
Multiparts can be handled in a number of ways. The CXF core runtime provides advanced support for handling attachments which CXF JAX-RS builds upon.
Please see the JAX-RS Multiparts page for more information.
Secure JAX-RS services
Transport level HTTPS security can be used to protect messages exchanged between CXF JAX-RS endpoints and providers.
Authentication and authorization can be enforced in a number of ways.
Please see the Secure JAX-RS Services page for more information.
Please also check JAX-RS XML Security, JAX-RS SAML and JAX-RS OAuth2 pages for more information about the advanced security topics.
Failover and Load Distribution Features
Starting from CXF 2.4.1, CXF JAX-RS proxy and WebClient consumers can be backed up by failover and load distribution features.
Please see the JAX-RS Failover page for more information.
Redirection
Starting from CXF 2.2.5 it is possible to redirect the request or response call to other servlet resources by configuring CXFServlet or using CXF JAX-RS RequestDispatcherProvider.
Please see the JAX-RS Redirection page for more information.
XSLT and XPath
XSLT and XPath are promoted and treated as first-class citizens in CXF JAX-RS. These technologies can be very powerful when generating complex data or retrieving data of interest out of complex XML fragments.
Please see the JAX-RS Advanced XML page for more information.
Complex Search Queries
Using query parameter beans provides a way to capture search requirements that can be expressed by enumerating name/value pairs, for example, a query such as \’?name=CXF&version=2.3\’ can be captured by a bean containing setName and setVersion methods. This \’template\’ bean can be used in the code to compare it against all available local data.
Versions 2.3 and later of CXF JAXRS support another option for doing advanced search queries using the Feed Item Query Language(FIQL).
Please see the JAX-RS Search page for more information.
Model-View-Controller support
XSLT
Please see the JAX-RS Advanced XML page for more information. on how XSLTJaxbProvider can be used to generate complex (X)HTML views.
JSP
With the introduction of RequestDispatcherProvider it is now possible for JAXRS service responses be redirected to JSP pages for further processing. Please see the JAX-RS Redirection page for more information.
Combining JAX-WS and JAX-RS
CXF JAX-RS tries to make it easy for SOAP developers to experiment with JAX-RS and combine both JAX-WS and JAX-RS in the same service bean when needed.
Please see the JAX-RS and JAX-WS page for more information.
Integration with Distributed OSGi
Distributed OSGi RI is a CXF subproject. DOSGi mandates how registered Java interfaces can be exposed
and consumed as remote services. DOSGi single and multi bundle distributions contain all the OSGI bundles required for a CXF endpoint be successfully published.
CXF JAX-RS implementations has been integrated with DOSGi RI 1.1-SNAPSHOT which makes it possible to expose Java interfaces as RESTful services and consume such services using a proxy-based client API.
Please see the DOSGI Reference page (\’org.apache.cxf.rs\’ properties) and a greeter_rest sample for more information. Note that this demo can be run exactly as a SOAP-based greeter demo as it registers and consumes a similar (but) JAX-RS annotated GreeterService. In addition, this demo shows how one can register and consume a given interface (GreeterService2) without using explicit JAX-RS annotations but providing an out-of-band user model description.
Other Advanced Features
CXF JAX-RS provides a number of advanced extensions such as the support for the JMS transport, one-way invocations (HTTP and JMS), suspended invocations (HTTP and JMS), making existing code REST-aware by applying external user models, etc.
Please see the JAX-RS Advanced Features page for more information.
Maven Plugins
Please see the JAX-RS Maven Plugins page for more information about the Maven plugins and archetypes which can help with creating CXF JAX-RS applications.
Deployment
CXF JAX-RS applications packaged as WAR archives can be deployed into standalone Servlet containers such as Tomcat or Jetty.
Please see the JAX-RS Deployment page for the tips on how to deploy the CXF JAX-RS applications into various Java EE and OSGI application servers successfully.
Third-party projects
- REST Utilities: RESTUtils
References
- JSR-000311 JAX-RS: The JavaTM API for RESTful Web Services
- Architectural Styles and the Design of Network-based Software Architectures
- Representational State Transfer – Wikipedia
- RESTful Web Services Cookbook – Solutions for Improving Scalability and Simplicity by Subbu Allamarajuy (O\’Reilly Media, February 2010)
- RESTful Java with JAX-RS by Bill Burke (O\’Reilly Media, November 2009)
- Java Web Services: Up and Running by Martin Kalin (O\’Reilly Media, February 2009)
- RESTful Web Services – Web services for the real world by Leonard Richardson, Sam Ruby (O\’Reilly Media, May 2007)
- RESTful Web Services by Sameer Tyagi (Oracle , August 2006)
- RESTful Web Services – “Unofficial homepage for a book about simple web services.” Unknown
- How I Explained REST to My Wife by Ryan Tomayko (http://tomayko.com, December 2004)
How to contribute
CXF JAX-RS implementation sits on top of the core CXF runtime and is quite self-contained and isolated from other CXF modules such as jaxws and simple frontends.
Please check the issue list and see if you are interested in fixing one of the issues.
If you decide to go ahead then the fastest way to start is to
- do the fast trunk build using \’mvn install -Pfastinstall\’
- setup the workspace \’mvn -Psetup.eclipse\’ which will create a workspace in a \’workspace\’ folder, next to \’trunk\’
- import cxf modules from the trunk into the workspace and start working with the cxf-frontend-jaxrs module
If you are about to submit a patch after building a trunk/rt/frontend/jaxrs, then please also run JAX-RS system tests in trunk/systests/jaxrs :
> mvn install
You can also check out the general Getting Involved web page for more information on contributing.