Use Encoders in EJB
Table of Content
This exercise shows how to invoke encoders (specifically, the custom encoder) in an EJB exposed as web service with an option to perform basic validation against XML schema.
Back to Top
Prerequisites
Back to Top
Create an EJB Module
- Using NetBeans, create an EJB module by selecting "New Project..." from the "File" menu.
- Choose "EJB Module" in the "Enterprise" category.
- In this example for new EJB module, we use the following:
- Project Name: UseEncoderInEJB
- Choose "GlassFish V2" as Server, and "Java EE 5" as Java EE Version.
- When "Finish", right click on the project and select "New -> Web Service..." from the menu.
- In this example for new Web Service, we use the following:
- Web Service name: DecodeService
- Package: com.sun.customencoder
- Right click on "DecodeService" Web Service, and select "Add Operation...".
- In the "Add Operation..." dialog, enter the following:
- Name: "decode"
- Return Type: "java.lang.String"
- Parameters:
- Name: "input", Type: "java.lang.String"
- Name: "toValidate", Type: "boolean"
- For "Exceptions", enter the following:
- Exception: "java.lang.Exception"
- When "Finish", the skeleton code is generated.
The actual content is as follows:
@WebService()
@Stateless()
public class DecodeService {
/**
* Web service operation
*/
@WebMethod(operationName = "decode")
public String decode(@WebParam(name = "input")
String input, @WebParam(name = "toValidate")
boolean toValidate) throws Exception {
//TODO write your implementation code here:
return null;
}
}
We will add the implmentation code later.
Back to Top
Create a Custom Defined Structure
- To test the invocation of the custom encoder in EJB, we need to have a XSD metadata with custom encoding information and input data in native format.
Please refer to
Create a Custom Structure and Test it for creating the sample custom defined structure and test it using Encoder Tester.
- In order to test validation of decoded XML against the custom defined XML schema metadata, we need to modify the "delimited1" and "delimited2" fields to have following restriction patterns:
- Now the XSD content is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml.netbeans.org/schema/customDefined1"
xmlns:tns="http://xml.netbeans.org/schema/customDefined1"
elementFormDefault="qualified" xmlns:enc="urn:com.sun:encoder">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder">
<enc:encoding name="Custom Encoding" namespace="urn:com.sun:encoder-custom-1.0" style="customencoder-1.0"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="root">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder">
<urn:top xmlns:urn="urn:com.sun:encoder">true</urn:top>
<urn:nodeProperties xmlns:urn="urn:com.sun:encoder-custom-1.0">
<urn:nodeType>group</urn:nodeType>
<urn:delimiterSet>
<urn:level>
<urn:delimiter>
<urn:kind>normal</urn:kind>
<urn:precedence>10</urn:precedence>
<urn:optionalMode>never</urn:optionalMode>
<urn:terminatorMode>never</urn:terminatorMode>
<urn:bytes>
<urn:constant>|</urn:constant>
</urn:bytes>
</urn:delimiter>
</urn:level>
<urn:level>
<urn:delimiter>
<urn:kind>normal</urn:kind>
<urn:precedence>10</urn:precedence>
<urn:optionalMode>never</urn:optionalMode>
<urn:terminatorMode>never</urn:terminatorMode>
<urn:bytes>
<urn:constant>,</urn:constant>
</urn:bytes>
</urn:delimiter>
</urn:level>
</urn:delimiterSet>
</urn:nodeProperties>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="group1">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder">
<urn:nodeProperties xmlns:urn="urn:com.sun:encoder-custom-1.0">
<urn:nodeType>delimited</urn:nodeType>
</urn:nodeProperties>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="delimited1">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder"/>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:length value="1"/>
<xsd:pattern value="(V|X)"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="delimited2">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder"/>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:decimal">
<xsd:totalDigits value="18"/>
<xsd:fractionDigits value="8"/>
<xsd:pattern value="(\+|-)?[0-9]{10}.[0-9]{8}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="group2">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder">
<urn:nodeProperties xmlns:urn="urn:com.sun:encoder-custom-1.0">
<urn:nodeType>delimited</urn:nodeType>
</urn:nodeProperties>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="fixed1" type="xsd:string">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder">
<urn:nodeProperties xmlns:urn="urn:com.sun:encoder-custom-1.0">
<urn:nodeType>fixedLength</urn:nodeType>
<urn:length>10</urn:length>
</urn:nodeProperties>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="fixed2" type="xsd:string">
<xsd:annotation>
<xsd:appinfo source="urn:com.sun:encoder">
<urn:nodeProperties xmlns:urn="urn:com.sun:encoder-custom-1.0">
<urn:nodeType>fixedLength</urn:nodeType>
<urn:length>15</urn:length>
</urn:nodeProperties>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Back to Top
Include Jar Files for Custom Encoder
Please refer to
Include Jar Files for Custom Encoder for details on how to include the set of jars for the custom encoder into the EJB Module project.
Invoke Custom Encoder in EJB
Now we will invoke the custom encoder in EJB to decode an input data, and optinally validate the decoded XML against the custom defined XML Schema metadata.
"decode" method (Web service operation)
/**
* Web service operation
* Invoke custom encoder to decode the given input data, and optionally
* validate decoded XML content against the custom-defined XML schema.
*
* @param input given input data to be decoded by custom encoder.
* @param toValidate if true, then validate decoded XML content against the
* custom-defined XML schema. Otherwise, no validation.
* @return if toValidate is flase, returns decoded XML or parsing exception
* string; if toValidate is true, then returns validation error string if
* any, or parsing exception string, or valid decoded XML.
*/
@WebMethod(operationName = "decode")
public String decode(@WebParam(name = "input")
String input, @WebParam(name = "toValidate")
boolean toValidate) throws Exception {
String output = "";
try {
if (logger.isLoggable(Level.INFO)) {
logger.info("Starting invoke parse on input [" + input + "]");
}
// Get the encoder factory instance
EncoderFactory encFactory = EncoderFactory.newInstance();
// Get the encoder type instance using an encoding style, which
// in our case is "customencoder-1.0" for custom encoder.
EncoderType encoderType = encFactory.makeType("customencoder-1.0");
// Specify a top element
String namespaceURI = "http://xml.netbeans.org/schema/customDefined1";
String rootElementName = "root";
QName topElement = new QName(namespaceURI, rootElementName);
// Locate xsd metadata
String xsdMetaFile = "com/sun/customencoder/customDefined1.xsd";
URL schemaURL = this.getClass().getClassLoader().getResource(xsdMetaFile);
if (schemaURL == null) {
throw new NullPointerException("Not able to locate xsd: " + xsdMetaFile);
}
// Construct the metadata instance
MetaRef metaRef = encFactory.makeMeta(schemaURL, topElement);
// Create the encoder instance. In our case, a custom encoder instance
Encoder encoder = encFactory.newEncoder(encoderType, metaRef);
// Decode the native input data
Source decodedXMLSource = encoder.decodeFromString(input);
if (decodedXMLSource == null) {
return "";
}
// Create a writer for decoded XML string
StringWriter writer = new StringWriter();
// Create the StreamResult object
StreamResult streamResult = new StreamResult(writer);
// Get the TransformerFactory instance
TransformerFactory tfFactory = TransformerFactory.newInstance();
// Create a Transformer instance
Transformer transformer = tfFactory.newTransformer();
// Transform from decoded XML source to StreamResult
transformer.transform(decodedXMLSource, streamResult);
// Set decoded XML string as output
output = writer.toString();
if (logger.isLoggable(Level.INFO)) {
logger.info("Finished parse, get output=[" + output + "]");
}
if (toValidate) {
// invoke validation on decoded XML against XSD,
// and assign validation result to output
output = validate(output, schemaURL);
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Finish validation, about to return output=[" + output + "]");
}
} catch (Exception e) {
logger.severe(e.toString());
// assign the exception details to the output
output = e.toString();
}
return output;
}
"validate" method
/**
* Validates input XML against the XML schema.
*
* @param xml input XML to be validated.
* @param schemaURL URL referencing the schema.
* @return validation error string if any during validation, or input XML
* string if no validation error.
*/
private String validate(String xml, URL schemaURL) {
String output = xml;
try {
// Create a schema factory
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// Create a validator
Validator validator = sf.newSchema(schemaURL).newValidator();
// Create a streamSource based on input XML
StreamSource source = new StreamSource(new StringReader(xml));
// Invoke the validation
validator.validate(source);
} catch (Exception e) {
logger.severe(e.toString());
// assign the exception details to the output
output = e.toString();
}
return output;
}
Back to Top
Run EJB/Web Service
- "Undeploy and Deploy" the project:
- Right click on the "DecodeService" Web Service, and select "Test Web Service". This will bring up the web browser for testing.
- In the "DecodeServiceService Web Service Tester", enter the data (the "input" string field) into the first text entry next to the "decode" operation button, and enter "true" or "false" value (the "toValidate" boolean field) into the second entry box to indicate whether or not to validate the decoded XML against the custom defined XSD metadata.
- Click on "decode" operation button, and examine the result. A typical response window is shown as following. Note the "return" element in the "SOAP Response" section.
- Change the input data and "toValidate" flag, and examine the "return" element value in the "SOAP Response" section.
| Input data | toValidate | result= |
| a,b|0123456789abcde12345ABCDE | false | <?xml version="1.0" encoding="UTF-8"?><root xmlns="http://xml.netbeans.org/schema/customDefined1" xsi:schemaLocation="${SCHEMA_LOCATION}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><group1><delimited1>a</delimited1><delimited2>b</delimited2></group1><group2><fixed1>0123456789</fixed1><fixed2>abcde12345ABCDE</fixed2></group2></root> |
| a,b|0123456789abcde12345ABCDE | true | org.xml.sax.SAXParseException: cvc-pattern-valid: Value 'a' is not facet-valid with respect to pattern '(V|X)' for type 'null'. |
| X,b|0123456789abcde12345ABCDE | true | org.xml.sax.SAXParseException: cvc-pattern-valid: Value 'b' is not facet-valid with respect to pattern '(\+|-)?[0-9]{10}.[0-9]{8}' for type 'null'. |
| X,-0000012345.50000000|0123456789abcde12345ABCDE | true | <?xml version="1.0" encoding="UTF-8"?><root xmlns="http://xml.netbeans.org/schema/customDefined1" xsi:schemaLocation="${SCHEMA_LOCATION}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><group1><delimited1>X</delimited1><delimited2>-0000012345.50000000</delimited2></group1><group2><fixed1>0123456789</fixed1><fixed2>abcde12345ABCDE</fixed2></group2></root> |
Back to Top
Source Code
The complete NetBean Projects are available to
download (UseEncodersInEJB.zip)
Email:
soabi-encoders@sun.com