Web Service Domain Requirement
My friend Matt Raible compared an AppFuse service delegate or manager class to the Session Facade. This had me thinking about this a lot this weekend. I was using Stateless SessionBean Web Services that BMW North America was accessing to prepare reports for their customer satisfaction survey data. This data was important to them because it provided a country-wide dealership dashboard for performance metrics. When I was preparing these Web services, I had a template J2EE project that I put together that would access Oracle stored procedures using pure JDBC callable statements but isolating the client calls using a JBoss container and these hosted session beans.
I always felt that these services were too simple because I copied this project everytime I created a new Web service. I had a thought in the back of my mind that maybe I would see performance problems and all the flack I'd heard about EJB's would be my ruin. While I do think that using EJBs for persistence prior to version 3.0 was overkill, Session EJB's have their place. I had success with using Session beans for my Web services.
The design was quite simple. The Session bean would host the Web service methods. I created a request and response object e.g. DetailReportArgObj
and DetailReportReplyObj.java respectively. These simple Java beans were used to first process the Web service report request arguments ultimately to the Oracle stored procedure and then the reply object would return the data that modeled a report row.
Here's an example of the request and response value objects:
package com.isky.detail;
public class DetailReportArgObj implements java.io.Serializable
{
private String _endDate;
private String _programId;
private String _orgId;
private String _orgLevel;
public DetailReportArgObj()
{
}
public DetailReportArgObj(String _reportType,
String _endDate, String _orgId, String _programId,
String _orgLevel )
{
this._endDate = _endDate;
this._programId = _programId;
this._orgId = _orgId;
this._orgLevel = _orgLevel;
}
public String get_programId() {
return _programId;
}
public void set_programId(String id) {
_programId = id;
}
public String get_endDate() {
return _endDate;
}
public void set_endDate(String date) {
_endDate = date;
}
public String get_orgId() {
return _orgId;
}
public void set_orgId(String id) {
_orgId = id;
}
public String get_orgLevel() {
return _orgLevel;
}
public void set_orgLevel(String level) {
_orgLevel = level;
}
}
package com.isky.detail;
public class DetailReportReplyObj implements java.io.Serializable
{
private String _lineNo;
private String _lineText;
private String _labelText;
private String _janScore;
private String _febScore;
private String _marScore;
...
private String _ytdScore;
public DetailReportReplyObj()
{
}
public String get_aprScore() {
return _aprScore;
}
public void set_aprScore(String score) {
_aprScore = score;
}
...
public String get_labelText() {
return _labelText;
}
public void set_labelText(String text) {
_labelText = text;
}
...
}
The beauty in these Web service projects was the ability to specify the requirements back to the Oracle development staff and keep the Web service interface virtually the same between various Web services. I was also able to keep the Web Service Definition Language (WSDL) (pronounced wizz-duhl) similar between services and was able to copy the file and basically refactor the names of things between services. I didn't truly write these projects from scratch. I pirated them in an entirely legal way from the open source community.
Here's an example WSDL file that defines the Web Service that I hosted:
="1.0"="UTF-8"
<wsdl:definitions
targetNamespace="http://localhost:8080/jboss-net/services/DetailReport"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://localhost:8080/jboss-net/services/DetailReport"
xmlns:intf="http://localhost:8080/jboss-net/services/DetailReport"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns1="http://localhost/DetailReportBean"
xmlns:tns2="http://net.jboss.org/jmx"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace="http://localhost/DetailReportBean"
xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="DetailReportArgObj">
<sequence>
<element name="_endDate" nillable="true" type="xsd:string"/>
<element name="_orgId" nillable="true" type="xsd:string"/>
<element name="_orgLevel" nillable="true" type="xsd:string"/>
<element name="_programId" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="DetailReportReplyObj">
<sequence>
<element name="_labelText" nillable="true" type="xsd:string"/>
<element name="_lineNo" nillable="true" type="xsd:string"/>
<element name="_lineText" nillable="true" type="xsd:string"/>
<element name="_qualifier" nillable="true" type="xsd:string"/>
<element name="_janScore" nillable="true" type="xsd:string"/>
<element name="_febScore" nillable="true" type="xsd:string"/>
<element name="_marScore" nillable="true" type="xsd:string"/>
<element name="_aprScore" nillable="true" type="xsd:string"/>
<element name="_mayScore" nillable="true" type="xsd:string"/>
<element name="_junScore" nillable="true" type="xsd:string"/>
<element name="_julScore" nillable="true" type="xsd:string"/>
<element name="_augScore" nillable="true" type="xsd:string"/>
<element name="_sepScore" nillable="true" type="xsd:string"/>
<element name="_octScore" nillable="true" type="xsd:string"/>
<element name="_novScore" nillable="true" type="xsd:string"/>
<element name="_decScore" nillable="true" type="xsd:string"/>
<element name="_ytdScore" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</schema>
<schema targetNamespace="http://localhost:8080/jboss-net/services/DetailReport"
xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="ArrayOf_xsd_anyType">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:anyType[]"/>
</restriction>
</complexContent>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name="DetailReportRequest">
<wsdl:part name="in0" type="xsd:anyType"/>
</wsdl:message>
<wsdl:message name="DetailReportResponse">
<wsdl:part name="DetailReportReturn" type="impl:ArrayOf_xsd_anyType"/>
</wsdl:message>
<wsdl:portType name="DetailReport">
<wsdl:operation name="getReport" parameterOrder="in0">
<wsdl:input message="impl:DetailReportRequest" name="DetailReportRequest"/>
<wsdl:output message="impl:DetailReportResponse" name="DetailReportResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="DetailReportSoapBinding" type="impl:DetailReport">
<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getReport">
<wsdlsoap:operation soapAction="DetailReport"/>
<wsdl:input name="DetailReportRequest">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://localhost:8080/jboss-net/services/DetailReport"
use="encoded"/>
</wsdl:input>
<wsdl:output name="DetailReportResponse">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://localhost:8080/jboss-net/services/DetailReport"
use="encoded"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="DetailReportService">
<wsdl:port binding="impl:DetailReportSoapBinding" name="DetailReport">
<wsdlsoap:address
location="http://localhost:8080/jboss-net/services/DetailReport"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Conclusions
If I could share anything with up and coming developers, become an open source pirate! Steal everything and choose for yourselves what works best. Take no one's word for anything. Try it all! I only showed you a bit of the code behind my Web services. SOAP requests were handled using Apache Axis. And I had to get one project to work and the first one wasn't easy. Also, Apache ANT was used to build and deploy everything.
I also used a Visual Studio package to create .NET clients for each service to give to my client. Also, if you create a client for your services, you can be sure that they work. But heed my words, mateys, there's much booty in the Open source world and guess what? Most of it is written by professionals. A lot of times, these professionals are writing boring and inefficient code for pay because some incompetent architect or manager made some bad decisions and they are stuck in a rut for some time. But, these creative guys and girls write code at night and give away their proud creations.
So, steal what you can and pillage these free downloads. Take advantage of what's out there. I'm piratepete on irc.freenode.net and some of my friends think the "pirate" name has negative connotations. I don't believe this. Open Source is free, so steal it, rob it, use it. Just don't call any of it you're own. Give credit where credit is due.
There's a good blog out there now written by Steve Jobs of Apple. He's flaming Gates something fierce. The funny part, Gates steals software and software concepts too. This is why I love Open Source, it's all in the open and I don't have to play games. If I use someone else's code to get the job done, I can get the job done, give the other guy the credit, go home, pay my mortgage, and still sleep well at night knowing I'm not part of this proprietary software nonsense.
Boy, did I get away from Session Bean Web services. Sorry. Go find some free software and be some business man or woman's IT hero. Business people are the ones that have the money. Go look for them.