Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Examples/DBWS/AdvancedOSGi"

(Generating the DBWS file)
 
(20 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
<css>
 
<css>
   .source-java5 {padding:4px;border:1px solid black;}
+
  .source-sql {padding:1em;border:1px solid black; background-color: white;}
   .source-xml {padding:4px;border:1px solid black;}
+
   .source-java5 {padding:1em;border:1px solid black; background-color: white;}
   .source-text {padding:4px;border:1px solid black;}
+
   .source-xml {padding:1em;border:1px solid black; background-color: white;}
 +
   .source-text {padding:1em;border:1px solid black; background-color: white;}
 
</css>
 
</css>
 
__NOTOC__  
 
__NOTOC__  
 
 
== DBWS in an OSGi Environment  ==
 
== DBWS in an OSGi Environment  ==
 
An Eclipselink DBWS service can be run in an OSGi environment using Javase 6's 'containerless' [http://java.sun.com/javase/6/docs/api/javax/xml/ws/Endpoint.html javax.xml.ws.Endpoint] API.<br/>
 
An Eclipselink DBWS service can be run in an OSGi environment using Javase 6's 'containerless' [http://java.sun.com/javase/6/docs/api/javax/xml/ws/Endpoint.html javax.xml.ws.Endpoint] API.<br/>
In this example, Eclipse Equinox is the OSGi environment.
+
In this example, Eclipse Equinox is the OSGi environment. {{:EclipseLink/Development/DBWS/OSGi/Environment}} {{:EclipseLink/Development/DBWS/OSGi/NewPluginProject}} {{:EclipseLink/Development/DBWS/OSGi/ModifyActivator}} {{:EclipseLink/Development/DBWS/OSGi/RunDBWSProviderActivator}} {{:EclipseLink/Development/DBWS/OSGi/GenerateDBWSFiles}} {{:EclipseLink/Development/DBWS/OSGi/SimpleClient}} {{:EclipseLink/Development/DBWS/OSGi/AdvCustIJetty}} {{:EclipseLink/Development/DBWS/OSGi/AdvCustIIAutoGen}} {{:EclipseLink/Development/DBWS/OSGi/AdvCustIIIJDBC}}
 
+
=== Environment Setup  ===
+
 
+
Download a [http://www.eclipse.org/downloads/packages version of the Eclipse IDE that includes the ''Plug-in Development Environment'' (PDE)] (''Eclipse IDE for Java EE Developers'', ''Eclipse Classic 3.6.0'' or <br> ''Eclipse for RCP and RAP Developers''). We will need an OSGi-friendly JDBC driver - for this example, we will use the Apache Derby bundle:
+
 
+
*Download Apache Derby (org.apache.derby) bundle from [http://download.eclipse.org/tools/orbit/downloads Orbit].
+
 
+
<br>[[Image:EclipseLinkExamplesOSGiDeveloping with EclipseLink OSGi in PDE-2v2.png]]<br>
+
 
+
*Place the <tt>org.apache.derby</tt> bundle into your $ECLIPSE_HOME/dropins folder.
+
 
+
Under 'Preferences' -&gt; 'Plug-in Development' -&gt; 'Target Platform', edit the '''Running Platform (Active)''' <br>
+
[[Image:EclipseLink DBWS with OSGi Target Platform.png]]<br><br>
+
 
+
<font color="red"><i>{Note - once the Eclipse P2 Repository has the correct version of EclipseLink that supports running DBWS with OSGi, <br>
+
the following steps will no longer be required and the EclipseLink target Component can be used}''</i></font> <br>
+
EclipseLink builds after <tt>2.1.2.v20100914-r8170</tt> have EclipseLink DBWS available as a [http://www.eclipse.org/downloads/download.php?file=/rt/eclipselink/nightly/2.1.1/20100805/eclipselink-plugins-2.1.1.v20100805-r7986.zip bundle] <br>
+
Extract the following 6 bundles to some directory:
+
<source lang="text">
+
$ ls additionalBundles/
+
org.eclipse.persistence.asm.source_2.1.2.v20100914-r8170.jar
+
org.eclipse.persistence.asm_2.1.2.v20100914-r8170.jar
+
org.eclipse.persistence.core.source_2.1.2.v20100914-r8170.jar
+
org.eclipse.persistence.core_2.1.2.v20100914-r8170.jar
+
org.eclipse.persistence.dbws.source_2.1.2.v20100914-r8170.jar
+
org.eclipse.persistence.dbws_2.1.2.v20100914-r8170.jar
+
</source>
+
Edit the Target Platform, adding the directory with the additional bundles:<br/>
+
[[Image:EclipseLink DBWS with OSGi UpdatedBundles1.png]]<br/><br/>
+
[[Image:EclipseLink DBWS with OSGi UpdatedBundles2.png]]<br/><br/>
+
Reload the Target Platform
+
 
+
=== Create a new Plug-in project ===
+
From the 'Plug-in Development' Perspective, create a new Plug-in project <b>SimpleTable</b>:<br/>
+
{Note the choice of 'standard' for OSGi framework}<br/>
+
[[Image:EclipseLink DBWS with OSGi SimpleTable.png]]<br/>
+
Proceed to the next panel of the wizard where the (OSGi bundle) Activator for SimpleTable is defined:<br/>
+
[[Image:EclipseLink DBWS with OSGi SimpleTableActivator.png]]<br/>
+
Click 'Finish' and open the generated Activator:
+
 
+
<source lang="java5">
+
package simpletable;
+
 
+
import org.osgi.framework.BundleActivator;
+
import org.osgi.framework.BundleContext;
+
 
+
public class Activator implements BundleActivator {
+
 
+
private static BundleContext context;
+
 
+
static BundleContext getContext() {
+
return context;
+
}
+
 
+
/*
+
* (non-Javadoc)
+
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+
*/
+
public void start(BundleContext bundleContext) throws Exception {
+
Activator.context = bundleContext;
+
}
+
 
+
/*
+
* (non-Javadoc)
+
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+
*/
+
public void stop(BundleContext bundleContext) throws Exception {
+
Activator.context = null;
+
}
+
}
+
</source>
+
=== Modifying Activator to implement <code>javax.xml.ws.Provider</code> ===
+
Change the Activator to extend <code>org.eclipse.persistence.internal.dbws.ProviderHelper</code> (NB - will show errors):<br/>
+
[[Image:EclipseLink DBWS with OSGi ProviderHelperwavy.png]]<br/>
+
In order to correct the errors, the <b>SimpleTable</b> project needs to update its list of required bundles. Open the <tt>MANIFEST.MF</tt> file in the 'Plug-in Manifest Editor' and select the 'Dependencies' tab and add the following bundles:
+
[[Image:EclipseLink DBWS with OSGi Dependencies.png]]<br/>
+
<br/>
+
The Activator needs to be annotated to support a (dynamic) Web services Endpoint:
+
<source lang="java5">
+
public class Constants {
+
    //DBWS Properties
+
    public static final String TEST_PROJECT =
+
    "simpletable";
+
 
+
    //JAX-WS properties
+
    public static final String TEST_NAMESPACE =
+
    "urn:" + TEST_PROJECT;
+
    public static final String TEST_SERVICE =
+
    TEST_PROJECT + "Service";
+
    public static final String TEST_SERVICE_NAMESPACE =
+
    "urn:" + TEST_SERVICE;
+
    public static final String TEST_PORT =
+
    TEST_SERVICE + "Port";
+
    public static final String ENDPOINT_ADDRESS =
+
    "http://localhost:9999/" + TEST_PROJECT;
+
}
+
package simpletable;
+
 
+
//java eXtension imports
+
import javax.xml.namespace.QName;
+
import javax.xml.soap.SOAPMessage;
+
import javax.xml.ws.Endpoint;
+
import javax.xml.ws.Provider;
+
import javax.xml.ws.Service;
+
import javax.xml.ws.WebServiceProvider;
+
import javax.xml.ws.ServiceMode;
+
import static javax.xml.ws.Service.Mode.MESSAGE;
+
import static javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING;
+
 
+
//EclipseLink imports
+
import org.eclipse.persistence.internal.dbws.ProviderHelper;
+
import org.eclipse.persistence.sessions.Session;
+
 
+
//OSGi/PDE imports
+
import org.osgi.framework.BundleActivator;
+
import org.osgi.framework.BundleContext;
+
 
+
//test imports
+
import static simpletable.Constants.ENDPOINT_ADDRESS;
+
import static simpletable.Constants.TEST_PORT;
+
import static simpletable.Constants.TEST_SERVICE_NAMESPACE;
+
import static simpletable.Constants.TEST_SERVICE;
+
 
+
@WebServiceProvider(
+
    targetNamespace = TEST_SERVICE_NAMESPACE,
+
    serviceName = TEST_SERVICE,
+
    portName = TEST_PORT
+
)
+
@ServiceMode(MESSAGE)
+
public class Activator extends ProviderHelper implements BundleActivator, Provider<SOAPMessage> {
+
 
+
private static BundleContext context;
+
private static Endpoint endpoint = null;
+
private static QName portQName = null;
+
private static Service testService = null;
+
 
+
static BundleContext getContext() {
+
return context;
+
}
+
 
+
/*
+
* (non-Javadoc)
+
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+
*/
+
public void start(BundleContext bundleContext) throws Exception {
+
    Activator.context = bundleContext;
+
            super.init(getClass().getClassLoader(), null, false);
+
            endpoint = Endpoint.create(this);
+
            endpoint.publish(ENDPOINT_ADDRESS);
+
            QName serviceQName = new QName(TEST_SERVICE_NAMESPACE, TEST_SERVICE);
+
            portQName = new QName(TEST_SERVICE_NAMESPACE, TEST_PORT);
+
            testService = Service.create(serviceQName);
+
            testService.addPort(portQName, SOAP11HTTP_BINDING, ENDPOINT_ADDRESS);
+
    System.out.println("Hello, " + testService.toString());
+
}
+
 
+
/*
+
* (non-Javadoc)
+
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+
*/
+
public void stop(BundleContext bundleContext) throws Exception {
+
    Activator.context = null;
+
            if (endpoint != null) {
+
                endpoint.stop();
+
            }
+
            super.destroy();
+
    System.out.println("Goodbye");
+
}
+
}
+
</source>
+
 
+
=== Running the DBWS Provider-Activator ===
+
In the Eclipse IDE, select the <code>MANIFEST.MF</code> file and bring up the context menu for 'Run As' -> 'Run Configurations' -> 'OSGi Framework' to create a new launch configuration.<br/><br/>
+
Deselect the 'Include optional dependencies when computing required bundles' and the <br/>
+
'Add new workspace bundles to this launch configuration automatically' check-boxes.<br/><br/>
+
Deselect all bundles and re-select 'SimpleTable' and then click 'Add Required Bundles'. This simplifies the list of Required Bundles:<br/>
+
[[Image:EclipseLink DBWS with OSGi NewLaunch.png]]<br/>
+
<br/>
+
When this launch configuration is run, an exception is thrown indicating that some DBWS files cannot be found. The <b>SimpleTable</b> project needs two additional source-folders. The first folder already exists - add the <code>META-INF</code> folder as a source-folder. The second needs to be created:
+
 
+
[[Image:EclipseLink DBWS with OSGi NewFolders.png]]<br/>
+
<br/>
+
In the next section, we will re-use a previous example [[EclipseLink/Examples/DBWS/DBWSBasicTable|(Basic Table)]] to generate the required files.
+
 
+
=== Generating the DBWS file ===
+
The simple use-case is the creation of a Web service that exposes a database table's <b>CRUD</b> (<b><i><u>C</u></i>reate/<i><u>R</u></i>ead</b>(findByPK,findAll)<b>/<i><u>U</u></i>pdate/<i><u>D</u></i>elete</b>) lifecycle operations. Here is the table <tt>SIMPLETABLE</tt> on my local MySql database:<br/>
+
[[Image:EclipseLink DBWS with OSGi SimpleTableDescription.png]]<br/>
+
<br/>
+
The DBWSBuilder utility requires a DBWS configuration file as input. NB - When running the DBWSBuilder, the setenv.{cmd, sh} file in .../eclipselink/utils/dbws needs to be updated with the path to JDBC driver jar:
+
<source lang="text">
+
@REM User MUST set DRIVER_CLASSPATH to point to their desired driver jar(s). For example:
+
set DRIVER_CLASSPATH=C:\external\libs\mysql-connector-java-5.1.13-bin.jar
+
</source>
+
<source lang="xml">
+
<?xml version="1.0" encoding="UTF-8"?>
+
<dbws-builder xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
  <properties>
+
    <property name="projectName">simpleTable</property>
+
    <property name="logLevel">fine</property>
+
    <property name="username">user</property>
+
    <property name="password">password</property>
+
    <property name="url">jdbc:mysql://localhost:3306/emp</property>
+
    <property name="driver">com.mysql.jdbc.Driver</property>
+
    <property name="platformClassname">org.eclipse.persistence.platform.database.DerbyPlatform</property>
+
  </properties>
+
  <table
+
    tableNamePattern="SIMPLETABLE"
+
  />
+
</dbws-builder>
+
</source>
+
<source lang="text">
+
prompt > dbwsbuilder.cmd -builderFile dbws-builder.xml -stageDir $ROOT/output_directory -packageAs:noArchive javase
+
</source>
+
 
+
The above builder file is a little 'weird' as it specifies how to log in to my local MySql database, but it declares that the platform is Derby. The following files are generated:
+
<source lang="text">
+
$ROOT
+
+
├───output_directory
+
│      DBWSProvider.class
+
│      DBWSProvider.java
+
│      eclipselink-dbws-or.xml
+
│      eclipselink-dbws-ox.xml
+
│      eclipselink-dbws-schema.xsd
+
│      eclipselink-dbws-sessions.xml
+
│      eclipselink-dbws.wsdl
+
│      eclipselink-dbws.xml
+
│      ProviderListener.class
+
│      ProviderListener.java
+
</source>
+
 
+
Copy the <code>eclipselink-dbws.xml</code>, <code>eclipselink-dbws-or.xml</code>, <code>eclipselink-dbws-ox.xml</code> and <code>eclipselink-dbws-sessions.xml</code> to the <tt>META-INF</tt> directory; <code>eclipselink-dbws-schema.xsd</code> and <code>eclipselink-dbws.wsdl</code> to the <tt>wsdl</tt> directory.
+
 
+
The <code>eclipselink-dbws-sessions.xml</code> needs some manual updates:
+
<source lang="xml">
+
<?xml version="1.0" encoding="UTF-8"?>
+
<sessions version="2.0.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
  <session xsi:type="database-session">
+
      <name>simpletable-dbws-or-session</name>
+
      <logging xsi:type="eclipselink-log">
+
        <log-level>fine</log-level>
+
      </logging>
+
      <!-- add the META-INF dir to the path to find the OR project -->
+
      <primary-project xsi:type="xml">META-INF/eclipselink-dbws-or.xml</primary-project>
+
      <login xsi:type="database-login">
+
        <platform-class>org.eclipse.persistence.platform.database.DerbyPlatform</platform-class>
+
        <!-- change the driver class to use Derby's EmbeddedDriver -->
+
        <driver-class>org.apache.derby.jdbc.EmbeddedDriver</driver-class>
+
        <!-- change the URL: use Derby's 'create' attribute to create database if it doesn't already exist -->
+
        <connection-url>jdbc:derby:test;create=true</connection-url>
+
        <byte-array-binding>false</byte-array-binding>
+
        <optimize-data-conversion>false</optimize-data-conversion>
+
        <trim-strings>false</trim-strings>
+
      </login>
+
  </session>
+
  <session xsi:type="database-session">
+
      <name>simpletable-dbws-ox-session</name>
+
      <logging xsi:type="eclipselink-log">
+
        <log-level>off</log-level>
+
      </logging>
+
      <!-- add the META-INF dir to the path to find the OX project -->
+
      <primary-project xsi:type="xml">META-INF/eclipselink-dbws-ox.xml</primary-project>
+
  </session>
+
</sessions>
+
</source>
+
Since the database won't exist the first time the service is run, need to create the table and populate a few rows:
+
<source lang="java5">
+
public class Constants {
+
...
+
   
+
    //database smts
+
    public static final String CREATE_TABLE =
+
    "CREATE TABLE SIMPLETABLE (\n" +
+
    "  id NUMERIC NOT NULL,\n" +
+
    "  name VARCHAR(25),\n" +
+
    "  since DATE,\n" +
+
    "  PRIMARY KEY (id)\n" +
+
    ")";
+
    public static final String INS1 =
+
    "INSERT INTO SIMPLETABLE (id, name, since) VALUES (1, 'mike', '2001-12-25')";
+
    public static final String INS2 =
+
"INSERT INTO SIMPLETABLE (id, name, since) VALUES (2, 'blaise','2001-12-25')";
+
    public static final String INS3 =
+
"INSERT INTO SIMPLETABLE (id, name, since) VALUES (3, 'rick','2001-12-25')";
+
}
+
 
+
...
+
import static simpletable.Constants.CREATE_TABLE;
+
import static simpletable.Constants.INS1;
+
import static simpletable.Constants.INS2;
+
import static simpletable.Constants.INS3;
+
...
+
 
+
@ServiceMode(MESSAGE)
+
public class Activator extends ProviderHelper implements BundleActivator, Provider<SOAPMessage> {
+
@Override
+
public void loginSessions() {
+
super.loginSessions();
+
Session orSession = xrService.getORSession();
+
try {
+
orSession.executeNonSelectingSQL(CREATE_TABLE);
+
orSession.executeNonSelectingSQL(INS1);
+
orSession.executeNonSelectingSQL(INS2);
+
orSession.executeNonSelectingSQL(INS3);
+
}
+
catch (Exception e) {
+
// table & rows already exist - ignore
+
}
+
}
+
...
+
</source>
+
Running the <b>SimpleTable</b> target should now work:
+
<source lang="text">
+
osgi> [EL Info]: 2010-08-16 16:02:10.68--DatabaseSessionImpl(17240206)--Thread(Thread[Start Level Event Dispatcher,5,main])--EclipseLink, version: Eclipse Persistence Services - 2.1.1.v20100805-r7986
+
[EL Config]: 2010-08-16 16:02:10.68--DatabaseSessionImpl(17240206)--Connection(13623369)--Thread(Thread[Start Level Event Dispatcher,5,main])--connecting(DatabaseLogin(
+
platform=>DerbyPlatform
+
user name=> ""
+
datasource URL=> "jdbc:derby:test;create=true"
+
))
+
[EL Config]: 2010-08-16 16:02:11.305--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--Connected: jdbc:derby:test
+
User: APP
+
Database: Apache Derby  Version: 10.5.1.1 - (764942)
+
Driver: Apache Derby Embedded JDBC Driver  Version: 10.5.1.1 - (764942)
+
[EL Info]: 2010-08-16 16:02:11.305--DatabaseSessionImpl(17240206)--Thread(Thread[Start Level Event Dispatcher,5,main])--simpletable-dbws-or-session login successful
+
[EL Fine]: 2010-08-16 16:02:11.305--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--CREATE TABLE SIMPLETABLE (
+
  id NUMERIC NOT NULL,
+
  name VARCHAR(25),
+
  since DATE,
+
  PRIMARY KEY (id)
+
)
+
[EL Fine]: 2010-08-16 16:02:11.383--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--INSERT INTO SIMPLETABLE (id, name, since) VALUES (1, mike, 2001-12-25)
+
[EL Fine]: 2010-08-16 16:02:11.43--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--INSERT INTO SIMPLETABLE (id, name, since) VALUES (2, blaise,2001-12-25)
+
[EL Fine]: 2010-08-16 16:02:11.43--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--INSERT INTO SIMPLETABLE (id, name, since) VALUES (3, rick,2001-12-25)
+
Hello, javax.xml.ws.Service@1bd06bf
+
</source>
+
 
+
=== A Simple(!) JAX-WS Client ===
+
Typically, JAX-WS Client are generated - either partially or completely - based on the published WSDL. In the example we can see that while the WSDL file is generated, it is not available via a 'live' URL (some tools exist to generate Clients from standalone WSDL files).
+
 
+
An alternate (simpler?) option is to use the <code>javax.xml.ws.Dispatch</code> API to directly send pre-computed SOAP messages:
+
<source lang="java5">
+
package simpletable;
+
 
+
//javase imports
+
import java.io.StringReader;
+
import java.io.StringWriter;
+
import org.w3c.dom.Document;
+
 
+
//java eXtension imports
+
import javax.xml.namespace.QName;
+
import javax.xml.soap.MessageFactory;
+
import javax.xml.soap.SOAPException;
+
import javax.xml.soap.SOAPMessage;
+
import javax.xml.transform.Source;
+
import javax.xml.transform.Transformer;
+
import javax.xml.transform.TransformerConfigurationException;
+
import javax.xml.transform.TransformerException;
+
import javax.xml.transform.TransformerFactory;
+
import javax.xml.transform.dom.DOMResult;
+
import javax.xml.transform.dom.DOMSource;
+
import javax.xml.transform.stream.StreamResult;
+
import javax.xml.transform.stream.StreamSource;
+
import javax.xml.ws.Dispatch;
+
import javax.xml.ws.Service;
+
import static javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING;
+
 
+
//EclipseLink imports
+
import org.eclipse.persistence.platform.xml.XMLComparer;
+
import org.eclipse.persistence.platform.xml.XMLParser;
+
import org.eclipse.persistence.platform.xml.XMLPlatform;
+
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
+
 
+
//test imports
+
import static simpletable.Constants.ENDPOINT_ADDRESS;
+
import static simpletable.Constants.TEST_NAMESPACE;
+
import static simpletable.Constants.TEST_PORT;
+
import static simpletable.Constants.TEST_SERVICE;
+
import static simpletable.Constants.TEST_SERVICE_NAMESPACE;
+
 
+
public class Client {
+
 
+
    static XMLComparer comparer = new XMLComparer();
+
    static XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
+
    static XMLParser xmlParser = xmlPlatform.newXMLParser();
+
 
+
    static final String SOAP_FINDBYPK_REQUEST =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
          "<SOAP-ENV:Body>" +
+
            "<findByPrimaryKey_simpletableType xmlns=\"urn:simpletableService\">" +
+
              "<id>1</id>" +
+
            "</findByPrimaryKey_simpletableType>" +
+
          "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_FINDALL_REQUEST =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
          "<SOAP-ENV:Body>" +
+
            "<findAll_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\"/>" +
+
          "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_UPDATE_REQUEST =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
          "<SOAP-ENV:Body>" +
+
            "<update_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
+
              "<theInstance>" +
+
                "<urn:simpletableType>" +
+
                  "<urn:id>1</urn:id>" +
+
                  "<urn:name>mike norman</urn:name>" +
+
                  "<urn:since>2010-12-25</urn:since>" +
+
                "</urn:simpletableType>" +
+
              "</theInstance>" +
+
            "</update_simpletableType>" +
+
          "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_CREATE_REQUEST =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
          "<SOAP-ENV:Body>" +
+
            "<create_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
+
              "<theInstance>" +
+
                "<urn:simpletableType>" +
+
                  "<urn:id>4</urn:id>" +
+
                  "<urn:name>test</urn:name>" +
+
                  "<urn:since>2009-03-27</urn:since>" +
+
                "</urn:simpletableType>" +
+
              "</theInstance>" +
+
            "</create_simpletableType>" +
+
          "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_DELETE_REQUEST =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
          "<SOAP-ENV:Body>" +
+
            "<delete_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
+
              "<id>4</id>" +
+
            "</delete_simpletableType>" +
+
          "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_UPDATE2_REQUEST =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
          "<SOAP-ENV:Body>" +
+
            "<update_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
+
              "<theInstance>" +
+
                "<urn:simpletableType>" +
+
                  "<urn:id>1</urn:id>" +
+
                  "<urn:name>mike</urn:name>" +
+
                  "<urn:since>2001-12-25</urn:since>" +
+
                "</urn:simpletableType>" +
+
              "</theInstance>" +
+
            "</update_simpletableType>" +
+
          "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
 
+
    public static void main(String[] args) throws SOAPException, TransformerException {
+
 
+
        QName qname = new QName(TEST_SERVICE_NAMESPACE, TEST_PORT);
+
        Service service = Service.create(new QName(TEST_NAMESPACE, TEST_SERVICE));
+
        service.addPort(qname, SOAP11HTTP_BINDING, ENDPOINT_ADDRESS);
+
        Dispatch<SOAPMessage> sourceDispatch = service.createDispatch(qname, SOAPMessage.class,
+
            Service.Mode.MESSAGE);
+
 
+
        SOAPMessage request = createSOAPMessage(SOAP_FINDBYPK_REQUEST);
+
        SOAPMessage result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            Source src = result.getSOAPPart().getContent();
+
            DOMResult domResult = new DOMResult();
+
            getTransformer().transform(src, domResult);
+
            Document resultDoc = (Document)domResult.getNode();
+
            System.out.println(documentToString(resultDoc));
+
            Document controlDoc = xmlParser.parse(new StringReader(SOAP_FINDBYPK_RESPONSE));
+
            boolean nodeEqual = comparer.isNodeEqual(controlDoc, resultDoc);
+
            if (!nodeEqual) {
+
                System.out.println("findByPrimaryKey_simpletableType response not same as control document");
+
            }
+
        }
+
 
+
        request = createSOAPMessage(SOAP_FINDALL_REQUEST);
+
        result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            Source src = result.getSOAPPart().getContent();
+
            DOMResult domResult = new DOMResult();
+
            getTransformer().transform(src, domResult);
+
            Document resultDoc = (Document)domResult.getNode();
+
            Document controlDoc = xmlParser.parse(new StringReader(SOAP_FINDALL_RESPONSE));
+
            boolean nodeEqual = comparer.isNodeEqual(controlDoc, resultDoc);
+
            if (!nodeEqual) {
+
                System.out.println("findAll_simpletableType response not same as control document");
+
            }
+
        }
+
 
+
        request = createSOAPMessage(SOAP_UPDATE_REQUEST);
+
        result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
+
            if (!SOAP_UPDATE_RESPONSE_ELEMENTNAME.equals(localName)) {
+
                System.out.println(SOAP_UPDATE_RESPONSE_ELEMENTNAME + " incorrect");
+
            }
+
        }
+
 
+
        request = createSOAPMessage(SOAP_FINDBYPK_REQUEST);
+
        result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            Source src = result.getSOAPPart().getContent();
+
            DOMResult domResult = new DOMResult();
+
            getTransformer().transform(src, domResult);
+
            Document resultDoc = (Document)domResult.getNode();
+
            Document controlDoc = xmlParser.parse(new StringReader(SOAP_FINDBYPK_AFTERUPDATE_RESPONSE));
+
            boolean nodeEqual = comparer.isNodeEqual(controlDoc, resultDoc);
+
            if (!nodeEqual) {
+
                System.out.println("findByPrimaryKey_simpletableType response (after update) not same as control document");
+
            }
+
        }
+
 
+
        request = createSOAPMessage(SOAP_CREATE_REQUEST);
+
        result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
+
            if (!SOAP_CREATE_RESPONSE_ELEMENTNAME.equals(localName)) {
+
                System.out.println(SOAP_CREATE_RESPONSE_ELEMENTNAME + " incorrect");
+
            }
+
        }
+
 
+
        request = createSOAPMessage(SOAP_DELETE_REQUEST);
+
        result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
+
            if (!SOAP_DELETE_RESPONSE_ELEMENTNAME.equals(localName)) {
+
                System.out.println(SOAP_DELETE_RESPONSE_ELEMENTNAME + " incorrect");
+
            }
+
        }
+
 
+
        request = createSOAPMessage(SOAP_UPDATE2_REQUEST);
+
        result = sourceDispatch.invoke(request);
+
        if (result != null) {
+
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
+
            if (!SOAP_UPDATE_RESPONSE_ELEMENTNAME.equals(localName)) {
+
                System.out.println(SOAP_UPDATE_RESPONSE_ELEMENTNAME + " incorrect");
+
            }
+
        }
+
 
+
    }
