org.xwiki.store.datanucleus.internal.DataNucleusPersistableObjectStore.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.store.datanucleus.internal.DataNucleusPersistableObjectStore.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.store.datanucleus.internal;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;

import javax.jdo.JDOHelper;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;
import javax.jdo.JDOObjectNotFoundException;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.store.TransactionException;
import org.xwiki.store.TransactionRunnable;
import org.xwiki.store.UnexpectedException;
import org.xwiki.store.objects.PersistableObject;
import org.xwiki.store.objects.PersistableClass;
import org.xwiki.store.objects.PersistableClassLoader;

/**
 * The DataNucleusClassLoader is designed to load java classes from the data store.
 */
public class DataNucleusPersistableObjectStore {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataNucleusPersistableObjectStore.class);

    public TransactionRunnable<PersistenceManager> getStoreTransactionRunnable(final String key,
            final PersistableObject value) {
        return (new TransactionRunnable<PersistenceManager>() {
            protected void onRun() {
                final PersistenceManager manager = this.getContext();
                manager.setDetachAllOnCommit(true);
                final Set<PersistableClass> classes = getClassesAndSetIds(key, value);
                LOGGER.debug("Storing object [{}] with classes [{}].", key, classes.size());
                for (final PersistableClass pc : classes) {
                    // "Internal" PCs such as PersistableXWikiDocument have 0 length
                    // and should never be stored.
                    if (pc.getBytes().length > 0 && (!JDOHelper.isDetached(pc) || JDOHelper.isDirty(pc))) {
                        LOGGER.debug("Storing class [{}] bytecode length [{}].", pc.getName(),
                                pc.getBytes().length);
                        manager.makePersistent(pc);
                    }
                }
                manager.makePersistent(value);
            }
        });
    }

    public TransactionRunnable<PersistenceManager> getLoadTransactionRunnable(final Collection<String> keys,
            final String className, final Collection<PersistableObject> outputs) {
        return (new TransactionRunnable<PersistenceManager>() {
            protected void onRun() throws ClassNotFoundException, ClassCastException {
                final Class cls = Class.forName(className);
                final PersistenceManager pm = this.getContext();
                pm.setDetachAllOnCommit(true);

                for (final String key : keys) {
                    try {
                        outputs.add((PersistableObject) pm.getObjectById(cls, key));
                    } catch (JDOObjectNotFoundException e) {
                        LOGGER.debug("Document [{}] was not found.", key);
                    }
                }
            }
        });
    }

    private static Set<PersistableClass> getClassesAndSetIds(final String key, final PersistableObject value) {
        final Map<String, PersistableObject> objectsByKey = new HashMap<String, PersistableObject>();
        walkTree(key, value, objectsByKey, new Stack());

        final Set<PersistableClass> out = new HashSet<PersistableClass>();
        for (final Map.Entry<String, PersistableObject> e : objectsByKey.entrySet()) {
            e.getValue().setId(e.getKey());
            out.add(e.getValue().getPersistableClass());
        }
        return out;
    }

    private static boolean potentiallyPersistable(final Class c) {
        return c.isAssignableFrom(PersistableObject.class) || PersistableObject.class.isAssignableFrom(c);
    }

    /**
     * Walk the elements in a collection or array looking for persistable objects.
     *
     * @param id the identifier for the object.
     * @param value the collction or array to walk, if this is not a collection or array,
     *              nothing will be done.
     * @param out, the output into which all discovered persistable objects will be placed.
     * @param stack a stack used for internal loop detection.
     */
    private static void walkTree(final String id, final Object value, final Map<String, PersistableObject> out,
            final Stack stack) {
        if (value == null || stack.contains(value)) {
            return;
        }
        stack.push(value);

        if (value instanceof PersistableObject) {
            out.put(id, (PersistableObject) value);

            // Index over the fields looking for more persistables
            for (final Field field : value.getClass().getDeclaredFields()) {
                final Class type = field.getType();
                boolean isMap = Map.class.isAssignableFrom(type);
                if (isMap || Collection.class.isAssignableFrom(type)) {
                    final ParameterizedType pType = (ParameterizedType) field.getGenericType();
                    final Class<?> componentType = (Class<?>) pType.getActualTypeArguments()[isMap ? 1 : 0];
                    if (!potentiallyPersistable(componentType)) {
                        continue;
                    }
                } else if (type.isArray()) {
                    if (!potentiallyPersistable(type.getComponentType())) {
                        continue;
                    }
                } else if (!potentiallyPersistable(type)) {
                    continue;
                }
                // We don't need to use reflection for this, we can use the jdo state manager.
                field.setAccessible(true);
                try {
                    walkTree(id + "." + field.getName(), field.get(value), out, stack);
                } catch (IllegalAccessException e) {
                    throw new UnexpectedException(
                            "Could not reflect nested objects, " + "is a security manager preventing it?");
                }
            }
        } else {
            walkCollection(id, value, out, stack);
        }

        stack.pop();
        return;
    }

    /**
     * Walk the elements in a collection or array looking for persistable objects.
     *
     * @param id the identifier for the object.
     * @param value the collction or array to walk, if this is not a collection or array,
     *              nothing will be done.
     * @param out, the output into which all discovered persistable objects will be placed.
     * @param stack a stack used for internal loop detection.
     */
    private static void walkCollection(final String id, final Object value,
            final Map<String, PersistableObject> out, final Stack stack) {
        if (value instanceof Map) {
            for (final Map.Entry e : ((Map<?, ?>) value).entrySet()) {
                final String nextId = id + "['" + e.getKey() + "']";
                walkTree(nextId, e.getValue(), out, stack);
            }
        } else if (value instanceof Collection) {
            int i = 0;
            for (final Object item : ((Collection) value)) {
                walkTree(id + "[" + i + "]", item, out, stack);
                i++;
            }
        } else if (value.getClass().isArray()) {
            for (int i = 0; i < Array.getLength(value); i++) {
                walkTree(id + "[" + i + "]", Array.get(value, i), out, stack);
            }
        }
    }
}