package org.wyona.yarep.util; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.wyona.yarep.core.NodeType; import org.wyona.yarep.core.Repository; /** * This utility directly interacts with Yarep and returns your data objects for a given Yarep Path or write your data object to a given Yarep Path. * The whole mechanism is based on the standard JAXB API. * Restriction: data objects must be in a package where the jaxb.properties file is available! * @author Balz Schreier, Zwischengas AG, www.zwischengas.com */ public class YarepXMLBindingUtil { private static final Logger log = Logger.getLogger(YarepXMLBindingUtil.class); /** * The following requirements must be fulfilled so that marshalling of objects to XML is working: * - You need all your domain objects in a specific package * - In that package you need a jaxb.properties file containing the following line: * javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory * - Alternatively you can specify a package-info.java so that namespace are used as you want it to. * @param repo Data repository * @param jaxbObject * @param yarepPath * @return */ public static boolean writeJAXBDataObject(Repository repo, Object jaxbObject, String yarepPath) { boolean success = true; OutputStream os = null; try { JAXBContext context = JAXBContext.newInstance(jaxbObject.getClass()); Marshaller m = context.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); org.wyona.yarep.core.Node node = null; if (repo.existsNode(yarepPath)) { node = repo.getNode(yarepPath); } else { node = repo.getRootNode().addNode(yarepPath, NodeType.RESOURCE); } os = node.getOutputStream(); m.marshal(jaxbObject, node.getOutputStream()); node.setMimeType("application/xml"); // important so that TikaParser kicks in. } catch (Exception e) { success = false; log.fatal("Could not write data object of class '"+jaxbObject.getClass()+"' into Yarep Node with path '"+yarepPath+"' !"); log.fatal(e,e); } finally { try { os.close(); } catch (Exception e) { } } return success; } /** * Convert a data object into a DOM document (not that the data object must be annotated with the JAXB annotations) * @param jaxbObject * @return */ public static Document getDocFromJAXBDataObject(Object jaxbObject) { Document result = null; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); result = db.newDocument(); JAXBContext context = JAXBContext.newInstance(jaxbObject.getClass()); Marshaller m = context.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); m.marshal(jaxbObject, result); } catch (Exception e) { log.fatal("Could not convert data object of class '"+jaxbObject.getClass()+"' into a DOM document!"); log.fatal(e,e); } return result; } /** * Convert a DOM document into a JAXB data object * @param jaxbObject * @return */ public static <T>T getJAXBObjectFromDoc(Class<T> targetClass, Document doc) { T result = null; InputStream is = null; try { is = getInputStreamFromDoc(doc); // prepare unmarshalling for targetClass JAXBContext jc = JAXBContext.newInstance(targetClass); Unmarshaller u = jc.createUnmarshaller(); result = (T)(u.unmarshal(is)); } catch (Exception e) { log.fatal("Could not convert document into object of class '"+targetClass+"' !"); log.fatal(e,e); } finally { try { is.close(); } catch (Exception e) { } } return result; } /** * Get your data object given the Yarep Path and the Repository. You also need to provide the Class. * @param targetClass You get an instance of this object after XML -> Object transformation * @param repository Data repository * @param yarepPath The Yarep Path to the XML * @return returns an instance of class targetClass */ public static <T>T readJAXBDataObject(Class<T> targetClass, Repository repository, String yarepPath) { T result = null; InputStream is = null; try { // get the node // TODO org.wyona.yarep.core.Node node = repository.getNode(yarepPath); is = node.getInputStream(); // prepare unmarshalling for targetClass JAXBContext jc = JAXBContext.newInstance(targetClass); Unmarshaller u = jc.createUnmarshaller(); result = (T)(u.unmarshal(is)); } catch (Throwable e) { log.fatal("Could not convert document at '"+yarepPath+"' into class '"+targetClass+"'!"); log.fatal(e,e); } finally { try { is.close(); } catch (Exception e) { } } return result; } private static InputStream getInputStreamFromDoc(Document doc) { InputStream is = null; try { DOMSource source = new DOMSource(doc); StringWriter xmlAsWriter = new StringWriter(); StreamResult result = new StreamResult(xmlAsWriter); TransformerFactory.newInstance().newTransformer().transform(source, result); is = new ByteArrayInputStream(xmlAsWriter.toString().getBytes("UTF-8")); } catch (Exception e) { } return is; } }