Created
March 27, 2013 15:47
-
-
Save jirutka/5255269 to your computer and use it in GitHub Desktop.
EclipseLink MOXy helpers for better handling of namespaces.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright 2013 Jakub Jirutka. All rights reserved. | |
* | |
* "THE KOFOLA-WARE LICENSE" (Revision 1): | |
* Jakub Jirutka originally wrote this file. As long as you retain this notice you | |
* can do whatever you want with this stuff. If we meet some day, and you think | |
* this stuff is worth it, you can buy me a Kofola in return. <[email protected]> | |
*/ | |
import java.io.File; | |
import java.io.FileNotFoundException; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import javax.ws.rs.Produces; | |
import javax.ws.rs.core.MediaType; | |
import javax.ws.rs.ext.ContextResolver; | |
import javax.ws.rs.ext.Provider; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.JAXBException; | |
import javax.xml.parsers.DocumentBuilder; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
import javax.xml.transform.Source; | |
import javax.xml.transform.stream.StreamSource; | |
import org.eclipse.persistence.jaxb.JAXBContextFactory; | |
import org.eclipse.persistence.oxm.NamespacePrefixMapper; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.beans.factory.InitializingBean; | |
import org.w3c.dom.Document; | |
/** | |
* Context Resolver that initializes MOXy/JAXB context from OXM metadata in XML | |
* mapping files. | |
* | |
* @author Jakub Jirutka <[email protected]> | |
*/ | |
@Provider | |
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) | |
public class MOXyContextResolver implements ContextResolver<JAXBContext>, InitializingBean { | |
private static final Logger LOG = LoggerFactory.getLogger(MOXyContextResolver.class); | |
private File[] bindingFiles; | |
private NamespacePrefixMapper namespacePrefixMapper; | |
private JAXBContext context; | |
public void afterPropertiesSet() throws Exception { | |
createContext(); | |
} | |
public JAXBContext getContext(Class<?> type) { | |
return context; | |
} | |
/** | |
* Creates new instance of {@link JAXBContext} of MOXy from the given | |
* OXM mapping files. | |
* | |
* @throws JAXBException | |
* @throws FileNotFoundException | |
*/ | |
private void createContext() throws JAXBException, FileNotFoundException { | |
ClassLoader classLoader = getClass().getClassLoader(); | |
List<Source> metadata = new ArrayList<Source>(bindingFiles.length); | |
StringBuilder contextPath = new StringBuilder(); | |
LOG.info("Creating JAXBContext with MOXy XML bindings"); | |
for (int i = 0; i < bindingFiles.length; i++) { | |
File file = bindingFiles[i]; | |
metadata.add(new StreamSource(file)); | |
if (i != 0) contextPath.append(":"); | |
contextPath.append(getPackageNameFromBinding(file)); | |
} | |
Map<String, Object> props = new HashMap<String, Object>(1); | |
props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, metadata); | |
if (namespacePrefixMapper != null) { | |
props.put(org.eclipse.persistence.jaxb.JAXBContext.NAMESPACE_PREFIX_MAPPER, namespacePrefixMapper); | |
} | |
context = JAXBContext.newInstance(contextPath.toString(), classLoader, props); | |
} | |
/** | |
* Reads package-name from the given OXM mapping file. | |
* | |
* <pre>{@code | |
* <xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" | |
* ... | |
* package-name="cz.jirutka.atom.jaxb"> | |
* ... | |
* </xml-bindings> | |
* }</pre> | |
* | |
* @param bindingFile | |
* @return fully qualified package name | |
*/ | |
private String getPackageNameFromBinding(File bindingFile) { | |
Document doc; | |
try { | |
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |
dbf.setNamespaceAware(true); | |
DocumentBuilder db = dbf.newDocumentBuilder(); | |
doc = db.parse(bindingFile); | |
} catch (Exception ex) { | |
throw new RuntimeException(ex); | |
} | |
return doc.getDocumentElement().getAttribute("package-name"); | |
} | |
//////// Accessors //////// | |
public File[] getBindingFiles() { | |
return bindingFiles; | |
} | |
public void setBindingFiles(File[] bindingFiles) { | |
this.bindingFiles = bindingFiles; | |
} | |
public NamespacePrefixMapper getNamespacePrefixMapper() { | |
return namespacePrefixMapper; | |
} | |
public void setNamespacePrefixMapper(NamespacePrefixMapper namespacePrefixMapper) { | |
this.namespacePrefixMapper = namespacePrefixMapper; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright 2013 Jakub Jirutka. All rights reserved. | |
* | |
* "THE KOFOLA-WARE LICENSE" (Revision 1): | |
* Jakub Jirutka originally wrote this file. As long as you retain this notice you | |
* can do whatever you want with this stuff. If we meet some day, and you think | |
* this stuff is worth it, you can buy me a Kofola in return. <[email protected]> | |
*/ | |
import java.io.File; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import org.dom4j.Document; | |
import org.dom4j.DocumentException; | |
import org.dom4j.Element; | |
import org.dom4j.io.SAXReader; | |
import org.eclipse.persistence.oxm.NamespacePrefixMapper; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.util.Assert; | |
/** | |
* Configurable mapper of XML namespaces and prefixes for MOXy. | |
* | |
* <p>This is very useful especially when you have many OXM mapping files | |
* with common namespaces. In this situation, MOXy fails to use declared | |
* default namespace.</p> | |
* | |
* @author Jakub Jirutka <[email protected]> | |
*/ | |
public class MOXyNamespacePrefixMapper extends NamespacePrefixMapper { | |
private static final Logger LOG = LoggerFactory.getLogger(MOXyNamespacePrefixMapper.class); | |
private final Map<String, String> mapping; | |
private String[] namespaces; | |
/** | |
* Creates new instance from the given XML file with declared namespace | |
* prefixes. It's basically MOXy's <tt>xml-schema</tt> as the root element | |
* with nested <tt>xml-ns</tt> elements, for example: | |
* | |
* <pre>{@code | |
* <xml-schema> | |
* <xml-ns prefix="" namespace-uri="http://www.w3.org/2005/Atom" /> | |
* <xml-ns prefix="osearch" namespace-uri="http://a9.com/-/spec/opensearch/1.1/" /> | |
* <xml-ns prefix="xlink" namespace-uri="http://www.w3.org/1999/xlink" /> | |
* <xml-ns prefix="xsi" namespace-uri="http://www.w3.org/2001/XMLSchema-instance" /> | |
* </xml-schema> | |
* }</pre> | |
* | |
* @param configFile | |
*/ | |
public MOXyNamespacePrefixMapper(File configFile) { | |
Assert.isTrue(configFile.canRead(), "Could not read file: " + configFile); | |
LOG.trace("Parsing namespaces mapping from file: {}", configFile); | |
try { | |
this.mapping = parseNamespacesMapping(configFile); | |
initialize(); | |
} catch (DocumentException ex) { | |
throw new RuntimeException(ex); | |
} | |
} | |
/** | |
* Creates new instance from the given map that contains namespace URIs | |
* as the keys and their prefixes as the values. | |
* | |
* @param namespacesMapping map of URI : prefix | |
*/ | |
public MOXyNamespacePrefixMapper(Map<String, String> namespacesMapping) { | |
this.mapping = namespacesMapping; | |
initialize(); | |
} | |
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { | |
LOG.trace("Requesting prefix for namespace {} with suggested prefix {}", | |
namespaceUri, suggestion, requirePrefix); | |
if (mapping.containsKey(namespaceUri)) { | |
String prefix = mapping.get(namespaceUri); | |
LOG.trace("Returns preferred prefix: {}", prefix); | |
return prefix; | |
} else { | |
LOG.trace("No preferred prefix found, returns suggested: {}", suggestion); | |
return suggestion; | |
} | |
} | |
@Override | |
public String[] getPreDeclaredNamespaceUris() { | |
return namespaces; | |
} | |
private void initialize() { | |
this.namespaces = mapping.keySet().toArray(new String[0]); | |
if (LOG.isInfoEnabled()) { | |
for (Entry<String, String> entry : mapping.entrySet()) { | |
LOG.info("Registering predefined namespace {} with prefix '{}'", entry.getKey(), entry.getValue()); | |
} | |
} | |
} | |
/** | |
* @see #MOXyNamespacePrefixMapper(java.io.File) | |
* | |
* @param file XML file | |
* @return map of URI : prefix | |
* @throws DocumentException | |
*/ | |
private Map<String, String> parseNamespacesMapping(File file) throws DocumentException { | |
Map<String, String> map = new HashMap<String, String>(); | |
Document doc = new SAXReader().read(file); | |
Element root = doc.getRootElement(); | |
for (Object node : root.elements("xml-ns")) { | |
Element xmlns = (Element) node; | |
String prefix = xmlns.attributeValue("prefix"); | |
String uri = xmlns.attributeValue("namespace-uri"); | |
map.put(uri, prefix); | |
} | |
return map; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment