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