+
    static final String SOAP_FINDBYPK_RESPONSE =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
            "<SOAP-ENV:Header/>" +
+
            "<SOAP-ENV:Body>" +
+
                "<srvc:findByPrimaryKey_simpletableTypeResponse xmlns=\"urn:simpletable\" xmlns:srvc=\"urn:simpletableService\">" +
+
                    "<srvc:result>" +
+
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
+
                            "<id>1</id>" +
+
                            "<name>mike</name>" +
+
                            "<since>2001-12-25</since>" +
+
                        "</simpletableType>" +
+
                    "</srvc:result>" +
+
                "</srvc:findByPrimaryKey_simpletableTypeResponse>" +
+
            "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_FINDALL_RESPONSE =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
            "<SOAP-ENV:Header/>" +
+
            "<SOAP-ENV:Body>" +
+
                "<srvc:findAll_simpletableTypeResponse xmlns=\"urn:simpletable\" xmlns:srvc=\"urn:simpletableService\">" +
+
                    "<srvc:result>" +
+
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
+
                            "<id>1</id>" +
+
                            "<name>mike</name>" +
+
                            "<since>2001-12-25</since>" +
+
                        "</simpletableType>" +
+
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
+
                            "<id>2</id>" +
+
                            "<name>blaise</name>" +
