Author: thorsten
Date: Fri Jan 23 03:00:16 2009
New Revision: 736998
URL: http://svn.apache.org/viewvc?rev=736998&view=rev
Log:
Due to perfomance consideration switching from StAX to SAX processing in the
contracts. This change in combination with a couple recent changes has enhanced
the response time notable
Added:
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/ContractHandler.java
Modified:
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/XSLContractHelper.java
Added:
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/ContractHandler.java
URL:
http://svn.apache.org/viewvc/forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/ContractHandler.java?rev=736998&view=auto
==============================================================================
---
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/ContractHandler.java
(added)
+++
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/ContractHandler.java
Fri Jan 23 03:00:16 2009
@@ -0,0 +1,154 @@
+package org.apache.forrest.dispatcher.impl.helper;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+public class ContractHandler extends EchoHandler {
+
+ private boolean recording = false;
+ private HashMap<String, String> map;
+ private HashSet<String> mapTrigger;
+ private String name = "", usage = "", description = "";
+ private boolean descriptionRecord;
+ private boolean usageRecord;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUsage() {
+ return usage;
+ }
+
+ public void setUsage(String usage) {
+ this.usage = usage;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ContractHandler(String encoding) {
+ super(encoding);
+ map = new HashMap<String, String>();
+ mapTrigger = new HashSet<String>();
+ descriptionRecord = false;
+ usageRecord = false;
+ }
+
+ public void startElement(String uri, String loc, String raw, Attributes
attrs)
+ throws SAXException {
+ if (uri.equals(Captions.NS) && loc.equals(Captions.CONTRACT_ELEMENT)){
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String aName = attrs.getLocalName(i); // Attr name
+ if (aName.equals(Captions.CONTRACT_NAME_ATT)) {
+ name = attrs.getValue(i);
+ }
+ }
+ } else if (raw.equals(Captions.DESCRIPTION_ELEMENT)){
+ descriptionRecord = true;
+ } else if (raw.equals(Captions.USAGE_ELEMENT)){
+ usageRecord = true;
+ }else if (uri.equals(Captions.NS_XSL) && loc.equals("stylesheet")) {
+ recording = true;
+ }
+
+ if (recording) {
+ String prefix = extractPrefix(raw);
+ emit(lineEnd + "<" + raw);
+ if (uri != null && !uri.equals("")) {
+ if (prefix != null) {
+ if (!map.containsKey(prefix)) {
+ emit(" xmlns:" + prefix + "=\"" + uri + "\"");
+ map.put(prefix, uri);
+ mapTrigger.add(raw);
+ }
+ } else {
+ emit(" xmlns=\"" + uri + "\"");
+ }
+ }
+ if (attrs != null) {
+ for(int i=attrs.getLength()-1;i>=0;i--){
+ String qName = attrs.getQName(i);
+ String localName = extractSufix(qName);
+ prefix = extractPrefix(qName);
+ if ( null == prefix || (null != prefix && !prefix.equals("xmlns"))) {
+ writeAttribute(attrs.getValue(i), qName);
+ }else if (null != prefix && prefix.equals("xmlns") &&
!map.containsKey(localName)){
+ writeAttribute(attrs.getValue(i), qName);
+ }
+ }
+ }
+ emit(">");
+ }
+ }
+
+ private void writeAttribute(String localValue, String qName)
+ throws SAXException {
+ emit(" ");
+ emit(qName);
+ emit("=\"");
+ char[] value = localValue.toCharArray();
+ characters(value, 0, value.length);
+ emit("\"");
+ }
+
+ public void endElement(String uri, String loc, String raw)
+ throws SAXException {
+ if (raw.equals(Captions.DESCRIPTION_ELEMENT)){
+ descriptionRecord = false;
+ }else if (raw.equals(Captions.USAGE_ELEMENT)){
+ usageRecord = false;
+ }
+ if (recording) {
+ String prefix = extractPrefix(raw);
+ if (prefix != null && map.containsKey(prefix) &&
mapTrigger.contains(raw)) {
+ map.remove(prefix);
+ mapTrigger.remove(raw);
+ }
+ super.endElement(uri, loc, raw);
+ }
+ if (uri.equals("http://www.w3.org/1999/XSL/Transform")
+ && loc.equals("stylesheet")) {
+ endPrefixMapping(extractPrefix(raw));
+ recording = false;
+ }
+ }
+
+ public void characters(char ch[], int start, int length) throws SAXException
{
+ if (recording) {
+ super.characters(ch, start, length);
+ }else if (descriptionRecord){
+ description += cleanCharacters(ch, start, length);
+ }else if (usageRecord){
+ usage += cleanCharacters(ch, start, length);
+ }
+ }
+
+ /**
+ * @param ch
+ * @param start
+ * @param length
+ */
+ private String cleanCharacters(char[] ch, int start, int length) {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ char c = ch[start + i];
+ buffer.append(c);
+ }
+ return StringEscapeUtils.escapeXml(buffer.toString().trim());
+ }
+
+}
Modified:
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/XSLContractHelper.java
URL:
http://svn.apache.org/viewvc/forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/XSLContractHelper.java?rev=736998&r1=736997&r2=736998&view=diff
==============================================================================
---
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/XSLContractHelper.java
(original)
+++
forrest/branches/dispatcher_rewrite/plugins/org.apache.forrest.plugin.internal.dispatcher/src/java/org/apache/forrest/dispatcher/impl/helper/XSLContractHelper.java
Fri Jan 23 03:00:16 2009
@@ -20,18 +20,16 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.stream.XMLEventWriter;
-import javax.xml.stream.XMLStreamConstants;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-import javax.xml.stream.XMLStreamWriter;
-import javax.xml.stream.util.XMLEventAllocator;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
@@ -40,20 +38,61 @@
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.forrest.dispatcher.exception.ContractException;
import org.apache.forrest.dispatcher.impl.XSLContract;
import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
-public class XSLContractHelper extends Loggable{
+public class XSLContractHelper extends Loggable {
private Transformer transformer = null;
private TransformerFactory transFact = null;
- private StAX stax = null;
+ private SAXParser parser;
+ private DocumentBuilder builder;
+ private String name;
+
- public XSLContractHelper(TransformerFactory transformerFactory,StAX stax) {
+ /**
+ * @return the name of the contract
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name the name to set of the contract
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Create a new instance of the contract helper
+ * with the transformer factory we want to use.
+ * <p>
+ * We are using the parameter approach to optimize performance.
+ * @param transformerFactory the transformer factory that we should use for
this helper class
+ * @throws ContractException
+ * @throws ParserConfigurationException
+ * @throws SAXNotSupportedException
+ * @throws SAXNotRecognizedException
+ */
+ public XSLContractHelper(TransformerFactory transformerFactory)
+ throws ContractException, SAXNotRecognizedException,
SAXNotSupportedException, ParserConfigurationException {
this.transFact = transformerFactory;
- this.stax = stax;
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+ try {
+ parser = factory.newSAXParser();
+ builder = DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder();
+ } catch (Exception e) {
+ throw new ContractException(e);
+ }
}
/**
@@ -65,12 +104,14 @@
* @param allowXmlProperties
* whether or not we want to allow xml properties
* @throws TransformerConfigurationException
- * @throws ParserConfigurationException
- * @throws IOException
- * @throws SAXException
+ * @throws ParserConfigurationException
+ * @throws IOException
+ * @throws SAXException
*/
- public void prepareTransformation(Source xslSource, boolean
allowXmlProperties, Map<String, Object> params)
- throws TransformerConfigurationException, ParserConfigurationException,
SAXException, IOException {
+ public void prepareTransformation(Source xslSource,
+ boolean allowXmlProperties, Map<String, Object> params)
+ throws TransformerConfigurationException, ParserConfigurationException,
+ SAXException, IOException {
// prepare transformation
transformer = transFact.newTransformer(xslSource);
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
@@ -79,30 +120,30 @@
// set errorListener
transformer.setErrorListener(new LoggingErrorListener(log));
// do we allow xml properties?
- if(allowXmlProperties){
- DocumentBuilder builder = DocumentBuilderFactory.newInstance()
- .newDocumentBuilder();
+ if (allowXmlProperties) {
for (Iterator<String> iter = params.keySet().iterator();
iter.hasNext();) {
String key = iter.next();
Class<String> string = String.class;
Object value = params.get(key);
- if (!string.isInstance(value)){
- if(log.isDebugEnabled()){
- log.debug( new String((byte[])value));
- }
- BufferedInputStream valueStream = new BufferedInputStream(new
ByteArrayInputStream((byte[]) value));
- transformer.setParameter(key,
builder.parse(valueStream).getFirstChild());
- }else{
- transformer.setParameter(key,value);
+ if (!string.isInstance(value)) {
+ if (log.isDebugEnabled()) {
+ log.debug(new String((byte[]) value));
+ }
+ BufferedInputStream valueStream = new BufferedInputStream(
+ new ByteArrayInputStream((byte[]) value));
+ transformer.setParameter(key, builder.parse(valueStream)
+ .getFirstChild());
+ } else {
+ transformer.setParameter(key, value);
}
}
- }else{
+ } else {
for (Iterator<String> iter = params.keySet().iterator();
iter.hasNext();) {
String key = iter.next();
String value = (String) params.get(key);
- transformer.setParameter(key,value);
+ transformer.setParameter(key, value);
}
-
+
}
}
@@ -114,10 +155,9 @@
*/
public ByteArrayOutputStream createEmptyXmlOutput() throws
XMLStreamException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- XMLStreamWriter writer = stax.getStreamWriter(out);
- writer.writeStartDocument("UTF-8", "1.0");
- writer.writeStartElement("foo");
- writer.writeEndDocument();
+ PrintWriter writer = new PrintWriter (out);
+ String doc ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<foo/>";
+ writer.write(doc);
writer.flush();
writer.close();
return out;
@@ -133,10 +173,9 @@
*/
public InputStream createEmptyXml() throws XMLStreamException {
ByteArrayOutputStream out = createEmptyXmlOutput();
- return (out !=null)?StreamHelper.switchStream(out):null;
+ return (out != null) ? StreamHelper.switchStream(out) : null;
}
-
/**
* This method sets the xslSource, name, description and usage information of
* the contract.
@@ -147,170 +186,77 @@
* @param stream
* @param contract
* @throws XMLStreamException
- * @throws ContractException
+ * @throws ContractException
*/
public void setTemplate(InputStream stream, XSLContract contract)
throws XMLStreamException, ContractException {
- XMLStreamReader reader = stax.getNSReader(stream);
- boolean process = true;
- while (process) {
- int event = reader.next();
- switch (event) {
-
- case XMLStreamConstants.END_DOCUMENT:
- process = false;
- break;
-
- case XMLStreamConstants.START_ELEMENT:
- String localName = reader.getLocalName();
- if (localName.equals(Captions.CONTRACT_ELEMENT)) {
- contract.setName(processContract(reader));
- }
- if (localName.equals(Captions.DESCRIPTION_ELEMENT)) {
- contract.setDescription(processDescription(reader));
- }
- if (localName.equals(Captions.USAGE_ELEMENT)) {
- contract.setUsage(processUsage(reader));
- }
-
- if (localName.equals(Captions.TEMPLATE_ELEMENT)) {
- contract.setXslSource(processTemplate(reader));
- }
-
- default:
- break;
- }
- }
- }
- private Source processTemplate(XMLStreamReader reader) throws
ContractException{
- ByteArrayOutputStream out = new ByteArrayOutputStream();
+ // FIXME: Make encoding configurable
+ ContractHandler handler = new ContractHandler("UTF-8");
try {
- XMLEventWriter writer = stax.getWriter(out);
- // Making sure we always have all ns
- // A wee bit brute force but it works
- XMLEventAllocator allocator = stax.getNSEventAllocator();
- String role = "";
- for (int i = 0; i < reader.getAttributeCount(); i++) {
- // Get attribute name
- String localName = reader.getAttributeLocalName(i);
- if (localName.equals(Captions.TEMPLATE_FORMAT_ATT)) {
- // Return value
- role = reader.getAttributeValue(i);
- }
- }
- boolean process = true;
- if (role.equals("xsl")) {
- while (process) {
- int event = reader.next();
- switch (event) {
-
- case XMLStreamConstants.END_ELEMENT:
- if (reader.getNamespaceURI() != null) {
- if (reader.getNamespaceURI().equals(Captions.NS)
- && reader.getLocalName().equals(Captions.TEMPLATE_ELEMENT)) {
- process = false;
- } else {
- allocator.allocate(reader,writer);
- }
- } else {
- allocator.allocate(reader,writer);
- }
- break;
-
- default:
- allocator.allocate(reader,writer);
- break;
- }
- }
- }
- writer.flush();
- log.debug(out.toString());
- } catch (XMLStreamException e) {
- throw new ContractException("Fatal error in contract.\n"+e);
- }
- Source source = new StreamSource(StreamHelper.switchStream(out));
- return source;
- }
-
- private String processUsage(XMLStreamReader reader) throws
XMLStreamException {
- boolean process = true;
- String usage = "";
- while (process) {
- int event = reader.next();
- switch (event) {
-
- case XMLStreamConstants.CHARACTERS:
- if (reader.getText().replace(" ", "").length() > 1) {
- usage = reader.getText().trim();
- }
- break;
-
- case XMLStreamConstants.END_ELEMENT:
- if (reader.getLocalName().equals(Captions.USAGE_ELEMENT)) {
- process = false;
- }
- break;
-
- default:
- break;
- }
- }
- return usage;
- }
-
- private String processDescription(XMLStreamReader reader)
- throws XMLStreamException {
- boolean process = true;
- String description = "";
- while (process) {
- int event = reader.next();
- switch (event) {
-
- case XMLStreamConstants.CHARACTERS:
- if (reader.getText().replace(" ", "").length() > 1) {
- description = reader.getText().trim();
- }
- break;
-
- case XMLStreamConstants.END_ELEMENT:
- if (reader.getLocalName().equals(Captions.DESCRIPTION_ELEMENT)) {
- process = false;
- }
- break;
-
- default:
- break;
- }
+ /* DEBUG_CODE:
+ * The following is useful to see what is going on
+ *
+ * It will output the contract descriptor as is to
+ * the system out.
+ */
+ /*
+ * byte[] debug = IOUtils.toByteArray(stream);
+ * System.out.println(new String(debug));
+ * parser.parse(new ByteArrayInputStream(debug), handler);
+ */
+ // Process the stream by parsing it with the contractHandler
+ parser.parse(stream, handler);
+ } catch (Exception e) {
+ throw new ContractException(e);
}
-
- return description;
+ // Setting the necessary attributes of the contract
+ // xsl
+ contract.setXslSource(new StreamSource(new ByteArrayInputStream(handler
+ .getBytes())));
+ /* DEBUG_CODE:
+ * The following is useful to see what is going on
+ *
+ * It will output the xsl stylesheet of the contract descriptor
+ * to the system out.
+ */
+ /* System.out.println(new String(handler
+ .getBytes()));*/
+ // description - what does the contract?
+ contract.setDescription(handler.getDescription());
+ // name - how is the contract called?
+ name = handler.getName();
+ contract.setName(name);
+ // usage - how can the contract be used?
+ contract.setUsage(handler.getUsage());
}
- private String processContract(XMLStreamReader reader) {
- String contractName = "";
- for (int i = 0; i < reader.getAttributeCount(); i++) {
- // Get attribute name
- String localName = reader.getAttributeLocalName(i);
- if (localName.equals(Captions.CONTRACT_NAME_ATT)) {
- // Return value
- contractName = reader.getAttributeValue(i);
- return contractName;
- }
- }
- return contractName;
- }
+
- public void transform(InputStream dataStream, Result streamResult) throws
ContractException {
+ /**
+ * Is wrapping {...@link javax.xml.transform.Transformer.transform(Source
xmlSource, Result outputTarget)}
+ * to enhancing the exceptions that may have been thrown.
+ * @param dataStream the input stream that we want to transform
+ * @param streamResult the result object where we want to store the outcome
of the
+ * transformation.
+ * @throws ContractException enhanced error messages for sax and FAQ of the
most
+ * common problems and solutions
+ */
+ public void transform(InputStream dataStream, Result streamResult)
+ throws ContractException {
Source dataSource = new StreamSource(dataStream);
try {
transformer.transform(dataSource, streamResult);
} catch (Exception e) {
- String message ="The xsl transformation has thrown an exception. Please
see some FAQ:"+"\n"+e+"\n\nproblem->solution:\n" +
- "- org.apache.xpath.XPathException: Can not convert #STRING to
a NodeList!\n" +
- "-> Try to activate \"allowXml\" and/or \"shrink\". If this is
not working try the contract " +
- "xsl standalone and make sure it is not a xsl specific
problem.";
- throw new ContractException(message);
+ String message = "The xsl transformation has thrown an exception. for "
+ + "the contract \""+name+"\".\nPlease see some FAQ:"
+ + "\n"
+ + e
+ + "\n\nproblem->solution:\n"
+ + "- org.apache.xpath.XPathException: Can not convert #STRING to a
NodeList!\n"
+ + "-> Try to activate \"allowXml\" and/or \"shrink\". If this is not
working try the contract "
+ + "xsl standalone and make sure it is not a xsl specific problem.";
+ throw new ContractException(message);
}
}
}