org.eclipse.winery.repository.JAXBSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.winery.repository.JAXBSupport.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2018 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 *******************************************************************************/
package org.eclipse.winery.repository;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;

import org.eclipse.winery.model.selfservice.Application;
import org.eclipse.winery.model.tosca.TDefinitions;
import org.eclipse.winery.model.tosca.constants.Namespaces;
import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition;
import org.eclipse.winery.repository.backend.MockXMLElement;
import org.eclipse.winery.repository.backend.RepositoryFactory;

import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// if com.sun.xml.bind.marshaller.NamespacePrefixMapper cannot be resolved,
// possibly
// http://mvnrepository.com/artifact/com.googlecode.jaxb-namespaceprefixmapper-interfaces/JAXBNamespacePrefixMapper/2.2.4
// helps
// also com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper could be the
// right package

/**
 * Bundles all general JAXB functionality
 */
public class JAXBSupport {

    // thread-safe JAXB as inspired by https://jaxb.java.net/guide/Performance_and_thread_safety.html
    // The other possibility: Each subclass sets JAXBContext.newInstance(theSubClass.class); in its static {} part.
    // This seems to be more complicated than listing all subclasses in initContext
    public final static JAXBContext context = JAXBSupport.initContext();

    private static final Logger LOGGER = LoggerFactory.getLogger(JAXBSupport.class);

    private final static PrefixMapper prefixMapper = new PrefixMapper();

    /**
     * Follows https://jaxb.java.net/2.2.5/docs/release-documentation.html#marshalling -changing-prefixes
     * <p>
     * See http://www.jarvana.com/jarvana/view/com/sun/xml/bind/jaxb-impl/2.2.2/ jaxb-impl-2.2.2-javadoc.jar!/com/sun/xml/bind/marshaller/
     * NamespacePrefixMapper.html for a JavaDoc of the NamespacePrefixMapper
     */
    private static class PrefixMapper extends NamespacePrefixMapper {

        @Override
        public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
            LOGGER.trace("Mapping params: {}, {}, {}", namespaceUri, suggestion, requirePrefix);
            if (StringUtils.isEmpty(namespaceUri)) {
                LOGGER.trace("Empty or null namespaceUri: null returned");
                return null;
            }

            if (!requirePrefix && namespaceUri.equals(Namespaces.TOSCA_NAMESPACE)) {
                // in case no prefix is required and the namespace is the TOSCA namespace, there should be no prefix added at all to increase human-readability of the XML
                LOGGER.trace("No prefix required: returning null.");
                return null;
            }

            final String prefix = RepositoryFactory.getRepository().getNamespaceManager().getPrefix(namespaceUri);
            LOGGER.trace("returned: {}", prefix);
            return prefix;
        }
    }

    private static JAXBContext initContext() {
        JAXBContext context;
        try {
            // For winery classes, eventually the package+jaxb.index method could be better. See http://stackoverflow.com/a/3628525/873282
            // @formatter:off
            context = JAXBContext.newInstance(
                    //InjectorReplaceData.class,
                    TDefinitions.class, // all other elements are referred by "@XmlSeeAlso"
                    WinerysPropertiesDefinition.class,
                    // for the self-service portal
                    Application.class,
                    // MockXMLElement is added for testing purposes only.
                    MockXMLElement.class);
            // @formatter:on
        } catch (JAXBException e) {
            System.out.println("HALLO");
            System.out.println(e);
            LOGGER.error("Could not initialize JAXBContext", e);
            throw new IllegalStateException(e);
        }
        return context;
    }

    /**
     * Creates a marshaller.
     *
     * IMPORTANT: always create a new instance and do not reuse the marhaller, otherwise the input-stream will throw a
     * NullPointerException! see https://stackoverflow.com/questions/11114665/org-xml-sax-saxparseexception-premature-end-of-file-for-valid-xml
     *
     * @throws IllegalStateException if marshaller could not be instantiated
     */
    public static Marshaller createMarshaller(boolean includeProcessingInstruction) {
        Marshaller m;
        try {
            m = JAXBSupport.context.createMarshaller();
            // pretty printed output is required as the XML is sent 1:1 to the browser for editing
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            try {
                m.setProperty("com.sun.xml.bind.namespacePrefixMapper", JAXBSupport.prefixMapper);
            } catch (PropertyException e) {
                // Namespace-Prefixing is not supported by the used Provider. Nothing we can do about that
            }
            if (!includeProcessingInstruction) {
                // side effect of JAXB_FRAGMENT property (when true): processing instruction is not included
                m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            }
        } catch (JAXBException e) {
            JAXBSupport.LOGGER.error("Could not instantiate marshaller", e);
            throw new IllegalStateException(e);
        }

        return m;
    }

    /**
     * Creates an unmarshaller
     *
     * @throws IllegalStateException if unmarshaller could not be instantiated
     */
    public static Unmarshaller createUnmarshaller() {
        try {
            return JAXBSupport.context.createUnmarshaller();
        } catch (JAXBException e) {
            JAXBSupport.LOGGER.error("Could not instantiate unmarshaller", e);
            throw new IllegalStateException(e);
        }
    }
}