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

com.marklogic.client.io.JAXBHandle Maven / Gradle / Ivy

/*
 * Copyright 2012-2016 MarkLogic Corporation
 *
 * 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 com.marklogic.client.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.marklogic.client.MarkLogicIOException;
import com.marklogic.client.io.marker.BufferableHandle;
import com.marklogic.client.io.marker.ContentHandle;
import com.marklogic.client.io.marker.ContentHandleFactory;
import com.marklogic.client.io.marker.XMLReadHandle;
import com.marklogic.client.io.marker.XMLWriteHandle;

/**
 * A JAXB Handle roundtrips a POJO (a Java data structure) to and from a database document.
 * 
 * The POJO class must have JAXB annotations or must be generated by xjc from an XML Schema.
 * 
 * The JAXB Handle must be initialized with a JAXB Context with which the root POJO classes
 * have been registered.
 * 
 * @param		one of the classes (or the <?> wildcard for any of the classes) registered with the JAXB Context
 */
public class JAXBHandle
	extends BaseHandle
    implements OutputStreamSender, BufferableHandle, ContentHandle,
        XMLReadHandle, XMLWriteHandle
{
	static final private Logger logger = LoggerFactory.getLogger(JAXBHandle.class);

	private JAXBContext  context;
	private Unmarshaller unmarshaller;
	private Marshaller   marshaller;
	private C            content;

	/**
	 * Creates a factory to create a JAXBHandle instance for POJO instances
	 * of the specified classes.
	 * @param pojoClasses	the POJO classes for which this factory provides a handle
	 * @return	the factory
	 * @throws JAXBException if a JAXB error occurs while initializing the new factory
	 */
	static public ContentHandleFactory newFactory(Class... pojoClasses)
	throws JAXBException {
		if (pojoClasses == null || pojoClasses.length == 0)
			return null;
		return new JAXBHandleFactory(pojoClasses);
	}
	/**
	 * Creates a factory to create a JAXBHandle instance for POJO instances
	 * of the specified classes.
	 * @param context	the JAXB context for marshaling the POJO classes
	 * @param pojoClasses	the POJO classes for which this factory provides a handle
	 * @return	the factory
	 * @throws JAXBException if a JAXB error occurs while initializing the new factory
	 */
	static public ContentHandleFactory newFactory(JAXBContext context, Class... pojoClasses)
	throws JAXBException {
		if (context == null || pojoClasses == null || pojoClasses.length == 0)
			return null;
		return new JAXBHandleFactory(context, pojoClasses);
	}

	/**
	 * Initializes the JAXB handle with the JAXB context for the classes
	 * of the marshalled or unmarshalled structure.
	 * @param context	the JAXB context
	 */
	public JAXBHandle(JAXBContext context) {
		super();
		if (context == null) {
			throw new IllegalArgumentException(
					"null JAXB context for converting classes"
					);
		}
		super.setFormat(Format.XML);
   		setResendable(true);
		this.context = context;
	}

	/**
	 * Returns the root object of the JAXB structure for the content.
	 * @return	the root JAXB object
	 */
	@Override
	public C get() {
		return content;
	}
	/**
	 * Returns the root object of the JAXB structure for the content
	 * cast to a more specific class.
	 * @param as	the class of the object
	 * @param  the type to return
	 * @return	the root JAXB object
	 */
	public  T get(Class as) {
		if (content == null) {
			return null;
		}
		if (as == null) {
			throw new IllegalArgumentException("Cannot cast content to null class");
		}
		if (!as.isAssignableFrom(content.getClass())) {
			throw new IllegalArgumentException(
					"Cannot cast "+content.getClass().getName()+" to "+as.getName()
					);
		}
		@SuppressWarnings("unchecked")
		T content = (T) get();
		return content;
	}
	/**
	 * Assigns the root object of the JAXB structure for the content.
	 * @param content	the root JAXB object
	 */
	@Override
    public void set(C content) {
    	this.content = content;
    }
    /**
	 * Assigns the root object of the JAXB structure for the content
	 * and returns the handle as a fluent convenience.
	 * @param content	the root JAXB object
	 * @return	this handle
     */
    public JAXBHandle with(C content) {
    	set(content);
    	return this;
    }

	/**
	 * Restricts the format to XML.
	 */
	@Override
    public void setFormat(Format format) {
		if (format != Format.XML)
			throw new IllegalArgumentException("JAXBHandle supports the XML format only");
	}
	/**
	 * Specifies the mime type of the content and returns the handle
	 * as a fluent convenience.
	 * @param mimetype	the mime type of the content
	 * @return	this handle
	 */
	public JAXBHandle withMimetype(String mimetype) {
		setMimetype(mimetype);
		return this;
	}

	/**
     * fromBuffer() unmarshals a JAXB POJO from a byte array
     * buffer.  The buffer must store the marshaled XML for the 
     * JAXB POJO in UTF-8 encoding. JAXB cannot unmarshal arbitrary XML.
	 */
	@Override
	public void fromBuffer(byte[] buffer) {
		if (buffer == null || buffer.length == 0)
			content = null;
		else
			receiveContent(new ByteArrayInputStream(buffer));
	}
	@Override
	public byte[] toBuffer() {
		try {
			if (content == null)
				return null;

			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
			write(buffer);

			return buffer.toByteArray();
		} catch (IOException e) {
			throw new MarkLogicIOException(e);
		}
	}
	/**
	 * Returns the JAXB structure as an XML string.
	 */
	@Override
	public String toString() {
		try {
			return new String(toBuffer(),"UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new MarkLogicIOException(e);
		}
	}

	/**
	 * Returns the unmarshaller that converts a tree data structure
	 * from XML to Java objects, reusing any existing unmarshaller.
	 * @return	the unmarshaller for the JAXB context
	 * @throws JAXBException if unmarshaller initialization fails
	 */
	public Unmarshaller getUnmarshaller()
	throws JAXBException {
		return getUnmarshaller(true);
	}
	/**
	 * Returns the unmarshaller that converts a tree data structure
	 * from XML to Java objects.
	 * @param reuse	whether to reuse an existing unmarshaller
	 * @return	the unmarshaller for the JAXB context
	 * @throws JAXBException if unmarshaller initialization fails
	 */
	public Unmarshaller getUnmarshaller(boolean reuse)
	throws JAXBException {
		if (!reuse || unmarshaller == null) {
			unmarshaller = context.createUnmarshaller();
		}
		return unmarshaller;
	}
	/**
	 * Returns the marshaller that converts a tree data structure
	 * from Java objects to XML, reusing any existing marshaller.
	 * @return	the marshaller for the JAXB context
	 * @throws JAXBException if marshaller initialization fails
	 */
	public Marshaller getMarshaller()
	throws JAXBException {
		return getMarshaller(true);
	}
	/**
	 * Returns the marshaller that converts a tree data structure
	 * from Java objects to XML.
	 * @param reuse	whether to reuse an existing marshaller
	 * @return	the marshaller for the JAXB context
	 * @throws JAXBException if marshaller initialization fails
	 */
	public Marshaller getMarshaller(boolean reuse)
	throws JAXBException {
		if (!reuse || this.marshaller == null) {
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
			marshaller.setProperty(Marshaller.JAXB_ENCODING,         "UTF-8");
			this.marshaller = marshaller;
		}
		return this.marshaller;
	}

	@Override
	protected Class receiveAs() {
    	return InputStream.class;
    }
    @Override
	protected void receiveContent(InputStream content) {
		try {
			@SuppressWarnings("unchecked")
			C unmarshalled = (C) getUnmarshaller().unmarshal(
					new InputStreamReader(content, "UTF-8")
					);
			this.content = unmarshalled;
		} catch (JAXBException e) {
			logger.error("Failed to unmarshall object read from database document",e);
			throw new MarkLogicIOException(e);
		} catch (UnsupportedEncodingException e) {
			logger.error("Failed to unmarshall object read from database document",e);
			throw new MarkLogicIOException(e);
		}  finally {
			try {
				content.close();
			} catch (IOException e) {
				// ignore.
			}
		}
	}
    @Override
	protected OutputStreamSender sendContent() {
		if (content == null) {
			throw new IllegalStateException("No object to write");
		}

		return this;
	}

	@Override
	public void write(OutputStream out) throws IOException {
		try {
			getMarshaller().marshal(content, out);
		} catch (JAXBException e) {
			logger.error("Failed to marshall object for writing to database document",e);
			throw new MarkLogicIOException(e);
		}
	}

	static private class JAXBHandleFactory implements ContentHandleFactory {
		private Class[]    pojoClasses;
		private JAXBContext   factoryContext;
		private Set> classSet;

		private JAXBHandleFactory(Class... pojoClasses)
		throws JAXBException {
			this(JAXBContext.newInstance(pojoClasses), pojoClasses);
		}
		private JAXBHandleFactory(JAXBContext factoryContext, Class... pojoClasses)
		throws JAXBException {
			super();
			this.pojoClasses    = pojoClasses;
			this.factoryContext = factoryContext;
			this.classSet       = new HashSet>(Arrays.asList(pojoClasses));
		}

		@Override
		public Class[] getHandledClasses() {
			return pojoClasses;
		}
		@Override
		public boolean isHandled(Class type) {
			return classSet.contains(type);
		}
		@Override
		public  ContentHandle newHandle(Class type) {
			ContentHandle handle = isHandled(type) ?
					(ContentHandle) new JAXBHandle(factoryContext) : null;
			return handle;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy