All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.jaxb.JAXBContextFactory Maven / Gradle / Ivy

There is a newer version: 5.0.0-B05
Show newest version
/*******************************************************************************
 * Copyright (c) 1998, 2014 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     Oracle - initial API and implementation from Oracle TopLink
 ******************************************************************************/
package org.eclipse.persistence.jaxb;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.eclipse.persistence.jaxb.JAXBContext.JAXBContextInput;
import org.eclipse.persistence.jaxb.JAXBContext.ContextPathInput;
import org.eclipse.persistence.jaxb.JAXBContext.TypeMappingInfoInput;
import org.eclipse.persistence.jaxb.compiler.CompilerHelper;
import org.eclipse.persistence.jaxb.compiler.XMLProcessor;
import org.eclipse.persistence.jaxb.metadata.MetadataSource;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;
import org.eclipse.persistence.oxm.MediaType;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

/**
 * 

* Purpose:An EclipseLink specific JAXBContextFactory. This class can be specified in a * jaxb.properties file to make use of EclipseLink's JAXB 2.1 implementation. *

* Responsibilities: *

    *
  • Create a JAXBContext from an array of Classes and a Properties object
  • *
  • Create a JAXBContext from a context path and a classloader
  • *
*

* This class is the entry point into in EclipseLink's JAXB 2.1 Runtime. It provides the required * factory methods and is invoked by javax.xml.bind.JAXBContext.newInstance() to create new * instances of JAXBContext. When creating a JAXBContext from a contextPath, the list of classes is * derived either from an ObjectFactory class (schema-to-java) or a jaxb.index file * (java-to-schema). * * @author mmacivor * @see javax.xml.bind.JAXBContext * @see org.eclipse.persistence.jaxb.JAXBContext * @see org.eclipse.persistence.jaxb.compiler.Generator */ public class JAXBContextFactory { /** * @deprecated As of release 2.4, replaced by JAXBContextProperties.OXM_METADATA_SOURCE * @see org.eclipse.persistence.jaxb.JAXBContextProperties#OXM_METADATA_SOURCE */ @Deprecated public static final String ECLIPSELINK_OXM_XML_KEY = "eclipselink-oxm-xml"; /** * @deprecated As of release 2.4, replaced by JAXBContextProperties.DEFAULT_TARGET_NAMESPACE * @see org.eclipse.persistence.jaxb.JAXBContextProperties#DEFAULT_TARGET_NAMESPACE */ @Deprecated public static final String DEFAULT_TARGET_NAMESPACE_KEY = "defaultTargetNamespace"; /** * @deprecated As of release 2.4, replaced by JAXBContextProperties.ANNOTATION_HELPER * @see org.eclipse.persistence.jaxb.JAXBContextProperties#ANNOTATION_HELPER */ @Deprecated public static final String ANNOTATION_HELPER_KEY = "annotationHelper"; public static final String PKG_SEPARATOR = "."; /** * Create a JAXBContext on the array of Class objects. The JAXBContext will * also be aware of classes reachable from the classes in the array. */ public static javax.xml.bind.JAXBContext createContext(Class[] classesToBeBound, Map properties) throws JAXBException { ClassLoader loader = null; if (classesToBeBound.length > 0) { loader = classesToBeBound[0].getClassLoader(); } return createContext(classesToBeBound, properties, loader); } /** * Create a JAXBContext on the array of Class objects. The JAXBContext will * also be aware of classes reachable from the classes in the array. */ public static javax.xml.bind.JAXBContext createContext(Class[] classesToBeBound, Map properties, ClassLoader classLoader) throws JAXBException { Type[] types = new Type[classesToBeBound.length]; System.arraycopy(classesToBeBound, 0, types, 0, classesToBeBound.length); return createContext(types, properties, classLoader); } /** * Create a JAXBContext on context path. The JAXBContext will * also be aware of classes reachable from the classes on the context path. */ public static javax.xml.bind.JAXBContext createContext(String contextPath, ClassLoader classLoader) throws JAXBException { return createContext(contextPath, classLoader, null); } /** * Create a JAXBContext on context path. The JAXBContext will * also be aware of classes reachable from the classes on the context path. */ public static javax.xml.bind.JAXBContext createContext(String contextPath, ClassLoader classLoader, Map properties) throws JAXBException { JAXBContextInput contextInput = new ContextPathInput(contextPath, properties, classLoader); JAXBContext context = new JAXBContext(contextInput); if (context.isRefreshable()) { context.postInitialize(); } return context; } /** * Create a JAXBContext on the array of Type objects. The JAXBContext will * also be aware of classes reachable from the types in the array. The * preferred means of creating a Type aware JAXBContext is to create the * JAXBContext with an array of TypeMappingInfo objects. */ public static javax.xml.bind.JAXBContext createContext(Type[] typesToBeBound, Map properties, ClassLoader classLoader) throws JAXBException { Map typeToTypeMappingInfo = new HashMap(); TypeMappingInfo[] typeMappingInfos = new TypeMappingInfo[typesToBeBound.length]; for(int i = 0; i < typesToBeBound.length; i++) { TypeMappingInfo tmi = new TypeMappingInfo(); tmi.setType(typesToBeBound[i]); typeToTypeMappingInfo.put(typesToBeBound[i], tmi); typeMappingInfos[i] = tmi; } JAXBContext context = (JAXBContext)createContext(typeMappingInfos, properties, classLoader); context.setTypeToTypeMappingInfo(typeToTypeMappingInfo); return context; } /** * Create a JAXBContext on the array of TypeMappingInfo objects. The * JAXBContext will also be aware of classes reachable from the types in the * array. This is the preferred means of creating a Type aware JAXBContext. */ public static javax.xml.bind.JAXBContext createContext(TypeMappingInfo[] typesToBeBound, Map properties, ClassLoader classLoader) throws JAXBException { JAXBContextInput contextInput = new TypeMappingInfoInput(typesToBeBound, properties, classLoader); JAXBContext context = new JAXBContext(contextInput); if (context.isRefreshable()) { context.postInitialize(); } return context; } /** *

Convenience method for processing a properties map and creating a map of * package names to XmlBindings instances.

* *

It is assumed that the given map's key will be JAXBContextProperties.OXM_METADATA_SOURCE, * and the value will be:

*
     * 1)  {@literal Map}
     *     - Object is one of those listed in 3) below
     * 2)  {@literal List}
     *     - Object is one of those listed in 3) below
     *     - Bindings file must contain package-name attribute on 
     *       xml-bindings element
     * 3)  One of:
     *     - java.io.File
     *     - java.io.InputStream
     *     - java.io.Reader
     *     - java.lang.String
     *     - java.net.URL
     *     - javax.xml.stream.XMLEventReader
     *     - javax.xml.stream.XMLStreamReader
     *     - javax.xml.transform.Source
     *     - org.eclipse.persistence.jaxb.metadata.MetadataSource
     *     - org.w3c.dom.Node
     *     - org.xml.sax.InputSource
     *     
     *     - Bindings file must contain package-name attribute on 
     *       xml-bindings element
     * 
     */
    public static Map getXmlBindingsFromProperties(Map properties, ClassLoader classLoader) {
        Map> bindings = new HashMap>();
        Object value = null;
        if (properties != null) {
            if ((value = properties.get(JAXBContextProperties.OXM_METADATA_SOURCE)) == null) {
                // try looking up the 'old' metadata source key
                value = properties.get(ECLIPSELINK_OXM_XML_KEY);
            }
        }
        if (value != null) {
            // handle Map
            if (value instanceof Map) {
                Map metadataFiles = null;
                try {
                    metadataFiles = (Map) value;
                } catch (ClassCastException x) {
                    throw org.eclipse.persistence.exceptions.JAXBException.incorrectValueParameterTypeForOxmXmlKey();
                }
                for(Entry entry : metadataFiles.entrySet()) {
                    String key = null;
                    List xmlBindings = new ArrayList();
                    try {
                        key = entry.getKey();
                        if (key == null) {
                            throw org.eclipse.persistence.exceptions.JAXBException.nullMapKey();
                        }
                    } catch (ClassCastException cce) {
                        throw org.eclipse.persistence.exceptions.JAXBException.incorrectKeyParameterType();
                    }
                    Object metadataSource = entry.getValue();
                    if (metadataSource == null) {
                        throw org.eclipse.persistence.exceptions.JAXBException.nullMetadataSource(key);
                    }
                    if(metadataSource instanceof List) {
                        for(Object next: (List)metadataSource) {
                            XmlBindings binding = getXmlBindings(next, classLoader, properties);
                            if(binding != null) {
                                xmlBindings.add(binding);
                            }
                        }
                    } else {
                        XmlBindings binding = getXmlBindings(metadataSource, classLoader, properties);
                        if(binding != null) {
                            xmlBindings.add(binding);
                        }
                    }
                    if (xmlBindings != null) {
                        bindings.put(key, xmlBindings);
                    }
                }
                // handle List
            } else if (value instanceof List) {
                for (Object metadataSource : (List) value) {
                    if (metadataSource == null) {
                        throw org.eclipse.persistence.exceptions.JAXBException.nullMetadataSource();
                    }
                    bindings = processBindingFile(bindings, metadataSource, classLoader, properties);
                }
            // handle Object
            } else {
                bindings = processBindingFile(bindings, value, classLoader, properties);
            }
        }
        Map mergedBindings = new HashMap(bindings.size());
        for(Entry> next:bindings.entrySet()) {
            mergedBindings.put(next.getKey(), XMLProcessor.mergeXmlBindings(next.getValue()));
        }
        return mergedBindings;
    }
    
    /**
     * Processing a bindings file and add it to a given Map of package name to binding
     * files.
     * 
     * @param originalBindings Map of bindings to be updated
     * @param bindingHandle handle to bindings file
     * @param classLoader
     * @return
     */
    private static Map> processBindingFile(Map> originalBindings, Object bindingHandle, ClassLoader classLoader, Map properties) {
        XmlBindings binding = getXmlBindings(bindingHandle, classLoader, properties);
        if (binding != null) {
            String key = binding.getPackageName();
            if (key.equals(XMLProcessor.DEFAULT)) {
                throw org.eclipse.persistence.exceptions.JAXBException.packageNotSetForBindingException();
            }
            List existingBindings = originalBindings.get(key);
            if(existingBindings != null) {
                existingBindings.add(binding);
            } else {
                existingBindings = new ArrayList();
                existingBindings.add(binding);
                originalBindings.put(key, existingBindings);
            }
        }
        return originalBindings;
    }

    /**
     * Convenience method for creating an XmlBindings object based on a given Object. The method
     * will load the eclipselink metadata model and unmarshal the Object. This assumes that the
     * Object represents the eclipselink-oxm.xml metadata file to be unmarshalled.
     * 
     * @param metadata assumed to be one of:  File, InputSource, InputStream, Reader, Source
     */
    private static XmlBindings getXmlBindings(Object metadata, ClassLoader classLoader, Map properties) {
	JAXBContext jaxbContext = CompilerHelper.getXmlBindingsModelContext();
        InputStream openedStream = null;

	try {
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_XML);
            unmarshaller.setProperty(UnmarshallerProperties.AUTO_DETECT_MEDIA_TYPE, true);
            unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
            unmarshaller.setEventHandler(new DefaultValidationEventHandler());
            if (metadata instanceof MetadataSource){
                return ((MetadataSource)metadata).getXmlBindings(properties, classLoader);
            }  
            JAXBElement bindingsJaxbElement = null;               
            if (metadata instanceof XMLEventReader) {
            	bindingsJaxbElement=  unmarshaller.unmarshal((XMLEventReader) metadata, XmlBindings.class);            	
            } else if (metadata instanceof XMLStreamReader) {
            	bindingsJaxbElement =  unmarshaller.unmarshal((XMLStreamReader) metadata, XmlBindings.class);
            } else {
            	Source source = null;            
	            if(metadata instanceof File){
	            	source = new StreamSource(new FileInputStream((File) metadata));            
	            } else if(metadata instanceof InputSource){    
	                if(((InputSource)metadata).getByteStream() != null){                	
	                	source = new StreamSource(((InputSource) metadata).getByteStream());
	                }else if(((InputSource)metadata).getCharacterStream() != null){                	
	                	source = new StreamSource(((InputSource) metadata).getCharacterStream());	           
	                }
	            }else if(metadata instanceof InputStream){
	            	source = new StreamSource((InputStream) metadata);            
	            } else if (metadata instanceof Node) {
	            	source = new DOMSource((Node) metadata);              
	            } else if(metadata instanceof Reader){  
	            	source = new StreamSource((Reader) metadata);
	            } else if(metadata instanceof Source){
	                source = (Source)metadata;    	              
	            } else if (metadata instanceof URL) {
	            	openedStream = ((URL) metadata).openStream();
	            	source = new StreamSource(openedStream);
	            } else if (metadata instanceof String) {	            	
	            	StreamSource streamSource = new StreamSource((String)metadata);
	            	try{
		               	bindingsJaxbElement = unmarshaller.unmarshal(streamSource, XmlBindings.class);
		            }catch(JAXBException e){		               	        
			            openedStream = classLoader.getResourceAsStream((String)metadata);
			            if(openedStream != null){
			            	bindingsJaxbElement = unmarshaller.unmarshal(new StreamSource(openedStream), XmlBindings.class);
			            }else{
			                throw org.eclipse.persistence.exceptions.JAXBException.couldNotUnmarshalMetadata(e);
                        }
		            }
	            } else{
	                throw org.eclipse.persistence.exceptions.JAXBException.incorrectValueParameterTypeForOxmXmlKey();
	            } 
	            if(bindingsJaxbElement == null){
	                if(source == null){
	                    throw org.eclipse.persistence.exceptions.JAXBException.incorrectValueParameterTypeForOxmXmlKey();
	                }else{
	                    bindingsJaxbElement = unmarshaller.unmarshal(source, XmlBindings.class); 
	                }
	            }	          
            }
            if(bindingsJaxbElement != null){
            	return bindingsJaxbElement.getValue();
            }
            throw org.eclipse.persistence.exceptions.JAXBException.incorrectValueParameterTypeForOxmXmlKey();            
        }catch(javax.xml.bind.JAXBException ex){        	        	
            throw org.eclipse.persistence.exceptions.JAXBException.couldNotUnmarshalMetadata(ex);           
        }catch(IOException ioException){
             throw org.eclipse.persistence.exceptions.JAXBException.couldNotUnmarshalMetadata(ioException);
        }finally{
        	if(openedStream != null){
        		try {
					openedStream.close();
				} catch (IOException e) {
					throw org.eclipse.persistence.exceptions.JAXBException.couldNotUnmarshalMetadata(e);
				}
        	}
        }
    }
}