+
                            "<since>2001-12-25</since>" +
+
                        "</simpletableType>" +
+
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
+
                            "<id>3</id>" +
+
                            "<name>rick</name>" +
+
                            "<since>2001-12-25</since>" +
+
                        "</simpletableType>" +
+
                    "</srvc:result>" +
+
                "</srvc:findAll_simpletableTypeResponse>" +
+
            "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_UPDATE_RESPONSE_ELEMENTNAME =
+
        "update_simpletableTypeResponse";
+
    static final String SOAP_FINDBYPK_AFTERUPDATE_RESPONSE =
+
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+
            "<SOAP-ENV:Header/>" +
+
            "<SOAP-ENV:Body>" +
+
                "<srvc:findByPrimaryKey_simpletableTypeResponse xmlns=\"urn:simpletable\" xmlns:srvc=\"urn:simpletableService\">" +
+
                    "<srvc:result>" +
+
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
+
                            "<id>1</id>" +
+
                            "<name>mike norman</name>" +
+
                            "<since>2010-12-25</since>" +
+
                        "</simpletableType>" +
+
                    "</srvc:result>" +
+
                "</srvc:findByPrimaryKey_simpletableTypeResponse>" +
+
            "</SOAP-ENV:Body>" +
+
        "</SOAP-ENV:Envelope>";
