Access WSO2 Registry programatically
When you have many services it is a good thing to register them somewhere so your consumers can easily find them, see what they are about, determine whether the service levels are good enough etc. When you look back at the heyday of SOA everyone was pusing registries (or repositories, but that terminology is a different subject). To fully do SOA you needed a UDDI registry to accompany your SOAP based services, defined by a WSDL and XML Schemas.
But, as with so many interesting ideas, UDDI never really took off. There are still a couple of UDDI repositories out there, but I’ve never seen this used in practice. SOAP and the whole set of WS-* standards however are still much in use in organizations. For public facing APIs on the other hand people most often choose a more RESTful approach using the basic HTTP verbs and JSON.
Open source registries
Regardless of whether your creating an internal SOAP/WS-* based service or a public RESTful API using self-describing resources, it’s very useful to have a central registry that you can use to store meta information on your services: lifecycle, SLA, documentation, policies etc. When you start looking for an open source registry, your options are rather slim. Basically there is, at the moment, in my opinion only one good open source registry: WSO2 Governance Registry.
(full disclosure: I use the WSO2 Governance Registry in my book, so I’m just a little bit biased, but if you know of any other mature open source registry please let me know)
This registry has a nice web frontend that you can use to regsiter resources, search for services and much more. Good to use for your end users or for operations during the runtime phase of your application and services. It would, however, also be nice to access the registry programatically. That way you could provision URLs, documentation, WSDLs and for instance schemas directly from a central location.
Access WSO2 programatically
Luckily the WSO2 guys provide us with a simple REST API and a java based client library you can use to access the repository directly. If you just need access to a resource you can use the REST interface using HTTP basic authentication, for more advanced functionality you should probably use the java based client.
Access using java client
If you want to get all the depencies using Maven for using the java client, you download pretty much the complete WSO2 Registry, and if your working with a fairly new version, you might also run into non-existing or corrupt dependencies. Luckily though, since Maven (unofficially) support wildcard exclusions (see my article on this here). I use the following pom.xml that explicitely defines the minimum set of dependencies to access the WSO2 Registry programatically.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>wsclient</groupId>
<artifactId>wsclient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<repositories>
<repository>
<id>maven2</id>
<url>http://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>wso2-maven2-repository</id>
<url>http://dist.wso2.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-impl</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-api</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.ws.client</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.core.common</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.utils</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.base</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.authenticator.stub</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId> org.wso2.carbon.registry.resource.ui</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<version>3.2.0</version>
<artifactId>org.wso2.carbon.registry.core</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<version>3.2.0</version>
<artifactId>org.wso2.carbon.registry.api</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- excluded since the version in the repo is incorrect -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2</artifactId>
<version>1.6.1</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>1.6.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>1.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-tcp</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>1.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-json</artifactId>
<version>1.6.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-adb</artifactId>
<version>1.6.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
With this pom it is now very easy to access the repository. The default examples reference information in a locally installed WSO2 repository. This isn’t really necessary. You need to set up the correct keystores, and the correct Axis2 configuration, but that’s it. You don’t need a locally installed repository for the client. The following code shows how you can do this:
package soa.governance.chapter2.registry;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.wso2.carbon.registry.core.Association;
import org.wso2.carbon.registry.core.Collection;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.ws.client.registry.WSRegistryServiceClient;
/**
* Simple client that can be used to connect to the repository.
*
* @author jos
*/
public class RegistryClient {
// url where the repository is running its services interface
private static String backendURL = "http://localhost:9763/services/";
private static ConfigurationContext configContext = null;
// configuration locations used to bootstrap axis2
private static String axis2Repo = "src/main/resources/client";
private static String axis2Conf = "src/main/resources/conf/axis2_client.xml";
private static String serverURL = "https://localhost:9443/services/";
/**
* Initialize the repository client
*
* @return an initialized and authenticated client
* @throws Exception
*/
private static WSRegistryServiceClient initialize() throws Exception {
// set these properties, this is used for authentication over https to the registry
// if you have a newer version, you can update the keystore by copying it from
// the security directory of the repository
System.setProperty("javax.net.ssl.trustStore", "src/main/resources/security/wso2carbon.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
System.setProperty("javax.net.ssl.trustStoreType","JKS");
configContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem(axis2Repo, axis2Conf);
return new WSRegistryServiceClient(serverURL, "admin", "admin", backendURL, configContext);
}
/**
* Simple main method that uses the repository to execute a certain operation on the repository
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Registry registry = initialize();
// get the governance folder
Resource governanceFolder = registry.get("/_system/governance");
System.out.println("Folder description: " +
governanceFolder.getDescription());
// get the WSDL folder resource (use the url we browsed to)
String wsdlUrl = "/_system/governance/trunk/wsdls/_0/service_1" +
"/account/wsdl/trafficavoidance/accountService.wsdl";
Resource wsdlResource = registry.get(wsdlUrl);
// output the content of the wsdl
System.out.println(new String((byte[])wsdlResource
.getContent()));
List<Resource> paths = getServicePath(registry, "/_system/governance/trunk/services");
for (Resource service : paths) {
// we've got all the services here
Properties props = service.getProperties();
for (Object prop : props.keySet()) {
System.out.println(prop + " - " + props.get(prop));
}
Association[] associations = registry.getAssociations(service.getPath(), "Documentation");
for (Association association : associations) {
System.out.println(association.getAssociationType());
}
}
}
private static List<Resource> getServicePath(Registry registry, String servicesResource) throws RegistryException {
List<Resource> result = new ArrayList<Resource>();
Resource resource = registry.get(servicesResource);
if (resource instanceof Collection) {
Object content = resource.getContent();
for (Object path : (Object[])content) {
result.addAll(getServicePath(registry,(String)path));
}
} else if (resource instanceof Resource){
result.add(resource);
}
return result;
}
}
As you can see we reference an axis2 configuration file here (axis2_client.xml). This is the standard axis2_client.xml provided by wso2. For completeness sake the content of this file is listed here:
<!--
~ Copyright 2005-2007 WSO2, Inc. (http://wso2.com)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<axisconfig name="AxisJava2.0">
<!-- ================================================= -->
<!-- Parameters -->
<!-- ================================================= -->
<parameter name="hotdeployment">true</parameter>
<parameter name="hotupdate">false</parameter>
<parameter name="enableMTOM">false</parameter>
<!-- commons-http-client defaultMaxConnPerHost -->
<parameter name="defaultMaxConnPerHost">500</parameter>
<!-- commons-http-client maxTotalConnections -->
<parameter name="maxTotalConnections">15000</parameter>
<!--If turned on with use the Accept header of the request to determine the contentType of the
response-->
<parameter name="httpContentNegotiation">false</parameter>
<!--During a fault, stacktrace can be sent with the fault message. The following flag will control -->
<!--that behaviour.-->
<parameter name="sendStacktraceDetailsWithFaults">true</parameter>
<!--If there aren't any information available to find out the fault reason, we set the message of the exception-->
<!--as the faultreason/Reason. But when a fault is thrown from a service or some where, it will be -->
<!--wrapped by different levels. Due to this the initial exception message can be lost. If this flag-->
<!--is set then, Axis2 tries to get the first exception and set its message as the faultreason/Reason.-->
<parameter name="DrillDownToRootCauseForFaultReason">false</parameter>
<!--This is the user name and password of admin console-->
<parameter name="userName">admin</parameter>
<parameter name="password">axis2</parameter>
<!--To override repository/services you need to uncomment following parameter and value SHOULD be absolute file path.-->
<!--ServicesDirectory only works on the following cases-->
<!---File based configurator and in that case the value should be a file URL (http:// not allowed)-->
<!---When creating URL Based configurator with URL “file://” -->
<!--- War based configurator with expanded case , -->
<!--All the other scenarios it will be ignored.-->
<!--<parameter name="ServicesDirectory">service</parameter>-->
<!--To override repository/modules you need to uncomment following parameter and value SHOULD be absolute file path-->
<!--<parameter name="ModulesDirectory">modules</parameter>-->
<!--Following params will set the proper context paths for invocations. All the endpoints will have a commons context-->
<!--root which can configured using the following contextRoot parameter-->
<!--<parameter name="contextRoot">axis2</parameter>-->
<!--Our HTTP endpoints can handle both REST and SOAP. Following parameters can be used to distinguish those endpoints-->
<!--<parameter name="servicePath">services</parameter>-->
<!--<parameter name="restPath">rest</parameter>-->
<!-- Following parameter will completely disable REST handling in Axis2-->
<parameter name="disableREST" locked="false">false</parameter>
<!--POJO deployer , this will alow users to drop .class file and make that into a service-->
<deployer extension=".class" directory="pojo" class="org.apache.axis2.deployment.POJODeployer"/>
<!-- Following parameter will set the host name for the epr-->
<!--<parameter name="hostname" locked="true">myhost.com</parameter>-->
<!-- ================================================= -->
<!-- Message Receivers -->
<!-- ================================================= -->
<!--This is the Default Message Receiver for the system , if you want to have MessageReceivers for -->
<!--all the other MEP implement it and add the correct entry to here , so that you can refer from-->
<!--any operation -->
<!--Note : You can override this for particular service by adding the same element with your requirement-->
<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2006/01/wsdl/in-only"
class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2006/01/wsdl/in-out"
class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
</messageReceivers>
<!-- ================================================= -->
<!-- Message Formatter -->
<!-- ================================================= -->
<!--Following content type to message formatter mapping can be used to implement support for different message -->
<!--format serialization in Axis2. These message formats are expected to be resolved based on the content type. -->
<messageFormatters>
<messageFormatter contentType="application/x-www-form-urlencoded"
class="org.apache.axis2.transport.http.XFormURLEncodedFormatter"/>
<messageFormatter contentType="multipart/form-data"
class="org.apache.axis2.transport.http.MultipartFormDataFormatter"/>
<messageFormatter contentType="application/xml"
class="org.apache.axis2.transport.http.ApplicationXMLFormatter"/>
<messageFormatter contentType="text/xml"
class="org.apache.axis2.transport.http.SOAPMessageFormatter"/>
<messageFormatter contentType="application/soap+xml"
class="org.apache.axis2.transport.http.SOAPMessageFormatter"/>
<!--JSON Message Formatters-->
<messageFormatter contentType="application/json"
class="org.apache.axis2.json.JSONMessageFormatter"/>
<messageFormatter contentType="application/json/badgerfish"
class="org.apache.axis2.json.JSONBadgerfishMessageFormatter"/>
<messageFormatter contentType="text/javascript"
class="org.apache.axis2.json.JSONMessageFormatter"/>
</messageFormatters>
<!-- ================================================= -->
<!-- Message Builders -->
<!-- ================================================= -->
<!--Following content type to builder mapping can be used to implement support for different message -->
<!--formats in Axis2. These message formats are expected to be resolved based on the content type. -->
<messageBuilders>
<messageBuilder contentType="application/xml"
class="org.apache.axis2.builder.ApplicationXMLBuilder"/>
<messageBuilder contentType="application/x-www-form-urlencoded"
class="org.apache.axis2.builder.XFormURLEncodedBuilder"/>
<!--JSON Message Builders-->
<messageBuilder contentType="application/json"
class="org.apache.axis2.json.JSONOMBuilder"/>
<messageBuilder contentType="application/json/badgerfish"
class="org.apache.axis2.json.JSONBadgerfishOMBuilder"/>
<messageBuilder contentType="text/javascript"
class="org.apache.axis2.json.JSONOMBuilder"/>
<!--Left commented because it adds the depandancy of servlet-api to other modules.
Please uncomment to Receive messages in multipart/form-data format-->
<!--<messageBuilder contentType="multipart/form-data"-->
<!--class="org.apache.axis2.builder.MultipartFormDataBuilder"/>-->
</messageBuilders>
<!-- ================================================= -->
<!-- Target Resolvers -->
<!-- ================================================= -->
<!-- Uncomment the following and specify the class name for your TargetResolver to add -->
<!-- a TargetResolver. TargetResolvers are used to process the To EPR for example to -->
<!-- choose a server in a cluster -->
<!--<targetResolvers>-->
<!--<targetResolver class="" />-->
<!--</targetResolvers>-->
<!-- ================================================= -->
<!-- Transport Ins -->
<!-- ================================================= -->
<transportReceiver name="http"
class="org.apache.axis2.transport.http.SimpleHTTPServer">
<parameter name="port">6071</parameter>
<!--If you want to give your own host address for EPR generation-->
<!--uncomment following parameter , and set as you required.-->
<!--<parameter name="hostname">http://myApp.com/ws</parameter>-->
</transportReceiver>
<!--Uncomment if you want to have TCP transport support-->
<!--<transportReceiver name="tcp"
class="org.apache.axis2.transport.tcp.TCPServer">
<parameter name="port">6061</parameter>-->
<!--If you want to give your own host address for EPR generation-->
<!--uncomment following parameter , and set as you required.-->
<!--<parameter name="hostname">tcp://myApp.com/ws</parameter>-->
<!--</transportReceiver>-->
<!-- ================================================= -->
<!-- Transport Outs -->
<!-- ================================================= -->
<!--<transportSender name="jms"-->
<!--class="org.apache.axis2.transport.jms.JMSSender"/>-->
<transportSender name="tcp"
class="org.apache.axis2.transport.tcp.TCPTransportSender"/>
<transportSender name="local"
class="org.apache.axis2.transport.local.LocalTransportSender"/>
<transportSender name="http"
class="org.apache.axis2.transport.http.CommonsHTTPTransportSender">
<parameter name="PROTOCOL">HTTP/1.1</parameter>
<parameter name="Transfer-Encoding">chunked</parameter>
<parameter name="SO_TIMEOUT">60000</parameter>
<parameter name="CONNECTION_TIMEOUT">60000</parameter>
</transportSender>
<transportSender name="https"
class="org.apache.axis2.transport.http.CommonsHTTPTransportSender">
<parameter name="PROTOCOL">HTTP/1.1</parameter>
<parameter name="Transfer-Encoding">chunked</parameter>
<parameter name="SO_TIMEOUT">60000</parameter>
<parameter name="CONNECTION_TIMEOUT">60000</parameter>
</transportSender>
<!--<transportSender name="java"-->
<!--class="org.apache.axis2.transport.java.JavaTransportSender"/>-->
<!-- ================================================= -->
<!-- SOAP Role Configuration -->
<!-- ================================================= -->
<!-- Use the following pattern to configure this axis2
instance to act in particular roles. Note that in
the absence of any configuration, Axis2 will act
only in the ultimate receiver role -->
<!--
<SOAPRoleConfiguration isUltimateReceiver="true">
<role>http://my/custom/role</role>
</SOAPRoleConfiguration>
-->
<!-- ================================================= -->
<!-- Phases -->
<!-- ================================================= -->
<phaseOrder type="InFlow">
<!-- System pre-defined phases -->
<phase name="Transport">
<handler name="RequestURIBasedDispatcher"
class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher">
<order phase="Transport"/>
</handler>
<handler name="SOAPActionBasedDispatcher"
class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher">
<order phase="Transport"/>
</handler>
</phase>
<phase name="Addressing">
<handler name="AddressingBasedDispatcher"
class="org.apache.axis2.dispatchers.AddressingBasedDispatcher">
<order phase="Addressing"/>
</handler>
</phase>
<phase name="Security"/>
<phase name="PreDispatch"/>
<phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
<handler name="RequestURIBasedDispatcher"
class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/>
<handler name="SOAPActionBasedDispatcher"
class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/>
<handler name="RequestURIOperationDispatcher"
class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/>
<handler name="SOAPMessageBodyBasedDispatcher"
class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/>
<handler name="HTTPLocationBasedDispatcher"
class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/>
</phase>
<phase name="RMPhase"/>
<!-- System pre defined phases -->
<!-- After Postdispatch phase module author or or service author can add any phase he want -->
<phase name="OperationInPhase"/>
</phaseOrder>
<phaseOrder type="OutFlow">
<!-- user can add his own phases to this area -->
<phase name="OperationOutPhase"/>
<!--system predefined phase-->
<!--these phase will run irrespective of the service-->
<phase name="RMPhase"/>
<phase name="PolicyDetermination"/>
<phase name="MessageOut"/>
<phase name="Security"/>
</phaseOrder>
<phaseOrder type="InFaultFlow">
<phase name="Addressing">
<handler name="AddressingBasedDispatcher"
class="org.apache.axis2.dispatchers.AddressingBasedDispatcher">
<order phase="Addressing"/>
</handler>
</phase>
<phase name="Security"/>
<phase name="PreDispatch"/>
<phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
<handler name="RequestURIBasedDispatcher"
class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/>
<handler name="SOAPActionBasedDispatcher"
class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/>
<handler name="RequestURIOperationDispatcher"
class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/>
<handler name="SOAPMessageBodyBasedDispatcher"
class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/>
<handler name="HTTPLocationBasedDispatcher"
class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/>
</phase>
<phase name="RMPhase"/>
<!-- user can add his own phases to this area -->
<phase name="OperationInFaultPhase"/>
</phaseOrder>
<phaseOrder type="OutFaultFlow">
<!-- user can add his own phases to this area -->
<phase name="OperationOutFaultPhase"/>
<phase name="RMPhase"/>
<phase name="PolicyDetermination"/>
<phase name="MessageOut"/>
<phase name="Security"/>
</phaseOrder>
</axisconfig>
So if you ever need a repository, I’ve currently haven’t found any better than the one from the guys at WSO2.