/**************************************************************************** * Copyright (C) 2012 ecsec GmbH. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.ws.jaxb; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; 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 javax.xml.bind.annotation.XmlType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Wrapper for JAXB marshaller and unmarshaller capable of modifying the supported JAXB types on the fly. * * @author Tobias Wich <tobias.wich@ecsec.de> */ public class MarshallerImpl { private static final Logger logger = LoggerFactory.getLogger(MarshallerImpl.class); private static final List<Class> baseXmlElementClasses; private static final JAXBContext baseJaxbContext; private boolean userOverride; private final Set<Class> userClasses; private Marshaller marshaller; private Unmarshaller unmarshaller; static { // load predefined classes Class[] jaxbClasses = getJaxbClasses(); baseXmlElementClasses = new ArrayList<Class>(jaxbClasses.length); baseXmlElementClasses.addAll(Arrays.asList(jaxbClasses)); try { baseJaxbContext = JAXBContext.newInstance(jaxbClasses); } catch (JAXBException ex) { logger.error("Failed to create JAXBContext instance.", ex); throw new RuntimeException("Failed to create JAXBContext."); } } /** * Creates a MarshallerImpl instance based on the JAXB types specified in the classpath resource classes.lst. */ public MarshallerImpl() { userOverride = false; userClasses = new HashSet<Class>(baseXmlElementClasses); } /** * Adds the specified JAXB element types class to the list of supported JAXB types. * This method triggers a recreation of the wrapped marshaller and unmarshaller. * * @param c Class of the JAXB element type. */ public synchronized void addXmlClass(Class c) { if (! userClasses.contains(c)) { //addJaxbClasses(c); userClasses.add(c); // class added to set userOverride = true; resetMarshaller(); } } /** * Remove all JAXB element types from this instance. * New types must be added first before this instance is usable for marshalling and unmarshalling again. */ public synchronized void removeAllClasses() { userOverride = true; userClasses.clear(); resetMarshaller(); } /** * Gets the wrapped JAXB marshaller instance. * * @return The wrapped JAXB marshaller instance. * @throws JAXBException If the marshaller could not be created. */ public synchronized Marshaller getMarshaller() throws JAXBException { if (marshaller == null) { loadInstances(); } return marshaller; } /** * Gets the wrapped JAXB unmarshaller instance. * * @return The wrapped JAXB unmarshaller instance. * @throws JAXBException If the unmarshaller could not be created. */ public synchronized Unmarshaller getUnmarshaller() throws JAXBException { if (unmarshaller == null) { loadInstances(); } return unmarshaller; } private void resetMarshaller() { marshaller = null; unmarshaller = null; } private void loadInstances() throws JAXBException { JAXBContext jaxbCtx; if (userOverride) { jaxbCtx = JAXBContext.newInstance(userClasses.toArray(new Class[userClasses.size()])); } else { jaxbCtx = baseJaxbContext; } marshaller = jaxbCtx.createMarshaller(); unmarshaller = jaxbCtx.createUnmarshaller(); } private static Class[] getJaxbClasses() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); List<Class> classes = new LinkedList<Class>(); InputStream classListStream = cl.getResourceAsStream("classes.lst"); InputStream classListStreamC = cl.getResourceAsStream("/classes.lst"); try { if (classListStream == null && classListStreamC == null) { throw new IOException("Failed to load classes.lst."); } else { // select the one stream that is set classListStream = (classListStream != null) ? classListStream : classListStreamC; LineNumberReader r = new LineNumberReader(new InputStreamReader(classListStream)); String next; // read all entries from file while ((next = r.readLine()) != null) { try { // load class and see if it is a JAXB class Class<?> c = cl.loadClass(next); if (isJaxbClass(c)) { classes.add(c); } } catch (ClassNotFoundException ex) { logger.error("Failed to load class: " + next, ex); } } } } catch (IOException ex) { logger.error("Failed to read classes from file classes.lst.", ex); } return classes.toArray(new Class[classes.size()]); } private static boolean isJaxbClass(Class<?> c) { return c.getAnnotation(XmlType.class) != null; } }