When I was designing complex workflow definitions that were containing a lot of groovy scripts I just didn't want to write them directly in workflow xml definition. Instead, I created a many groovy script files and put ${references} into workflow definitions. During deployment I just expand them.
With this setup I can profit from IDE support and I don't have to maintain hundreds of lines of code in a workflow definition. I just keep them basic scripts that has up to 15-20 lines. Imagine all those java import declarations, you wouldn't even know that you have that stuff on classpath.
This code takes care of it. I didn't use Liferay DOM API because it is missing a few key methods from classes that extend org.dom4j.Node and I didn't want to work that around. You just change those IllegalStateExceptions to your System ones or something.
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jaxen.JaxenException;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.XPath;
import org.jaxen.dom4j.Dom4jXPath;
public class WhatEver {
public byte[] expandScripts(String workFlowPath) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Document document;
try {
document = new SAXReader().read(cl.getResource(workFlowPath));
List<Element> scripts = findAllElements("script", document.getRootElement());
expandScripts(cl, scripts);
} catch (DocumentException e) {
throw new IllegalStateException("Writing dom4j document failed", e);
}
return toByteArray(document);
}
private List<Element> findAllElements(String name, Element rootElement) {
String ns = rootElement.getQName().getNamespaceURI();
Map<String, String> map = new HashMap<String, String>();
map.put("x", ns);
List<Element> scripts;
try {
XPath xpath = new Dom4jXPath("//x:" + name);
xpath.setNamespaceContext(new SimpleNamespaceContext(map));
scripts = xpath.selectNodes(rootElement);
} catch (JaxenException e) {
throw new IllegalStateException("Xpath search for: '" + name + "' failed", e);
}
return scripts;
}
private void expandScripts(ClassLoader cl, List<Element> scripts) {
DocumentFactory docFactory = DocumentFactory.getInstance();
for (Element oldScriptElement : scripts) {
String text = oldScriptElement.getText();
if (!isCDATA(oldScriptElement) && text != null && text.startsWith("$")) {
String scriptPath = text.substring(2, text.length() - 1);
Element result = docFactory.createElement("script", oldScriptElement.getParent().getQName().getNamespaceURI());
String script;
try {
script = getString(cl.getResourceAsStream(scriptPath));
} catch (IOException e) {
throw new IllegalStateException("Reading script: '" + scriptPath + "' failed", e);
}
result.addCDATA(script);
replaceNode(oldScriptElement, result);
}
}
}
private byte[] toByteArray(Document document) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XMLWriter writer;
try {
writer = new XMLWriter(baos, OutputFormat.createPrettyPrint());
writer.write(document);
writer.close();
} catch (IOException e) {
throw new IllegalStateException("Writing dom4j document failed", e);
}
return baos.toByteArray();
}
private boolean isCDATA(Element node) {
for (Node n : (List<Node>) node.content()) {
if (Node.CDATA_SECTION_NODE == n.getNodeType()) {
return true;
}
}
return false;
}
private void replaceNode(Element oldNode, Element newNode) {
List parentContent = oldNode.getParent().content();
int index = parentContent.indexOf(oldNode);
oldNode.detach();
parentContent.set(index, newNode);
}
private String getString(InputStream is) throws IOException {
final char[] buffer = new char[0x10000];
StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(is, "UTF-8");
int read;
do {
read = in.read(buffer, 0, buffer.length);
if (read > 0) {
out.append(buffer, 0, read);
}
} while (read >= 0);
return out.toString();
}
}
Gist
No comments:
Post a Comment