org.projectforge.database.xstream.HibernateXmlConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.projectforge.database.xstream.HibernateXmlConverter.java

Source

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2013 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.database.xstream;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.sf.cglib.proxy.Enhancer;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.PredicateUtils;
import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import com.thoughtworks.xstream.MarshallingStrategy;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.io.xml.CompactWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.mapper.MapperWrapper;

import de.micromata.hibernate.history.HistoryEntry;
import de.micromata.hibernate.history.delta.AssociationPropertyDelta;
import de.micromata.hibernate.history.delta.CollectionPropertyDelta;
import de.micromata.hibernate.history.delta.PropertyDelta;
import de.micromata.hibernate.history.delta.SimplePropertyDelta;
import de.micromata.hibernate.spring.NullWriter;
import de.micromata.hibernate.spring.ProxyIdRefMarshallingStrategy;

/**
 * Hilfsklasse zum Laden und Speichern einer gesamten Hibernate-Datenbank im XML-Format. Zur Darstellung der Daten in XML wird XStream zur
 * Serialisierung eingesetzt. Alle Lazy-Objekte aus Hibernate werden vollstndig initialisiert. http://jira.codehaus.org/browse/XSTR-377
 * 
 * @author Wolfgang Jung (w.jung@micromata.de)
 * 
 */
public class HibernateXmlConverter {
    /** The logger */
    private static final org.apache.log4j.Logger log = org.apache.log4j.Logger
            .getLogger(HibernateXmlConverter.class);

    /** the wrapper to hibernate */
    private HibernateTemplate hibernate;

    // Ignore these objects listing in the top level list saving because the are saved implicit by their parent objects.
    private final Set<Class<?>> ignoreFromTopLevelListing = new HashSet<Class<?>>();

    public HibernateXmlConverter() {
        this.ignoreFromTopLevelListing.add(PropertyDelta.class);
        this.ignoreFromTopLevelListing.add(SimplePropertyDelta.class);
        this.ignoreFromTopLevelListing.add(AssociationPropertyDelta.class);
        this.ignoreFromTopLevelListing.add(CollectionPropertyDelta.class);
    }

    /**
     * Initialisierung der Hibernate-verbindung.
     * 
     * @param hibernate ein bereits initialisiertes HibernateTemplate
     */
    public void setHibernate(final HibernateTemplate hibernate) {
        this.hibernate = hibernate;
    }

    /**
     * Schreibt alle Objekte der Datenbank in den angegebenen Writer.<br/>
     * <b>Warnung!</b> Bei der Serialisierung von Collections wird derzeit nur {@link java.util.Set} sauber untersttzt.
     * @param writer Ziel fr die XML-Datei.
     * @param includeHistory bei false werden die History Eintrge nicht geschrieben
     */
    public void dumpDatabaseToXml(final Writer writer, final boolean includeHistory) {
        dumpDatabaseToXml(writer, includeHistory, true);
    }

    /**
     * Schreibt alle Objekte der Datenbank in den angegebenen Writer.<br/>
     * <b>Warnung!</b> Bei der Serialisierung von Collections wird derzeit nur {@link java.util.Set} sauber untersttzt.
     * @param writer Ziel fr die XML-Datei.
     * @param includeHistory bei false werden die History Eintrge nicht geschrieben
     * @param preserveIds If true, the object ids will be preserved, otherwise new ids will be assigned through xstream.
     */
    public void dumpDatabaseToXml(final Writer writer, final boolean includeHistory, final boolean preserveIds) {
        final TransactionTemplate tx = new TransactionTemplate(
                new HibernateTransactionManager(hibernate.getSessionFactory()));
        tx.execute(new TransactionCallback() {
            public Object doInTransaction(final TransactionStatus status) {
                hibernate.execute(new HibernateCallback() {
                    public Object doInHibernate(final Session session) throws HibernateException {
                        writeObjects(writer, includeHistory, session, preserveIds);
                        status.setRollbackOnly();
                        return null;
                    }
                });
                return null;
            }
        });
    }

    public HibernateXmlConverter appendIgnoredTopLevelObjects(final Class<?>... types) {
        if (types != null) {
            for (final Class<?> type : types) {
                this.ignoreFromTopLevelListing.add(type);
            }
        }
        return this;
    }

    /**
     * @param writer
     * @param includeHistory
     * @param session
     * @throws DataAccessException
     * @throws HibernateException
     */
    private void writeObjects(final Writer writer, final boolean includeHistory, final Session session,
            final boolean preserveIds) throws DataAccessException, HibernateException {
        // Container fr die Objekte
        final List<Object> all = new ArrayList<Object>();
        final XStream stream = initXStream(session, true);
        final XStream defaultXStream = initXStream(session, false);

        session.flush();
        // Alles laden
        List<?> list = session.createQuery("select o from java.lang.Object o").setReadOnly(true).list();
        list = (List<?>) CollectionUtils.select(list, PredicateUtils.uniquePredicate());
        final int size = list.size();
        log.info("Writing " + size + " objects");
        for (final Iterator<?> it = list.iterator(); it.hasNext();) {
            final Object obj = it.next();
            if (log.isDebugEnabled()) {
                log.debug("loaded object " + obj);
            }
            if ((obj instanceof HistoryEntry || obj instanceof PropertyDelta) && includeHistory == false) {
                continue;
            }
            Hibernate.initialize(obj);
            Class<?> targetClass = obj.getClass();
            while (Enhancer.isEnhanced(targetClass) == true) {
                targetClass = targetClass.getSuperclass();
            }
            final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(targetClass);
            if (classMetadata == null) {
                log.fatal("Can't init " + obj + " of type " + targetClass);
                continue;
            }
            // initalisierung des Objekts...
            defaultXStream.marshal(obj, new CompactWriter(new NullWriter()));

            if (preserveIds == false) {
                // Nun kann die ID gelscht werden
                classMetadata.setIdentifier(obj, null, EntityMode.POJO);
            }
            if (log.isDebugEnabled()) {
                log.debug("loading evicted object " + obj);
            }
            if (this.ignoreFromTopLevelListing.contains(targetClass) == false) {
                all.add(obj);
            }
        }
        // und schreiben
        try {
            writer.write("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
        } catch (final IOException ex) {
            // ignore, will fail on stream.marshal()
        }
        log.info("Wrote " + all.size() + " objects");
        final MarshallingStrategy marshallingStrategy = new ProxyIdRefMarshallingStrategy();
        stream.setMarshallingStrategy(marshallingStrategy);
        stream.marshal(all, new PrettyPrintWriter(writer));
    }

    /**
     * Overload this method if you need further initializations before reading xml stream. Does nothing at default.
     * @param xstream
     */
    protected void init(final XStream xstream) {
    }

    /**
     * @return
     */
    private XStream initXStream(final Session session, final boolean nullifyPk) {
        final XStream xstream = new XStream() {
            @Override
            protected MapperWrapper wrapMapper(final MapperWrapper next) {
                return new HibernateMapper(new HibernateCollectionsMapper(next));
            }
        };
        // Converter fr die Hibernate-Collections
        xstream.registerConverter(new HibernateCollectionConverter(xstream.getConverterLookup()));
        xstream.registerConverter(new HibernateProxyConverter(xstream.getMapper(), new PureJavaReflectionProvider(),
                xstream.getConverterLookup()), XStream.PRIORITY_VERY_HIGH);
        xstream.setMarshallingStrategy(new XStreamMarshallingStrategy(XStreamMarshallingStrategy.RELATIVE));
        init(xstream);
        return xstream;
    }
}