+
    static final String SOAP_CREATE_RESPONSE_ELEMENTNAME =
+
        "create_simpletableTypeResponse";
+
    static final String SOAP_DELETE_RESPONSE_ELEMENTNAME =
+
        "delete_simpletableTypeResponse";
+
 
+
    static SOAPMessage createSOAPMessage(String message) {
+
        try {
+
            MessageFactory factory = MessageFactory.newInstance();
+
            SOAPMessage soapMessage = factory.createMessage();
+
            soapMessage.getSOAPPart().setContent(
+
                (Source)new StreamSource(new StringReader(message)));
+
            soapMessage.saveChanges();
+
            return soapMessage;
+
        }
+
        catch (Exception e) {
+
            e.printStackTrace();
+
            return null;
+
        }
+
    }
+
    static Transformer getTransformer() {
+
        Transformer transformer = null;
+
        try {
+
            TransformerFactory tf = TransformerFactory.newInstance();
+
            transformer = tf.newTransformer();
+
        }
+
        catch (TransformerConfigurationException e) {
+
            /* extremely rare, safe to ignore */
+
        }
+
        return transformer;
+
    }
+
    static String documentToString(Document doc) {
+
        DOMSource domSource = new DOMSource(doc);
+
        StringWriter stringWriter = new StringWriter();
+
        StreamResult result = new StreamResult(stringWriter);
+
        try {
+
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
+
            transformer.setOutputProperty("indent", "yes");
+
            transformer.transform(domSource, result);
+
            return stringWriter.toString();
+
        } catch (Exception e) {
+
            // e.printStackTrace();
+
            return "<empty/>";
+
        }
+
    }
+
}
+
</source>
+
 
