/** * Copyright 2008 The University of North Carolina at Chapel Hill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.unc.lib.dl.data.ingest.solr; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.xml.transform.Source; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamSource; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.Namespace; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.jdom2.transform.JDOMResult; import org.jdom2.transform.JDOMSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.Resource; /** * Stores a list of documents that are transformed into a Solr update document format. * Entries in the list are individual XML operations, such as add or delete. The list can * be exported to a single update document. Should be thread-safe. * * @author bbpennel */ public class UpdateDocTransformer { private static final Logger LOG = LoggerFactory.getLogger(UpdateDocTransformer.class); private List<Element> addDocElements; private List<Element> synchronizedAddDocElements; private String xslName = "generateAddDoc.xsl"; private Transformer transformer; private List<Namespace> namespaces; public UpdateDocTransformer() { addDocElements = new ArrayList<Element>(); synchronizedAddDocElements = Collections.synchronizedList(addDocElements); namespaces = new ArrayList<Namespace>(); namespaces.add(Namespace.getNamespace("dc", "http://purl.org/dc/elements/1.1/")); namespaces.add(Namespace.getNamespace("ns5", "http://cdr.unc.edu/definitions/1.0/base-model.xml#")); namespaces.add(Namespace.getNamespace("ns6", "info:fedora/fedora-system:def/model#")); namespaces.add(Namespace.getNamespace("rdfs", "http://www.w3.org/2000/01/rdf-schema#")); namespaces.add(Namespace.getNamespace("mods", "http://www.loc.gov/mods/v3")); namespaces.add(Namespace.getNamespace("oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/")); namespaces.add(Namespace.getNamespace("foxml", "info:fedora/fedora-system:def/foxml#")); namespaces.add(Namespace.getNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")); namespaces.add(Namespace.getNamespace("dcterms", "http://purl.org/dc/terms/")); namespaces.add(Namespace.getNamespace("owl", "http://www.w3.org/2002/07/owl#")); namespaces.add(Namespace.getNamespace("cdr-fn", "http://cdr.lib.unc.edu/")); } /** * Initializes the transformer by retrieving the XSLT document to use and building a transformer from it. * @throws Exception */ public void init() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext(); Resource res = ctx.getResource("classpath:/transform/" + xslName); Source transformSource = new StreamSource(res.getInputStream()); TransformerFactory factory = TransformerFactory.newInstance(); factory.setURIResolver(new URIResolver() { public Source resolve(String href, String base) throws TransformerException { Source result = null; if (href.startsWith("/")) result = new StreamSource(UpdateDocTransformer.class .getResourceAsStream(href)); else result = new StreamSource(UpdateDocTransformer.class .getResourceAsStream("/transform/" + href)); return result; } }); Templates transformTemplate = factory.newTemplates(transformSource); transformer = transformTemplate.newTransformer(); } /** * Adds an add document element to the list * @param doc * @throws Exception */ public synchronized void addDocument(Document doc) throws Exception { JDOMResult out = new JDOMResult(); synchronized(transformer){ transformer.transform(new JDOMSource(doc), out); } Element rootElement = out.getDocument().getRootElement(); for (Namespace namespace: namespaces){ rootElement.removeNamespaceDeclaration(namespace); } synchronizedAddDocElements.add(rootElement.detach()); LOG.debug("Added " + doc.hashCode()); } /** * Adds a "delete" pid update element to the list * @param pid * @throws Exception */ public void deleteDocument(String pid) throws Exception { deleteQuery("id", pid); } public void deleteQuery(String query) throws Exception { deleteQuery("query", query); } public synchronized void deleteQuery(String field, String query) throws Exception { Element deleteElement = new Element("delete"); Element deleteLimit = new Element(field); deleteLimit.setText(query); deleteElement.addContent(deleteLimit); synchronizedAddDocElements.add(deleteElement); } public void commit() throws Exception { Element commitElement = new Element("commit"); synchronizedAddDocElements.add(commitElement); } public void clearDocs() { LOG.debug("Clearing update doc list"); addDocElements.clear(); this.synchronizedAddDocElements.clear(); } /** * Returns the list of update documents as a single update document with * all the individual elements as children. */ public String toString(){ Document addDoc = new Document(); Element addDocRoot = new Element("update"); addDoc.setRootElement(addDocRoot); addDocRoot.addContent(addDocElements); XMLOutputter out = new XMLOutputter(Format.getPrettyFormat()); return out.outputString(addDoc); } public synchronized String exportUpdateDocument(){ String updateDocument = this.toString(); this.clearDocs(); return updateDocument; } public String getXslName() { return xslName; } public void setXslName(String xslName) { this.xslName = xslName; } public int getDocumentCount(){ return this.addDocElements.size(); } }