org.apache.karaf.features.internal.model.JaxbUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.karaf.features.core Show documentation
Show all versions of org.apache.karaf.features.core Show documentation
This bundle is the core implementation of the Karaf features support.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.karaf.features.internal.model;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.karaf.features.FeaturesNamespaces;
import org.apache.karaf.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
public final class JaxbUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JaxbUtil.class);
private static final JAXBContext FEATURES_CONTEXT;
private static final Map SCHEMAS = new ConcurrentHashMap<>();
static {
try {
FEATURES_CONTEXT = JAXBContext.newInstance(Features.class);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
private JaxbUtil() {
}
public static void marshal(Features features, OutputStream out) throws JAXBException {
Marshaller marshaller = FEATURES_CONTEXT.createMarshaller();
marshaller.setProperty("jaxb.formatted.output", true);
marshaller.marshal(features, out);
}
public static void marshal(Features features, Writer out) throws JAXBException {
Marshaller marshaller = FEATURES_CONTEXT.createMarshaller();
marshaller.setProperty("jaxb.formatted.output", true);
marshaller.marshal(features, out);
}
/**
* Read in a Features from the input stream.
*
* @param uri uri to read
* @param validate whether to validate the input.
* @return a Features read from the input stream
*/
public static Features unmarshal(String uri, boolean validate) {
return unmarshal(uri, null, validate);
}
public static Features unmarshal(String uri, InputStream stream, boolean validate) {
Features features;
if (validate) {
features = unmarshalValidate(uri, stream);
} else {
features = unmarshalNoValidate(uri, stream);
}
features.postUnmarshall(uri);
return features;
}
private static Features unmarshalValidate(String uri, InputStream stream) {
try {
Document doc;
if (stream != null) {
doc = XmlUtils.parse(stream);
doc.setDocumentURI(uri);
} else {
doc = XmlUtils.parse(uri);
}
String nsuri = doc.getDocumentElement().getNamespaceURI();
if (nsuri == null) {
LOGGER.warn("Old style feature file without namespace found (URI: {}). This format is deprecated and support for it will soon be removed", uri);
} else {
Schema schema = getSchema(nsuri);
try {
schema.newValidator().validate(new DOMSource(doc));
} catch (SAXException e) {
throw new IllegalArgumentException("Unable to validate " + uri, e);
}
}
fixDom(doc, doc.getDocumentElement());
Unmarshaller unmarshaller = FEATURES_CONTEXT.createUnmarshaller();
Features features = (Features) unmarshaller.unmarshal(new DOMSource(doc));
features.setNamespace(nsuri);
return features;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Unable to load " + uri, e);
}
}
private static Schema getSchema(String namespace) throws SAXException {
Schema schema = SCHEMAS.get(namespace);
if (schema == null) {
String schemaLocation;
switch (namespace) {
case FeaturesNamespaces.URI_1_0_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.0.0.xsd";
break;
case FeaturesNamespaces.URI_1_1_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.1.0.xsd";
break;
case FeaturesNamespaces.URI_1_2_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.2.0.xsd";
break;
case FeaturesNamespaces.URI_1_2_1:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.2.1.xsd";
break;
case FeaturesNamespaces.URI_1_3_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.3.0.xsd";
break;
case FeaturesNamespaces.URI_1_4_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.4.0.xsd";
break;
case FeaturesNamespaces.URI_1_5_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.5.0.xsd";
break;
case FeaturesNamespaces.URI_1_6_0:
schemaLocation = "/org/apache/karaf/features/karaf-features-1.6.0.xsd";
break;
default:
throw new IllegalArgumentException("Unsupported namespace: " + namespace);
}
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// root element has namespace - we can use schema validation
URL url = JaxbUtil.class.getResource(schemaLocation);
if (url == null) {
throw new IllegalStateException("Could not find resource: " + schemaLocation);
}
schema = factory.newSchema(new StreamSource(url.toExternalForm()));
SCHEMAS.put(namespace, schema);
}
return schema;
}
private static void fixDom(Document doc, Node node) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (!FeaturesNamespaces.URI_CURRENT.equals(node.getNamespaceURI())) {
doc.renameNode(node, FeaturesNamespaces.URI_CURRENT, node.getLocalName());
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
fixDom(doc, children.item(i));
}
}
}
private static Features unmarshalNoValidate(String uri, InputStream stream) {
try {
Unmarshaller unmarshaller = FEATURES_CONTEXT.createUnmarshaller();
NoSourceAndNamespaceFilter xmlFilter = new NoSourceAndNamespaceFilter(XmlUtils.xmlReader());
xmlFilter.setContentHandler(unmarshaller.getUnmarshallerHandler());
InputSource is = new InputSource(uri);
if (stream != null) {
is.setByteStream(stream);
}
SAXSource source = new SAXSource(xmlFilter, is);
Features features = (Features) unmarshaller.unmarshal(source);
features.setNamespace(xmlFilter.getNamespace());
return features;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Unable to load " + uri, e);
}
}
/**
* Provides an empty inputsource for the entity resolver.
* Converts all elements to the features namespace to make old feature files
* compatible to the new format
*/
public static class NoSourceAndNamespaceFilter extends XMLFilterImpl {
private static final InputSource EMPTY_INPUT_SOURCE = new InputSource(new ByteArrayInputStream(new byte[0]));
private String namespace;
public NoSourceAndNamespaceFilter(XMLReader xmlReader) {
super(xmlReader);
}
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return EMPTY_INPUT_SOURCE;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
if ("features".equals(localName)) {
namespace = uri;
}
super.startElement(FeaturesNamespaces.URI_CURRENT, localName, qName, atts);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(FeaturesNamespaces.URI_CURRENT, localName, qName);
}
public String getNamespace() {
return namespace;
}
}
}