+
=== Advanced Customization - Part I: Jetty ===
+
This example relies on the built-in JavaSE HTTP server (<code>com.sun.net.httpserver.HttpServer</code>) which has some known issues ([http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6675392 6675392], [http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6946825 6946825]). A user may wish to 'swap-in' a different HTTP Server, such as [http://www.eclipse.org/jetty Jetty], which can be added to the PDE Target Platform from the Helios P2 Update Repository:<br/>
+
<br/>
+
[[Image:EclipseLink DBWS with OSGi in PDE.png]]
+
<br/>
+
<br/>
+
Open the Manifest Editor and add the following to the Required Plug-ins list of dependencies:
+
<source lang="text">
+
org.eclipse.jetty.continuation
+
org.eclipse.jetty.http
+
org.eclipse.jetty.io
+
org.eclipse.jetty.server
+
org.eclipse.jetty.util
+
</source>
+
There is a system property to override the built-in JavaSE HTTP server class:
+
<source lang="text">
+
-Dcom.sun.net.httpserver.HttpServerProvider=org.eclipse.jetty.jaxws2spi.JettyHttpServerProvider
+
</source>
+
However, the SPI used to load the new <code>HttpServerProvider</code> class is not OSGi-friendly; a 'revised' version of <code>com.sun.net.httpserver.spi.HttpServerProvider</code> (using source from the OpenJDK 6 project) is changed to use the Thread Context Classloader instead of the System Classloader. The revised code is pre-pended to the JVM's bootclassloader so as to override the version that is built-in to JDK 6:
+
<source lang="text">
+
-Xbootclasspath/p:c:/temp/http_server_spi.jar
+
</source>
+
The new <code>HttpServerProvider</code> can be found in maven <tt>.../org/mortbay/jetty/jetty-jaxws2spi/7.0.1.v20091125</tt> in the package <code>org.eclipse.jetty.jaxws2spi</code><br/>
+
<source lang="text">
+
prompt> ls
+
JAXWS2ContextHandler.java  JettyHttpServer.java
+
JettyHttpContext.java      JettyHttpServerProvider.java
+
JettyHttpExchange.java    ThreadPoolExecutorAdapter.java
+
</source>
+
Once the above classes are added to the project and the required configuration changes are done, the existing code in <code>simpletable.Activator</code> will run - using Jetty - without modification.<br/>
+
[[Image:EclipseLink DBWS with OSGi SimpleTable with Jetty running.png]]
+
<br/>
+
 
+
=== Advanced Customization - Part II: Auto-generation of EclipseLink metadata  ===
+
 
+
The files under <tt>META-INF</tt> and <tt>wsdl</tt> (<tt>eclipselink-dbws.xml</tt>, <tt>eclipselink-dbws-or.xml</tt>, etc.) can be generated at runtime when the Activator is first initialized. Starting with EclipseLink 2.1.2, portions of the initialization of <code>org.eclipse.persistence.internal.dbws.ProviderHelper</code> can be sub-classed:<br>
+
<source lang="java5">
+
 
+
protected InputStream initXRServiceStream(ClassLoader parentClassLoader, ServletContext sc) {
+
...
+
 
+
protected InputStream initXRSchemaStream(ClassLoader parentClassLoader, ServletContext sc) {
+
...
+
   
+
protected InputStream initWSDLInputStream(ClassLoader parentClassLoader, ServletContext sc) {
+
....
+
</source>
+
From the [http://www.eclipse.org/downloads/download.php?file=/rt/eclipselink/nightly/2.1.2/20100914/eclipselink-2.1.2.v20100914-r8170.zip  EclipseLink installation], add <tt>eclipselink-dbwsutils.jar</tt> and <tt>javax.wsdl_1.6.2.v200806030405.jar</tt> to the '''SimpleTable''' project, under a <tt>lib</tt> dir: <br>
+
[[Image:EclipseLinkExamplesOSGiDeveloping_extraLibJars.png]]
+
 
+
Open the <tt>MANIFEST.MF</tt> file in the 'Plug-in Manifest Editor' and select the 'Runtime' tab and add the above jars to the plug-in's classpath:
+
[[Image:EclipseLinkExamplesOSGiDeveloping_runtimeClasspath.png]]
+
 
+
<code>DBWSBuilder</code> is normally invoked as a command-line tool along with a file containing 'operations' that are converted to DBWS runtime artifacts (<tt>eclipselink-dbws.xml</tt>, <tt>eclipselink-dbws-or.xml</tt>, etc.) It is also possible to '''program''' an instance of <code>DBWSBuilder</code>:
+
<source lang="java5">
+
.... additional constants
+
//database properties
+
public static final String DATABASE_URL =
+
    "jdbc:derby:test;create=true";
+
public static final String DATABASE_DRIVER =
+
    "org.apache.derby.jdbc.EmbeddedDriver";
+
public static final String DATABASE_PLATFORM =
+
    "org.eclipse.persistence.platform.database.DerbyPlatform";
+
 
+
... additional imports in simpletable.Activator
+
//EclipseLink imports
+
import org.eclipse.persistence.internal.databaseaccess.Platform;
+
import org.eclipse.persistence.internal.dbws.ProviderHelper;
+
import org.eclipse.persistence.internal.helper.ConversionManager;
+
import org.eclipse.persistence.internal.xr.ProjectHelper;
+
import org.eclipse.persistence.internal.xr.XRDynamicClassLoader;
+
import org.eclipse.persistence.logging.SessionLog;
+
import org.eclipse.persistence.oxm.XMLContext;
+
import org.eclipse.persistence.oxm.XMLLogin;
+
import org.eclipse.persistence.sessions.DatabaseLogin;
+
import org.eclipse.persistence.sessions.DatabaseSession;
+
import org.eclipse.persistence.sessions.DatasourceLogin;
+
import org.eclipse.persistence.sessions.Project;
+
import org.eclipse.persistence.sessions.Session;
+
import org.eclipse.persistence.sessions.factories.XMLProjectReader;
+
import org.eclipse.persistence.tools.dbws.DBWSBuilder;
+
import org.eclipse.persistence.tools.dbws.JavasePackager;
+
import org.eclipse.persistence.tools.dbws.TableOperationModel;
+
import static org.eclipse.persistence.logging.AbstractSessionLog.translateStringToLoggingLevel;
+
import static org.eclipse.persistence.tools.dbws.DBWSBuilder.NO_SESSIONS_FILENAME;
+
import static org.eclipse.persistence.tools.dbws.DBWSBuilder.SESSIONS_FILENAME_KEY;
+
import static org.eclipse.persistence.tools.dbws.DBWSPackager.ArchiveUse.noArchive;
+
import static org.eclipse.persistence.tools.dbws.XRPackager.__nullStream;
+
 
+
... additional member variables in simpletable.Activator
+
 
+
private static DBWSBuilder builder = new DBWSBuilder();
+
private static ByteArrayOutputStream DBWS_SERVICE_STREAM = new ByteArrayOutputStream();
+
private static ByteArrayOutputStream DBWS_SCHEMA_STREAM = new ByteArrayOutputStream();
+
private static ByteArrayOutputStream DBWS_OR_STREAM = new ByteArrayOutputStream();
+
private static ByteArrayOutputStream DBWS_OX_STREAM = new ByteArrayOutputStream();
+
private static ByteArrayOutputStream DBWS_WSDL_STREAM = new ByteArrayOutputStream();
+
 
+
... additional setup required when bundle starts
+
public void start(BundleContext bundleContext) throws Exception {
+
    Activator.context = bundleContext;
+
 
+
    builder.quiet = true;
+
    //set properties
+
    builder.setProjectName("simpletable");
+
    builder.setLogLevel(SessionLog.FINE_LABEL);
+
    builder.setDriver(DATABASE_DRIVER);
+
    builder.setPlatformClassname(DATABASE_PLATFORM);
+
    builder.setUrl(DATABASE_URL);
+
    builder.getProperties().put(SESSIONS_FILENAME_KEY, NO_SESSIONS_FILENAME);
+
    TableOperationModel tModel = new TableOperationModel();
+
    tModel.setName("simpleTable");
+
    tModel.setTablePattern("SIMPLETABLE");
+
    builder.getOperations().add(tModel);
+
    JavasePackager packager = new JavasePackager() {
+
@Override
+
public void start() {
+
// normally start() checks for the existence of a temp directory -
+
// no need since all DBWSBuilder artifacts are in-memory
+
}
+
    };
+
    packager.setArchiveUse(noArchive);
+
    builder.setPackager(packager);
+
    try {
+
        // can't go thru java.sql.DriverManager 'cause it uses Class.forName() - not OSGi-friendly
+
        Driver driver = new org.apache.derby.jdbc.EmbeddedDriver();
+
        Connection conn = driver.connect(DATABASE_URL, null);
+
        builder.setConnection(conn);
+
    }
+
    catch (Exception e) {
+
        // find out what happened
+
        e.printStack();
+
throw e;
+
    }
+
    builder.build(DBWS_SCHEMA_STREAM, __nullStream, DBWS_SERVICE_STREAM, DBWS_OR_STREAM,
+
      DBWS_OX_STREAM, __nullStream, __nullStream, DBWS_WSDL_STREAM, __nullStream, __nullStream,
+
      __nullStream, __nullStream, null);
+
    super.init(this.getClass().getClassLoader(), null, false);
+
 
+
    // regular setup of Endpoint
+
    endpoint = Endpoint.create(this);
+
    endpoint.publish(ENDPOINT_ADDRESS); 
+
    ....
+
</source>
+
The call to <code>super.init(XRDynamicClassLoader, null, false)</code> makes callbacks to the three <code>init</code> routines mentioned above where we return the information computed in <code>builder.build(DBWS_SCHEMA_STREAM,...)</code>:
+
<source lang="java5">
+
 
+
protected InputStream initXRServiceStream(ClassLoader parentClassLoader, ServletContext sc) {
+
    return new ByteArrayInputStream(DBWS_SERVICE_STREAM.toByteArray());
+
}
+
 
+
protected InputStream initXRSchemaStream(ClassLoader parentClassLoader, ServletContext sc) {
+
    return new ByteArrayInputStream(DBWS_SCHEMA_STREAM.toByteArray());
+
}
+
   
+
protected InputStream initWSDLInputStream(ClassLoader parentClassLoader, ServletContext sc) {
+
    return new ByteArrayInputStream(DBWS_WSDL_STREAM.toByteArray());
+
}
+
</source>
+
 
+
The final step is to build the EclipseLink sessions - Override <code>buildSessions()</code> (looks complicated but its just 'plumbing'): <source lang="java5">
+
...
+
import static org.eclipse.persistence.logging.AbstractSessionLog.translateStringToLoggingLevel;
+
...
+
@Override
+
public void buildSessions() {
+
    XRDynamicClassLoader xrdecl = new XRDynamicClassLoader(parentClassLoader);
+
    Project oxProject = XMLProjectReader.read(new StringReader(DBWS_OX_STREAM.toString()),
+
        xrdecl);
+
    ((XMLLogin)oxProject.getDatasourceLogin()).setEqualNamespaceResolvers(false);
+
    Project orProject = XMLProjectReader.read(new StringReader(DBWS_OR_STREAM.toString()),
+
        xrdecl);
+
    DatasourceLogin login = orProject.getLogin();
+
    ((DatabaseLogin)login).setConnectionString(builder.getUrl());
+
    ((DatabaseLogin)login).setDriverClassName(builder.getDriver());
+
    Platform platform = builder.getDatabasePlatform();
+
    ConversionManager cm = platform.getConversionManager();
+
    cm.setLoader(xrdecl);
+
    login.setDatasourcePlatform(platform);
+
    ((DatabaseLogin)login).bindAllParameters();
+
    orProject.setDatasourceLogin(login);
+
    ProjectHelper.fixOROXAccessors(orProject, oxProject);
+
    DatabaseSession databaseSession = orProject.createDatabaseSession();
+
    int logLevel = translateStringToLoggingLevel(builder.getLogLevel());
+
    if (SessionLog.OFF == logLevel) {
+
        databaseSession.dontLogMessages();
+
    }
+
    else {
+
        databaseSession.setLogLevel(logLevel);
+
    }
+
    xrService.setORSession(databaseSession);
+
    xrService.setXMLContext(new XMLContext(oxProject));
+
    xrService.setOXSession(xrService.getXMLContext().getSession(0));
+
}
+
 
+
</source>
+
 
+
=== Advanced Customization - Part III: OSGi and JDBC Drivers  ===
+
The <b>SimpleTable</b> demonstrates how to run an EclipseLink DBWS service in an OSGi environment. One aspect of that environment was simplified by the use of the Apache Derby database which has a JDBC driver that is also an OSGi bundle. Unfortunately, this is not normally the case - most JDBC drivers are not OSGi-friendly. This section describes some of the techniques one can use to manage this addition complexity.
+

Latest revision as of 10:46, 7 October 2010


DBWS in an OSGi Environment

An Eclipselink DBWS service can be run in an OSGi environment using Javase 6's 'containerless' javax.xml.ws.Endpoint API.
In this example, Eclipse Equinox is the OSGi environment.

Environment Setup

Download a version of the Eclipse IDE that includes the Plug-in Development Environment (PDE) (Eclipse IDE for Java EE Developers, Eclipse Classic 3.6.0 or
Eclipse for RCP and RAP Developers). We will need an OSGi-friendly JDBC driver - for this example, we will use the Apache Derby bundle:

  • Download Apache Derby (org.apache.derby) bundle from Orbit.


EclipseLinkExamplesOSGiDeveloping with EclipseLink OSGi in PDE-2v2.png

  • Place the org.apache.derby bundle into your $ECLIPSE_HOME/dropins folder.

Under 'Preferences' -> 'Plug-in Development' -> 'Target Platform', edit the Running Platform (Active)
EclipseLink DBWS with OSGi Target Platform.png

{Note - once the Eclipse P2 Repository has the correct version of EclipseLink that supports running DBWS with OSGi,
the following steps will no longer be required and the EclipseLink target Component can be used}

EclipseLink builds after 2.1.2.v20100928-r8243 have EclipseLink DBWS available as a bundle
Extract the following 6 bundles to some directory:

$ ls additionalBundles/
org.eclipse.persistence.asm.source_2.1.2.v20100928-r8243.jar
org.eclipse.persistence.asm_2.1.2.v20100928-r8243.jar
org.eclipse.persistence.core.source_2.1.2.v20100928-r8243.jar
org.eclipse.persistence.core_2.1.2.v20100928-r8243.jar
org.eclipse.persistence.dbws.source_2.1.2.v20100928-r8243.jar
org.eclipse.persistence.dbws_2.1.2.v20100928-r8243.jar

Edit the Target Platform, adding the directory with the additional bundles:
EclipseLink DBWS with OSGi UpdatedBundles1.png

EclipseLink DBWS with OSGi UpdatedBundles2.png

Reload the Target Platform

Create a new Plug-in project

From the 'Plug-in Development' Perspective, create a new Plug-in project SimpleTable:
{Note the choice of 'standard' for OSGi framework}
EclipseLink DBWS with OSGi SimpleTable.png
Proceed to the next panel of the wizard where the (OSGi bundle) Activator for SimpleTable is defined:
EclipseLink DBWS with OSGi SimpleTableActivator.png
Click 'Finish' and open the generated Activator:

package simpletable;
 
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
 
public class Activator implements BundleActivator {
 
	private static BundleContext context;
 
	static BundleContext getContext() {
		return context;
	}
 
	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext bundleContext) throws Exception {
		Activator.context = bundleContext;
	}
 
	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext bundleContext) throws Exception {
		Activator.context = null;
	}
}

Modifying Activator to implement javax.xml.ws.Provider

Change the Activator to extend org.eclipse.persistence.internal.dbws.ProviderHelper (NB - will show errors):
EclipseLink DBWS with OSGi ProviderHelperwavy.png
In order to correct the errors, the SimpleTable project needs to update its list of required bundles. Open the MANIFEST.MF file in the 'Plug-in Manifest Editor' and select the 'Dependencies' tab and add the following bundles: EclipseLink DBWS with OSGi Dependencies.png

The Activator needs to be annotated to support a (dynamic) Web services Endpoint:

public class Constants {
    //DBWS Properties
    public static final String TEST_PROJECT = 
    	"simpletable";
 
    //JAX-WS properties
    public static final String TEST_NAMESPACE = 
    	"urn:" + TEST_PROJECT;
    public static final String TEST_SERVICE = 
    	TEST_PROJECT + "Service";
    public static final String TEST_SERVICE_NAMESPACE = 
    	"urn:" + TEST_SERVICE;
    public static final String TEST_PORT = 
    	TEST_SERVICE + "Port";
    public static final String ENDPOINT_ADDRESS = 
    	"http://localhost:9999/" + TEST_PROJECT;
}
package simpletable;
 
//java eXtension imports
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.ServiceMode;
import static javax.xml.ws.Service.Mode.MESSAGE;
import static javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING;
 
//EclipseLink imports
import org.eclipse.persistence.internal.dbws.ProviderHelper;
import org.eclipse.persistence.sessions.Session;
 
//OSGi/PDE imports
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
 
//test imports
import static simpletable.Constants.ENDPOINT_ADDRESS;
import static simpletable.Constants.TEST_PORT;
import static simpletable.Constants.TEST_SERVICE_NAMESPACE;
import static simpletable.Constants.TEST_SERVICE;
 
@WebServiceProvider(
    targetNamespace = TEST_SERVICE_NAMESPACE,
    serviceName = TEST_SERVICE,
    portName = TEST_PORT
)
@ServiceMode(MESSAGE)
public class Activator extends ProviderHelper implements BundleActivator, Provider<SOAPMessage> {
 
	private static BundleContext context;
	private static Endpoint endpoint = null;
	private static QName portQName = null;
	private static Service testService = null;
 
	static BundleContext getContext() {
		return context;
	}
 
	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext bundleContext) throws Exception {
	    Activator.context = bundleContext;
            super.init(getClass().getClassLoader(), null, false);
            endpoint = Endpoint.create(this);
            endpoint.publish(ENDPOINT_ADDRESS);
            QName serviceQName = new QName(TEST_SERVICE_NAMESPACE, TEST_SERVICE);
            portQName = new QName(TEST_SERVICE_NAMESPACE, TEST_PORT);
            testService = Service.create(serviceQName);
            testService.addPort(portQName, SOAP11HTTP_BINDING, ENDPOINT_ADDRESS);
	    System.out.println("Hello, " + testService.toString());
	}
 
	/*
	 * (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext bundleContext) throws Exception {
	    Activator.context = null;
            if (endpoint != null) {
                endpoint.stop();
            }
            super.destroy();
	    System.out.println("Goodbye");
	}
}

Running the DBWS Provider-Activator

In the Eclipse IDE, select the MANIFEST.MF file and bring up the context menu for 'Run As' -> 'Run Configurations' -> 'OSGi Framework' to create a new launch configuration called 'Server'.

Deselect the 'Include optional dependencies when computing required bundles' and the
'Add new workspace bundles to this launch configuration automatically' check-boxes.

Deselect all bundles and re-select 'Server' and then click 'Add Required Bundles'. This simplifies the list of Required Bundles:
EclipseLink DBWS with OSGi NewLaunch.png

When this launch configuration is run, an exception is thrown indicating that some DBWS files cannot be found. The SimpleTable project needs two additional source-folders. The first folder already exists - add the META-INF folder as a source-folder. The second needs to be created:

EclipseLink DBWS with OSGi NewFolders.png

In the next section, we will re-use a previous example (Basic Table) to generate the required files.

Generating the DBWS file

The simple use-case is the creation of a Web service that exposes a database table's CRUD (Create/Read(findByPK,findAll)/Update/Delete) lifecycle operations. Here is the table SIMPLETABLE on my local MySql database:
EclipseLink DBWS with OSGi SimpleTableDescription.png

The DBWSBuilder utility requires a DBWS configuration file as input. NB - When running the DBWSBuilder, the setenv.{cmd, sh} file in .../eclipselink/utils/dbws needs to be updated with the path to JDBC driver jar:

@REM User MUST set DRIVER_CLASSPATH to point to their desired driver jar(s). For example:
set DRIVER_CLASSPATH=C:\external\libs\mysql-connector-java-5.1.13-bin.jar
<?xml version="1.0" encoding="UTF-8"?>
<dbws-builder xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <properties>
    <property name="projectName">simpleTable</property>
    <property name="logLevel">fine</property>
    <property name="username">user</property>
    <property name="password">password</property>
    <property name="url">jdbc:mysql://localhost:3306/emp</property>
    <property name="driver">com.mysql.jdbc.Driver</property>
    <property name="platformClassname">org.eclipse.persistence.platform.database.DerbyPlatform</property>
  </properties>
  <table
    tableNamePattern="SIMPLETABLE"
  />
</dbws-builder>
prompt > dbwsbuilder.cmd -builderFile dbws-builder.xml -stageDir $ROOT/output_directory -packageAs:noArchive javase

The above builder file is a little 'weird' as it specifies how to log in to my local MySql database, but it declares that the platform is Derby. The following files are generated:

$ROOT
│ 
├───output_directory
│       DBWSProvider.class
│       DBWSProvider.java
│       eclipselink-dbws-or.xml
│       eclipselink-dbws-ox.xml
│       eclipselink-dbws-schema.xsd
│       eclipselink-dbws-sessions.xml
│       eclipselink-dbws.wsdl
│       eclipselink-dbws.xml
│       ProviderListener.class
│       ProviderListener.java

Copy the eclipselink-dbws.xml, eclipselink-dbws-or.xml, eclipselink-dbws-ox.xml and eclipselink-dbws-sessions.xml to the META-INF directory; eclipselink-dbws-schema.xsd and eclipselink-dbws.wsdl to the wsdl directory.

The eclipselink-dbws-sessions.xml needs some manual updates:

<?xml version="1.0" encoding="UTF-8"?>
<sessions version="2.0.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <session xsi:type="database-session">
      <name>simpletable-dbws-or-session</name>
      <logging xsi:type="eclipselink-log">
         <log-level>fine</log-level>
      </logging>
      <!-- add the META-INF dir to the path to find the OR project -->
      <primary-project xsi:type="xml">META-INF/eclipselink-dbws-or.xml</primary-project>
      <login xsi:type="database-login">
         <platform-class>org.eclipse.persistence.platform.database.DerbyPlatform</platform-class>
         <!-- change the driver class to use Derby's EmbeddedDriver -->
         <driver-class>org.apache.derby.jdbc.EmbeddedDriver</driver-class>
         <!-- change the URL: use Derby's 'create' attribute to create database if it doesn't already exist -->
         <connection-url>jdbc:derby:test;create=true</connection-url>
         <byte-array-binding>false</byte-array-binding>
         <optimize-data-conversion>false</optimize-data-conversion>
         <trim-strings>false</trim-strings>
      </login>
   </session>
   <session xsi:type="database-session">
      <name>simpletable-dbws-ox-session</name>
      <logging xsi:type="eclipselink-log">
         <log-level>off</log-level>
      </logging>
      <!-- add the META-INF dir to the path to find the OX project -->
      <primary-project xsi:type="xml">META-INF/eclipselink-dbws-ox.xml</primary-project>
   </session>
</sessions>

Since the database won't exist the first time the service is run, need to create the table and populate a few rows:

public class Constants {
...
 
    //database smts
    public static final String CREATE_TABLE = 
    	"CREATE TABLE SIMPLETABLE (\n" +
    	"  id NUMERIC NOT NULL,\n" +
    	"  name VARCHAR(25),\n" +
    	"  since DATE,\n" +
    	"  PRIMARY KEY (id)\n" +
    	")";
    public static final String INS1 =
    	"INSERT INTO SIMPLETABLE (id, name, since) VALUES (1, 'mike', '2001-12-25')";
    public static final String INS2 =
	"INSERT INTO SIMPLETABLE (id, name, since) VALUES (2, 'blaise','2001-12-25')";
    public static final String INS3 =
	"INSERT INTO SIMPLETABLE (id, name, since) VALUES (3, 'rick','2001-12-25')";
}
 
...
import static simpletable.Constants.CREATE_TABLE;
import static simpletable.Constants.INS1;
import static simpletable.Constants.INS2;
import static simpletable.Constants.INS3;
...
 
@ServiceMode(MESSAGE)
public class Activator extends ProviderHelper implements BundleActivator, Provider<SOAPMessage> {
...
 
@Override
public void loginSessions() {
    super.loginSessions();
    Session orSession = xrService.getORSession();
    try {
        orSession.executeNonSelectingSQL(CREATE_TABLE);
        orSession.executeNonSelectingSQL(INS1);
        orSession.executeNonSelectingSQL(INS2);
        orSession.executeNonSelectingSQL(INS3);
    }
    catch (Exception e) {
        // table & rows already exist - ignore
    }
}
...

Running the Server target should now work:

osgi> [EL Info]: 2010-08-16 16:02:10.68--DatabaseSessionImpl(17240206)--Thread(Thread[Start Level Event Dispatcher,5,main])--EclipseLink, version: Eclipse Persistence Services - 2.1.1.v20100805-r7986
[EL Config]: 2010-08-16 16:02:10.68--DatabaseSessionImpl(17240206)--Connection(13623369)--Thread(Thread[Start Level Event Dispatcher,5,main])--connecting(DatabaseLogin(
	platform=>DerbyPlatform
	user name=> ""
	datasource URL=> "jdbc:derby:test;create=true"
))
[EL Config]: 2010-08-16 16:02:11.305--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--Connected: jdbc:derby:test
	User: APP
	Database: Apache Derby  Version: 10.5.1.1 - (764942)
	Driver: Apache Derby Embedded JDBC Driver  Version: 10.5.1.1 - (764942)
[EL Info]: 2010-08-16 16:02:11.305--DatabaseSessionImpl(17240206)--Thread(Thread[Start Level Event Dispatcher,5,main])--simpletable-dbws-or-session login successful
[EL Fine]: 2010-08-16 16:02:11.305--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--CREATE TABLE SIMPLETABLE (
  id NUMERIC NOT NULL,
  name VARCHAR(25),
  since DATE,
  PRIMARY KEY (id)
)
[EL Fine]: 2010-08-16 16:02:11.383--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--INSERT INTO SIMPLETABLE (id, name, since) VALUES (1, mike, 2001-12-25)
[EL Fine]: 2010-08-16 16:02:11.43--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--INSERT INTO SIMPLETABLE (id, name, since) VALUES (2, blaise,2001-12-25)
[EL Fine]: 2010-08-16 16:02:11.43--DatabaseSessionImpl(17240206)--Connection(9656129)--Thread(Thread[Start Level Event Dispatcher,5,main])--INSERT INTO SIMPLETABLE (id, name, since) VALUES (3, rick,2001-12-25)
Hello, javax.xml.ws.Service@1bd06bf

A Simple(!) JAX-WS Client

Typically, JAX-WS Client are generated - either partially or completely - based on the published WSDL. In the example we can see that while the WSDL file is generated, it is not available via a 'live' URL (some tools exist to generate Clients from standalone WSDL files).

An alternate (simpler?) option is to use the javax.xml.ws.Dispatch API to directly send pre-computed SOAP messages:

package simpletable;
 
//javase imports
import java.io.StringReader;
import java.io.StringWriter;
import org.w3c.dom.Document;
 
//java eXtension imports
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import static javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING;
 
//EclipseLink imports
import org.eclipse.persistence.platform.xml.XMLComparer;
import org.eclipse.persistence.platform.xml.XMLParser;
import org.eclipse.persistence.platform.xml.XMLPlatform;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
 
//test imports
import static simpletable.Constants.ENDPOINT_ADDRESS;
import static simpletable.Constants.TEST_NAMESPACE;
import static simpletable.Constants.TEST_PORT;
import static simpletable.Constants.TEST_SERVICE;
import static simpletable.Constants.TEST_SERVICE_NAMESPACE;
 
public class Client {
 
    static XMLComparer comparer = new XMLComparer();
    static XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
    static XMLParser xmlParser = xmlPlatform.newXMLParser();
 
    static final String SOAP_FINDBYPK_REQUEST =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
          "<SOAP-ENV:Body>" +
            "<findByPrimaryKey_simpletableType xmlns=\"urn:simpletableService\">" +
              "<id>1</id>" +
            "</findByPrimaryKey_simpletableType>" +
          "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_FINDALL_REQUEST =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
          "<SOAP-ENV:Body>" +
            "<findAll_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\"/>" +
          "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_UPDATE_REQUEST =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
          "<SOAP-ENV:Body>" +
            "<update_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
              "<theInstance>" +
                "<urn:simpletableType>" +
                  "<urn:id>1</urn:id>" +
                  "<urn:name>mike norman</urn:name>" +
                  "<urn:since>2010-12-25</urn:since>" +
                "</urn:simpletableType>" +
              "</theInstance>" +
            "</update_simpletableType>" +
          "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_CREATE_REQUEST =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
          "<SOAP-ENV:Body>" +
            "<create_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
              "<theInstance>" +
                "<urn:simpletableType>" +
                  "<urn:id>4</urn:id>" +
                  "<urn:name>test</urn:name>" +
                  "<urn:since>2009-03-27</urn:since>" +
                "</urn:simpletableType>" +
              "</theInstance>" +
            "</create_simpletableType>" +
          "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_DELETE_REQUEST =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
          "<SOAP-ENV:Body>" +
            "<delete_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
              "<id>4</id>" +
            "</delete_simpletableType>" +
          "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_UPDATE2_REQUEST =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
          "<SOAP-ENV:Body>" +
            "<update_simpletableType xmlns=\"urn:simpletableService\" xmlns:urn=\"urn:simpletable\">" +
              "<theInstance>" +
                "<urn:simpletableType>" +
                  "<urn:id>1</urn:id>" +
                  "<urn:name>mike</urn:name>" +
                  "<urn:since>2001-12-25</urn:since>" +
                "</urn:simpletableType>" +
              "</theInstance>" +
            "</update_simpletableType>" +
          "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
 
    public static void main(String[] args) throws SOAPException, TransformerException {
 
        QName qname = new QName(TEST_SERVICE_NAMESPACE, TEST_PORT);
        Service service = Service.create(new QName(TEST_NAMESPACE, TEST_SERVICE));
        service.addPort(qname, SOAP11HTTP_BINDING, ENDPOINT_ADDRESS);
        Dispatch<SOAPMessage> sourceDispatch = service.createDispatch(qname, SOAPMessage.class,
            Service.Mode.MESSAGE);
 
        SOAPMessage request = createSOAPMessage(SOAP_FINDBYPK_REQUEST);
        SOAPMessage result = sourceDispatch.invoke(request);
        if (result != null) {
            Source src = result.getSOAPPart().getContent();
            DOMResult domResult = new DOMResult();
            getTransformer().transform(src, domResult);
            Document resultDoc = (Document)domResult.getNode();
            System.out.println(documentToString(resultDoc));
            Document controlDoc = xmlParser.parse(new StringReader(SOAP_FINDBYPK_RESPONSE));
            boolean nodeEqual = comparer.isNodeEqual(controlDoc, resultDoc);
            if (!nodeEqual) {
                System.out.println("findByPrimaryKey_simpletableType response not same as control document");
            }
        }
 
        request = createSOAPMessage(SOAP_FINDALL_REQUEST);
        result = sourceDispatch.invoke(request);
        if (result != null) {
            Source src = result.getSOAPPart().getContent();
            DOMResult domResult = new DOMResult();
            getTransformer().transform(src, domResult);
            Document resultDoc = (Document)domResult.getNode();
            Document controlDoc = xmlParser.parse(new StringReader(SOAP_FINDALL_RESPONSE));
            boolean nodeEqual = comparer.isNodeEqual(controlDoc, resultDoc);
            if (!nodeEqual) {
                System.out.println("findAll_simpletableType response not same as control document");
            }
        }
 
        request = createSOAPMessage(SOAP_UPDATE_REQUEST);
        result = sourceDispatch.invoke(request);
        if (result != null) {
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
            if (!SOAP_UPDATE_RESPONSE_ELEMENTNAME.equals(localName)) {
                System.out.println(SOAP_UPDATE_RESPONSE_ELEMENTNAME + " incorrect");
            }
        }
 
        request = createSOAPMessage(SOAP_FINDBYPK_REQUEST);
        result = sourceDispatch.invoke(request);
        if (result != null) {
            Source src = result.getSOAPPart().getContent();
            DOMResult domResult = new DOMResult();
            getTransformer().transform(src, domResult);
            Document resultDoc = (Document)domResult.getNode();
            Document controlDoc = xmlParser.parse(new StringReader(SOAP_FINDBYPK_AFTERUPDATE_RESPONSE));
            boolean nodeEqual = comparer.isNodeEqual(controlDoc, resultDoc);
            if (!nodeEqual) {
                System.out.println("findByPrimaryKey_simpletableType response (after update) not same as control document");
            }
        }
 
        request = createSOAPMessage(SOAP_CREATE_REQUEST);
        result = sourceDispatch.invoke(request);
        if (result != null) {
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
            if (!SOAP_CREATE_RESPONSE_ELEMENTNAME.equals(localName)) {
                System.out.println(SOAP_CREATE_RESPONSE_ELEMENTNAME + " incorrect");
            }
        }
 
        request = createSOAPMessage(SOAP_DELETE_REQUEST);
        result = sourceDispatch.invoke(request);
        if (result != null) {
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
            if (!SOAP_DELETE_RESPONSE_ELEMENTNAME.equals(localName)) {
                System.out.println(SOAP_DELETE_RESPONSE_ELEMENTNAME + " incorrect");
            }
        }
 
        request = createSOAPMessage(SOAP_UPDATE2_REQUEST);
        result = sourceDispatch.invoke(request);
        if (result != null) {
            String localName = result.getSOAPBody().getFirstChild().getLocalName();
            if (!SOAP_UPDATE_RESPONSE_ELEMENTNAME.equals(localName)) {
                System.out.println(SOAP_UPDATE_RESPONSE_ELEMENTNAME + " incorrect");
            }
        }
 
    }
    static final String SOAP_FINDBYPK_RESPONSE =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
            "<SOAP-ENV:Header/>" +
            "<SOAP-ENV:Body>" +
                "<srvc:findByPrimaryKey_simpletableTypeResponse xmlns=\"urn:simpletable\" xmlns:srvc=\"urn:simpletableService\">" +
                    "<srvc:result>" +
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                            "<id>1</id>" +
                            "<name>mike</name>" +
                            "<since>2001-12-25</since>" +
                        "</simpletableType>" +
                    "</srvc:result>" +
                "</srvc:findByPrimaryKey_simpletableTypeResponse>" +
            "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_FINDALL_RESPONSE =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
            "<SOAP-ENV:Header/>" +
            "<SOAP-ENV:Body>" +
                "<srvc:findAll_simpletableTypeResponse xmlns=\"urn:simpletable\" xmlns:srvc=\"urn:simpletableService\">" +
                    "<srvc:result>" +
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                            "<id>1</id>" +
                            "<name>mike</name>" +
                            "<since>2001-12-25</since>" +
                        "</simpletableType>" +
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                            "<id>2</id>" +
                            "<name>blaise</name>" +
                            "<since>2001-12-25</since>" +
                        "</simpletableType>" +
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                            "<id>3</id>" +
                            "<name>rick</name>" +
                            "<since>2001-12-25</since>" +
                        "</simpletableType>" +
                    "</srvc:result>" +
                "</srvc:findAll_simpletableTypeResponse>" +
            "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_UPDATE_RESPONSE_ELEMENTNAME =
        "update_simpletableTypeResponse";
    static final String SOAP_FINDBYPK_AFTERUPDATE_RESPONSE =
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
            "<SOAP-ENV:Header/>" +
            "<SOAP-ENV:Body>" +
                "<srvc:findByPrimaryKey_simpletableTypeResponse xmlns=\"urn:simpletable\" xmlns:srvc=\"urn:simpletableService\">" +
                    "<srvc:result>" +
                        "<simpletableType xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                            "<id>1</id>" +
                            "<name>mike norman</name>" +
                            "<since>2010-12-25</since>" +
                        "</simpletableType>" +
                    "</srvc:result>" +
                "</srvc:findByPrimaryKey_simpletableTypeResponse>" +
            "</SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";
    static final String SOAP_CREATE_RESPONSE_ELEMENTNAME =
        "create_simpletableTypeResponse";
    static final String SOAP_DELETE_RESPONSE_ELEMENTNAME =
        "delete_simpletableTypeResponse";
 
    static SOAPMessage createSOAPMessage(String message) {
        try {
            MessageFactory factory = MessageFactory.newInstance();
            SOAPMessage soapMessage = factory.createMessage();
            soapMessage.getSOAPPart().setContent(
                (Source)new StreamSource(new StringReader(message)));
            soapMessage.saveChanges();
            return soapMessage;
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    static Transformer getTransformer() {
        Transformer transformer = null;
        try {
            TransformerFactory tf = TransformerFactory.newInstance();
            transformer = tf.newTransformer();
        }
        catch (TransformerConfigurationException e) {
            /* extremely rare, safe to ignore */
        }
        return transformer;
    }
    static String documentToString(Document doc) {
        DOMSource domSource = new DOMSource(doc);
        StringWriter stringWriter = new StringWriter();
        StreamResult result = new StreamResult(stringWriter);
        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty("indent", "yes");
            transformer.transform(domSource, result);
            return stringWriter.toString();
        } catch (Exception e) {
            // e.printStackTrace();
            return "<empty/>";
        }
    }
}

Advanced Customization - Part I: Jetty

The above example relies on the built-in JavaSE HTTP server (com.sun.net.httpserver.HttpServer) which has some known issues (6675392, 6946825). A user may wish to 'swap-in' a different HTTP Server, such as Jetty, which can be added to the PDE Target Platform from the Helios P2 Update Repository:

EclipseLink DBWS with OSGi in PDE.png

Open the Manifest Editor and add the following to the Required Plug-ins list of dependencies:

org.eclipse.jetty.continuation
org.eclipse.jetty.http
org.eclipse.jetty.io
org.eclipse.jetty.server
org.eclipse.jetty.util

There is a system property to override the built-in JavaSE HTTP server class:

-Dcom.sun.net.httpserver.HttpServerProvider=org.eclipse.jetty.jaxws2spi.JettyHttpServerProvider

{NB - the SPI used to load the new HttpServerProvider class is not OSGi-friendly; a revised version of com.sun.net.httpserver.spi.HttpServerProvider can be found here}

The new HttpServerProvider can be found in maven .../org/mortbay/jetty/jetty-jaxws2spi/7.0.1.v20091125 in the package org.eclipse.jetty.jaxws2spi

prompt> ls
JAXWS2ContextHandler.java  JettyHttpServer.java
JettyHttpContext.java      JettyHttpServerProvider.java
JettyHttpExchange.java     ThreadPoolExecutorAdapter.java

Once the above classes are added to the project and the required configuration changes are done, the existing code in simpletable.Activator will run - using Jetty - without modification.
EclipseLink DBWS with OSGi SimpleTable with Jetty running.png

Advanced Customization - Part II: Auto-generation of EclipseLink metadata

The files under META-INF and wsdl (eclipselink-dbws.xml, eclipselink-dbws-or.xml, etc.) can be generated at runtime when the Activator is first initialized. Starting with EclipseLink 2.1.2, portions of the initialization of org.eclipse.persistence.internal.dbws.ProviderHelper can be sub-classed:

protected InputStream initXRServiceStream(ClassLoader parentClassLoader, ServletContext sc) {
...
 
protected InputStream initXRSchemaStream(ClassLoader parentClassLoader, ServletContext sc) {
...
 
protected InputStream initWSDLInputStream(ClassLoader parentClassLoader, ServletContext sc) {
....

From the EclipseLink installation, add eclipselink-dbwsutils.jar and javax.wsdl_1.6.2.v200806030405.jar to the SimpleTable project, under a lib dir:
EclipseLinkExamplesOSGiDeveloping extraLibJars.png

Open the MANIFEST.MF file in the 'Plug-in Manifest Editor' and select the 'Runtime' tab and add the above jars to the plug-in's classpath: EclipseLinkExamplesOSGiDeveloping runtimeClasspath.png

The wsdl directory can be removed, along with all the DBWS files under META-INF (the project no longer needs these directories to be 'src' directories).

DBWSBuilder is normally invoked as a command-line tool along with a file containing 'operations' that are converted to DBWS runtime artifacts (eclipselink-dbws.xml, eclipselink-dbws-or.xml, etc.) It is also possible to program an instance of DBWSBuilder:

.... additional constants
//database properties
public static final String DATABASE_URL =
    "jdbc:derby:test;create=true";
public static final String DATABASE_DRIVER = 
    "org.apache.derby.jdbc.EmbeddedDriver";
public static final String DATABASE_PLATFORM = 
    "org.eclipse.persistence.platform.database.DerbyPlatform";
 
... additional imports in simpletable.Activator
//EclipseLink imports
import org.eclipse.persistence.internal.databaseaccess.Platform;
import org.eclipse.persistence.internal.dbws.ProviderHelper;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.xr.ProjectHelper;
import org.eclipse.persistence.internal.xr.XRDynamicClassLoader;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLLogin;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.DatasourceLogin;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.factories.XMLProjectReader;
import org.eclipse.persistence.tools.dbws.DBWSBuilder;
import org.eclipse.persistence.tools.dbws.JavasePackager;
import org.eclipse.persistence.tools.dbws.TableOperationModel;
import static org.eclipse.persistence.logging.AbstractSessionLog.translateStringToLoggingLevel;
import static org.eclipse.persistence.tools.dbws.DBWSBuilder.NO_SESSIONS_FILENAME;
import static org.eclipse.persistence.tools.dbws.DBWSBuilder.SESSIONS_FILENAME_KEY;
import static org.eclipse.persistence.tools.dbws.DBWSPackager.ArchiveUse.noArchive;
import static org.eclipse.persistence.tools.dbws.XRPackager.__nullStream;
 
... additional member variables in simpletable.Activator
 
private static DBWSBuilder builder = new DBWSBuilder();
private static ByteArrayOutputStream DBWS_SERVICE_STREAM = new ByteArrayOutputStream();
private static ByteArrayOutputStream DBWS_SCHEMA_STREAM = new ByteArrayOutputStream();
private static ByteArrayOutputStream DBWS_OR_STREAM = new ByteArrayOutputStream();
private static ByteArrayOutputStream DBWS_OX_STREAM = new ByteArrayOutputStream();
private static ByteArrayOutputStream DBWS_WSDL_STREAM = new ByteArrayOutputStream();
 
... additional setup required when bundle starts
public void start(BundleContext bundleContext) throws Exception {
    Activator.context = bundleContext;
 
    builder.quiet = true;
    //set properties
    builder.setProjectName("simpletable");
    builder.setLogLevel(SessionLog.FINE_LABEL);
    builder.setDriver(DATABASE_DRIVER);
    builder.setPlatformClassname(DATABASE_PLATFORM);
    builder.setUrl(DATABASE_URL);
    builder.getProperties().put(SESSIONS_FILENAME_KEY, NO_SESSIONS_FILENAME);
    TableOperationModel tModel = new TableOperationModel();
    tModel.setName("simpleTable");
    tModel.setTablePattern("SIMPLETABLE");
    builder.getOperations().add(tModel);
    JavasePackager packager = new JavasePackager() {
	@Override
	public void start() {
		// normally start() checks for the existence of a temp directory -
		// no need since all DBWSBuilder artifacts are in-memory
	}
    };
    packager.setArchiveUse(noArchive);
    builder.setPackager(packager);
    try {
        // can't go thru java.sql.DriverManager 'cause it uses Class.forName() - not OSGi-friendly
        Driver driver = new org.apache.derby.jdbc.EmbeddedDriver();
        Connection conn = driver.connect(DATABASE_URL, null);
        builder.setConnection(conn);
    }
    catch (Exception e) {
        // find out what happened
        e.printStack();
	throw e;
    }
    builder.build(DBWS_SCHEMA_STREAM, __nullStream, DBWS_SERVICE_STREAM, DBWS_OR_STREAM,
       DBWS_OX_STREAM, __nullStream, __nullStream, DBWS_WSDL_STREAM, __nullStream, __nullStream,
       __nullStream, __nullStream, null);
    super.init(this.getClass().getClassLoader(), null, false);
 
    // regular setup of Endpoint
    endpoint = Endpoint.create(this);
    endpoint.publish(ENDPOINT_ADDRESS);   
    ....

The call to super.init(XRDynamicClassLoader, null, false) makes callbacks to the three init routines mentioned above where we return the information computed in builder.build(DBWS_SCHEMA_STREAM,...):

protected InputStream initXRServiceStream(ClassLoader parentClassLoader, ServletContext sc) {
    return new ByteArrayInputStream(DBWS_SERVICE_STREAM.toByteArray());
}
 
protected InputStream initXRSchemaStream(ClassLoader parentClassLoader, ServletContext sc) {
    return new ByteArrayInputStream(DBWS_SCHEMA_STREAM.toByteArray());
}
 
protected InputStream initWSDLInputStream(ClassLoader parentClassLoader, ServletContext sc) {
    return new ByteArrayInputStream(DBWS_WSDL_STREAM.toByteArray());
}
The final step is to build the EclipseLink sessions - Override buildSessions() (looks complicated but its just 'plumbing'):
...
import static org.eclipse.persistence.logging.AbstractSessionLog.translateStringToLoggingLevel;
...
@Override
public void buildSessions() {
    XRDynamicClassLoader xrdecl = new XRDynamicClassLoader(parentClassLoader);
    Project oxProject = XMLProjectReader.read(new StringReader(DBWS_OX_STREAM.toString()),
        xrdecl);
    ((XMLLogin)oxProject.getDatasourceLogin()).setEqualNamespaceResolvers(false);
    Project orProject = XMLProjectReader.read(new StringReader(DBWS_OR_STREAM.toString()),
        xrdecl);
    DatasourceLogin login = orProject.getLogin();
    ((DatabaseLogin)login).setConnectionString(builder.getUrl());
    ((DatabaseLogin)login).setDriverClassName(builder.getDriver());
    Platform platform = builder.getDatabasePlatform();
    ConversionManager cm = platform.getConversionManager();
    cm.setLoader(xrdecl);
    login.setDatasourcePlatform(platform);
    ((DatabaseLogin)login).bindAllParameters();
    orProject.setDatasourceLogin(login);
    ProjectHelper.fixOROXAccessors(orProject, oxProject);
    DatabaseSession databaseSession = orProject.createDatabaseSession();
    int logLevel = translateStringToLoggingLevel(builder.getLogLevel());
    if (SessionLog.OFF == logLevel) {
        databaseSession.dontLogMessages();
    }
    else {
        databaseSession.setLogLevel(logLevel); 
    }
    xrService.setORSession(databaseSession);
    xrService.setXMLContext(new XMLContext(oxProject));
    xrService.setOXSession(xrService.getXMLContext().getSession(0));
}

Advanced Customization - Part III: OSGi and JDBC Drivers

The SimpleTable demonstrates how to run an EclipseLink DBWS service in an OSGi environment. One aspect of that environment was simplified by the use of the Apache Derby database which has a JDBC driver that is also an OSGi bundle. Unfortunately, this is not normally the case - most JDBC drivers are not OSGi-friendly. This section describes some of the techniques one can use to manage this addition complexity.
TBD

Back to the top