Java tutorial
/** * OpenSpotLight - Open Source IT Governance Platform * * Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA * or third-party contributors as indicated by the @author tags or express * copyright attribution statements applied by the authors. All third-party * contributions are distributed under license by CARAVELATECH CONSULTORIA E * TECNOLOGIA EM INFORMATICA LTDA. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program 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 distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * *********************************************************************** * OpenSpotLight - Plataforma de Governana de TI de Cdigo Aberto * * Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA * EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta * @author ou por expressa atribuio de direito autoral declarada e atribuda pelo autor. * Todas as contribuies de terceiros esto distribudas sob licena da * CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA. * * Este programa software livre; voc pode redistribu-lo e/ou modific-lo sob os * termos da Licena Pblica Geral Menor do GNU conforme publicada pela Free Software * Foundation. * * Este programa distribudo na expectativa de que seja til, porm, SEM NENHUMA * GARANTIA; nem mesmo a garantia implcita de COMERCIABILIDADE OU ADEQUAO A UMA * FINALIDADE ESPEC?FICA. Consulte a Licena Pblica Geral Menor do GNU para mais detalhes. * * Voc deve ter recebido uma cpia da Licena Pblica Geral Menor do GNU junto com este * programa; se no, escreva para: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.openspotlight.persist.support; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newLinkedList; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.newHashSet; import static java.lang.Class.forName; import static java.text.MessageFormat.format; import static java.util.Collections.reverse; import static java.util.Collections.sort; import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors; import static org.openspotlight.common.util.Assertions.checkCondition; import static org.openspotlight.common.util.Assertions.checkNotNull; import static org.openspotlight.common.util.Exceptions.logAndReturnNew; import static org.openspotlight.common.util.Reflection.unwrapCollectionFromMethodReturn; import static org.openspotlight.common.util.Reflection.unwrapMapFromMethodReturn; import static org.openspotlight.common.util.SLCollections.iterableToList; import java.beans.PropertyDescriptor; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.SerializationUtils; import org.openspotlight.common.Pair; import org.openspotlight.common.collection.IteratorBuilder; import org.openspotlight.common.exception.SLRuntimeException; import org.openspotlight.common.util.Conversion; import org.openspotlight.common.util.Reflection; import org.openspotlight.common.util.Wrapper; import org.openspotlight.persist.annotation.IndexedProperty; import org.openspotlight.persist.annotation.KeyProperty; import org.openspotlight.persist.annotation.Name; import org.openspotlight.persist.annotation.ParentProperty; import org.openspotlight.persist.annotation.PersistPropertyAsStream; import org.openspotlight.persist.annotation.SimpleNodeType; import org.openspotlight.persist.annotation.TransientProperty; import org.openspotlight.persist.internal.LazyProperty; import org.openspotlight.persist.internal.StreamPropertyWithParent; import org.openspotlight.storage.NodeCriteria.NodeCriteriaBuilder; import org.openspotlight.storage.Partition; import org.openspotlight.storage.StorageSession; import org.openspotlight.storage.domain.NodeFactory; import org.openspotlight.storage.domain.StorageNode; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.inject.Singleton; /** * Created by IntelliJ IDEA. User: feuteston Date: 05/04/2010 Time: 13:19:32 To change this template use File | Settings | File * Templates. */ @Singleton public class SimplePersistImpl implements SimplePersistCapable<StorageNode, StorageSession> { private static class BeanToNodeChildData { final Collection<SimpleNodeType> childrenToSave; final boolean multiple; final Class<?> nodeType; final String propertyName; private BeanToNodeChildData(final String propertyName, final boolean multiple, final Class<?> nodeType) { this.propertyName = propertyName; this.nodeType = nodeType; childrenToSave = newHashSet(); this.multiple = multiple; } } private static class ConversionToBeanContext { final Wrapper<SimpleNodeType> beanReference = Wrapper.createMutable(); final Map<StorageNode, Object> beansConverted = newHashMap(); final StorageNode node; private ConversionToBeanContext(final StorageNode node) { this.node = node; } } private static class ConversionToNodeContext { final Set<StorageNode> allNodes = newHashSet(); final SimpleNodeType bean; final Wrapper<StorageNode> nodeReference = Wrapper.createMutable(); final Map<Object, StorageNode> nodesConverted = newHashMap(); ConversionToNodeContext(final SimpleNodeType bean) { this.bean = bean; } } private static class Descriptors { private List<Pair<String, SimpleNodeType>> rootObjects = null; final SimpleNodeType bean; final Class<?> beanType; final List<PropertyDescriptor> childrenPropertiesDescriptor; final List<PropertyDescriptor> keyPropertiesDescriptor; final List<PropertyDescriptor> lazyPropertiesDescriptor; final List<PropertyDescriptor> parentPropertiesDescriptor; final Wrapper<PropertyDescriptor> parentPropertyDescriptor; final List<PropertyDescriptor> simplePropertiesDescriptor; final List<PropertyDescriptor> streamPropertiesDescriptor; private Descriptors(final Class<?> beanType, final SimpleNodeType bean, final Wrapper<PropertyDescriptor> parentPropertyDescriptor, final List<PropertyDescriptor> simplePropertiesDescriptor, final List<PropertyDescriptor> keyPropertiesDescriptor, final List<PropertyDescriptor> streamPropertiesDescriptor, final List<PropertyDescriptor> childrenPropertiesDescriptor, final List<PropertyDescriptor> parentPropertiesDescriptor, final List<PropertyDescriptor> lazyPropertiesDescriptor) { this.beanType = beanType; this.bean = bean; this.parentPropertyDescriptor = parentPropertyDescriptor; this.simplePropertiesDescriptor = simplePropertiesDescriptor; this.keyPropertiesDescriptor = keyPropertiesDescriptor; this.streamPropertiesDescriptor = streamPropertiesDescriptor; this.childrenPropertiesDescriptor = childrenPropertiesDescriptor; this.parentPropertiesDescriptor = parentPropertiesDescriptor; this.lazyPropertiesDescriptor = lazyPropertiesDescriptor; } static <T extends SimpleNodeType> Descriptors fillDescriptors(final Class<?> beanType) throws Exception { return fillDescriptors(null, beanType); } static <T extends SimpleNodeType> Descriptors fillDescriptors(final Object bean) throws Exception { return fillDescriptors(bean, bean.getClass()); } static <T extends SimpleNodeType> Descriptors fillDescriptors(final Object bean, final Class<?> beanType) throws Exception { final Descriptors descriptors = Descriptors.createMutable(beanType, bean); for (final PropertyDescriptor descriptor : getPropertyDescriptors(beanType)) { if (descriptor.getName().equals("class")) { continue;// Object#getClass } final Method readMethod = descriptor.getReadMethod(); if (readMethod.isAnnotationPresent(TransientProperty.class)) { continue; } final Class<?> returnType = readMethod.getReturnType(); if (readMethod.isAnnotationPresent(ParentProperty.class)) { final Object value = bean != null ? readMethod.invoke(bean) : null; if (value != null && descriptors.parentPropertyDescriptor != null && descriptors.parentPropertyDescriptor.getWrapped() != null) { throw new IllegalStateException("only one parent property is allowed"); } if (value != null) { descriptors.parentPropertyDescriptor.setWrapped(descriptor); } descriptors.parentPropertiesDescriptor.add(descriptor); } else if (readMethod.isAnnotationPresent(KeyProperty.class)) { descriptors.keyPropertiesDescriptor.add(descriptor); } else if (readMethod.isAnnotationPresent(PersistPropertyAsStream.class)) { descriptors.streamPropertiesDescriptor.add(descriptor); } else if (returnType.isAssignableFrom(InputStream.class)) { descriptors.streamPropertiesDescriptor.add(descriptor); } else if (SimpleNodeType.class.isAssignableFrom(returnType)) { descriptors.childrenPropertiesDescriptor.add(descriptor); } else if (Collection.class.isAssignableFrom(returnType)) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodInformation = unwrapCollectionFromMethodReturn( readMethod); if (SimpleNodeType.class.isAssignableFrom(methodInformation.getItemType())) { descriptors.childrenPropertiesDescriptor.add(descriptor); } else { descriptors.streamPropertiesDescriptor.add(descriptor); } } else if (Map.class.isAssignableFrom(returnType)) { final Reflection.UnwrappedMapTypeFromMethodReturn<Object, Object> methodInformation = unwrapMapFromMethodReturn( readMethod); if (SimpleNodeType.class.isAssignableFrom(methodInformation.getItemType().getK2())) { descriptors.childrenPropertiesDescriptor.add(descriptor); } else { descriptors.streamPropertiesDescriptor.add(descriptor); } } else if (LazyProperty.class.isAssignableFrom(returnType)) { descriptors.lazyPropertiesDescriptor.add(descriptor); } else { descriptors.simplePropertiesDescriptor.add(descriptor); } } return Descriptors.createImmutableFrom(descriptors); } public static Descriptors createImmutableFrom(final Descriptors from) { return new Descriptors(from.beanType, from.bean, Wrapper.<PropertyDescriptor>createImmutable(from.parentPropertyDescriptor.getWrapped()), ImmutableList.copyOf(from.simplePropertiesDescriptor), ImmutableList.copyOf(from.keyPropertiesDescriptor), ImmutableList.copyOf(from.streamPropertiesDescriptor), ImmutableList.copyOf(from.childrenPropertiesDescriptor), ImmutableList.copyOf(from.parentPropertiesDescriptor), ImmutableList.copyOf(from.lazyPropertiesDescriptor)); } public static Descriptors createMutable(final Class<?> beanType, final Object bean) { return new Descriptors(beanType, (SimpleNodeType) bean, Wrapper.<PropertyDescriptor>createMutable(), Lists.<PropertyDescriptor>newLinkedList(), Lists.<PropertyDescriptor>newLinkedList(), Lists.<PropertyDescriptor>newLinkedList(), Lists.<PropertyDescriptor>newLinkedList(), Lists.<PropertyDescriptor>newLinkedList(), Lists.<PropertyDescriptor>newLinkedList()); } private List<Pair<String, SimpleNodeType>> loadRootObjects() throws Exception { final List<Pair<String, SimpleNodeType>> resultInReverseOrder = newLinkedList(); Descriptors currentDescriptors = this; Object parent = bean; Object oldParent = null; do { final PropertyDescriptor descriptor = currentDescriptors.parentPropertyDescriptor.getWrapped(); oldParent = parent; final Class<?> oldType = oldParent != null ? oldParent.getClass() : null; parent = descriptor != null ? descriptor.getReadMethod().invoke(parent) : null; currentDescriptors = parent != null ? Descriptors.fillDescriptors(parent) : null; String oldParentName = null; if (oldParent != null && currentDescriptors != null) { lookingForNames: for (final PropertyDescriptor childDescriptor : currentDescriptors.childrenPropertiesDescriptor) { if (childDescriptor.getPropertyType().equals(oldType)) { oldParentName = childDescriptor.getName(); break lookingForNames; } } } resultInReverseOrder.add(Pair.newPair(oldParentName, (SimpleNodeType) oldParent)); } while (currentDescriptors != null && oldParent != null); reverse(resultInReverseOrder); return ImmutableList.copyOf(resultInReverseOrder); } public List<Pair<String, SimpleNodeType>> getRootObjects() throws Exception { if (rootObjects == null) { rootObjects = loadRootObjects(); } return rootObjects; } } private class InternalMethodsImpl implements InternalMethods { @Override public Object beforeUnConvert(final SimpleNodeType bean, final Serializable value, final Method readMethod) { try { if (value instanceof Collection) { boolean mayBeStreamPropertyWithParent = true; if (readMethod != null) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription = unwrapCollectionFromMethodReturn( readMethod); mayBeStreamPropertyWithParent = StreamPropertyWithParent.class .isAssignableFrom(methodDescription.getItemType()); } if (mayBeStreamPropertyWithParent) { final Collection<?> valueAsCollection = (Collection<?>) value; for (final Object o : valueAsCollection) { if (o instanceof StreamPropertyWithParent) { ((StreamPropertyWithParent) o).setParent(bean); } } } } else if (value instanceof Map) { boolean mayBeStreamPropertyWithParent = true; if (readMethod != null) { final Reflection.UnwrappedMapTypeFromMethodReturn<Object, Object> methodDescription = unwrapMapFromMethodReturn( readMethod); mayBeStreamPropertyWithParent = StreamPropertyWithParent.class .isAssignableFrom(methodDescription.getItemType().getK2()); } if (mayBeStreamPropertyWithParent) { final Map<?, ?> valueAsMap = (Map<?, ?>) value; for (final Map.Entry<?, ?> entry : valueAsMap.entrySet()) { if (entry.getValue() instanceof StreamPropertyWithParent) { ((StreamPropertyWithParent) entry.getValue()).setParent(bean); } } } } else if (value instanceof StreamPropertyWithParent) { ((StreamPropertyWithParent) value).setParent(bean); } return value; } catch (final Exception e) { throw logAndReturnNew(e, SLRuntimeException.class); } } @Override public String getNodeName(final Class<?> nodeType) { return internalGetNodeName(nodeType); } } private static final String[] EMPTY_NAMES = new String[] {}; private static final Object[] EMPTY_VALUES = new Object[] {}; private static final String NODE_ENTRY_TYPE = "internal-node-entry-type"; private static final String NODE_PROPERTY_NAME = "internal-node-proeprty-name"; private static final String SHA1_PROPERTY_NAME = "internal-{0}-sha1"; private final Partition currentPartition; private final StorageSession currentSession; private final InternalMethods internalMethods = new InternalMethodsImpl(); public SimplePersistImpl(final StorageSession currentSession, final Partition currentPartition) { this.currentSession = currentSession; this.currentPartition = currentPartition; } private static <T extends Serializable> T asObject(final InputStream is) throws Exception { if (is == null) { return null; } final ObjectInputStream ois = new ObjectInputStream(is); final T result = (T) ois.readObject(); return result; } private static <T extends Serializable> InputStream asStream(final T o) throws Exception { if (o == null) { return null; } final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ObjectOutputStream ois = new ObjectOutputStream(baos); ois.writeObject(o); ois.flush(); return new ByteArrayInputStream(baos.toByteArray()); } private Serializable beforeSerializeList(final List<? extends Serializable> value, final Method readMethod) throws Exception { if (readMethod != null) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription = unwrapCollectionFromMethodReturn( readMethod); if (StreamPropertyWithParent.class.isAssignableFrom(methodDescription.getItemType())) { final LinkedList<Serializable> newCollection = newLinkedList(); for (final Serializable o : value) { newCollection.add(beforeSerializeSerializable(o)); } return newCollection; } return (Serializable) value; } else { final LinkedList<Serializable> newCollection = newLinkedList(); for (final Serializable o : value) { newCollection.add(beforeSerializeSerializable(o)); } return newCollection; } } private Serializable beforeSerializeMap(final Map<? extends Serializable, ? extends Serializable> value, final Method readMethod) throws Exception { if (readMethod != null) { final Reflection.UnwrappedMapTypeFromMethodReturn<Object, Object> methodDescription = unwrapMapFromMethodReturn( readMethod); if (StreamPropertyWithParent.class.isAssignableFrom(methodDescription.getItemType().getK2())) { final HashMap<Serializable, Serializable> newMap = newHashMap(); for (final Map.Entry<? extends Serializable, ? extends Serializable> entry : value.entrySet()) { newMap.put(entry.getKey(), beforeSerializeSerializable(entry.getValue())); } return newMap; } return (Serializable) value; } else { final HashMap<Serializable, Serializable> newMap = newHashMap(); for (final Map.Entry<? extends Serializable, ? extends Serializable> entry : value.entrySet()) { newMap.put(entry.getKey(), beforeSerializeSerializable(entry.getValue())); } return newMap; } } private Serializable beforeSerializeSerializable(final Serializable value) { if (value instanceof StreamPropertyWithParent) { final StreamPropertyWithParent typedValue = (StreamPropertyWithParent) value; final SimpleNodeType oldParent = typedValue.getParent(); typedValue.setParent(null); final StreamPropertyWithParent newValue = (StreamPropertyWithParent) SerializationUtils.clone(value); typedValue.setParent(oldParent); return newValue; } return value; } private Serializable beforeSerializeSet(final Set<? extends Serializable> value, final Method readMethod) throws Exception { if (readMethod != null) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription = unwrapCollectionFromMethodReturn( readMethod); if (StreamPropertyWithParent.class.isAssignableFrom(methodDescription.getItemType())) { final HashSet<Serializable> newCollection = newHashSet(); for (final Serializable o : value) { newCollection.add(beforeSerializeSerializable(o)); } return newCollection; } return (Serializable) value; } else { final HashSet<Serializable> newCollection = newHashSet(); for (final Serializable o : value) { newCollection.add(beforeSerializeSerializable(o)); } return newCollection; } } private Map<String, PropertyDescriptor> createMapWith(final PropertyDescriptor[] propertyDescriptors) { final ImmutableMap.Builder<String, PropertyDescriptor> builder = ImmutableMap .<String, PropertyDescriptor>builder(); for (final PropertyDescriptor d : propertyDescriptors) { builder.put(d.getName(), d); } return builder.build(); } private <T> StorageNode createNewNode(final ConversionToNodeContext context, final StorageNode parentNode, final Descriptors descriptors, final String propertyName) throws Exception { final String name = internalGetNodeName(descriptors.bean); final NodeFactory.NodeBuilder builder = currentSession.withPartition(currentPartition) .createNodeWithType(name); if (parentNode != null) { builder.withParent(parentNode); } for (final PropertyDescriptor descriptor : descriptors.keyPropertiesDescriptor) { builder.withSimpleKey(descriptor.getName(), Conversion.convert(descriptor.getReadMethod().invoke(descriptors.bean), String.class)); } if (propertyName != null) { builder.withSimpleKey(NODE_PROPERTY_NAME, propertyName); } final StorageNode newNode = builder.andCreate(); newNode.setIndexedProperty(currentSession, NODE_ENTRY_TYPE, internalGetNodeType(descriptors.bean)); return newNode; } private <T> void fillBeanChildren(final ConversionToBeanContext context, final StorageNode node, final T bean, final List<PropertyDescriptor> childrenPropertiesDescriptor) throws Exception { for (final PropertyDescriptor descriptor : childrenPropertiesDescriptor) { final Class<?> propertyType = descriptor.getPropertyType(); Class<?> nodeType = null; final boolean isMultiple = Collection.class.isAssignableFrom(propertyType); final Method readMethod = descriptor.getReadMethod(); if (isMultiple) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription = unwrapCollectionFromMethodReturn( readMethod); if (List.class.isAssignableFrom(propertyType)) { descriptor.getWriteMethod().invoke(bean, newLinkedList()); } else if (Set.class.isAssignableFrom(propertyType)) { descriptor.getWriteMethod().invoke(bean, newHashSet()); } else { throw new IllegalStateException("wrong child type"); } nodeType = methodDescription.getItemType(); } else if (SimpleNodeType.class.isAssignableFrom(propertyType)) { nodeType = propertyType; } else { throw new IllegalStateException("wrong child type"); } if (!SimpleNodeType.class.isAssignableFrom(nodeType)) { throw new IllegalStateException("wrong child type"); } final String childrenName = internalGetNodeName(nodeType); Iterable<StorageNode> children = iterableToList( node.getChildren(currentPartition, currentSession, childrenName)); children = filterChildrenWithProperty(children, descriptor.getName()); final List<Object> childrenAsBeans = newLinkedList(); for (final StorageNode child : children) { childrenAsBeans.add(internalConvertNodeToBean(context, child, bean)); } if (isMultiple) { final Collection c = (Collection) readMethod.invoke(bean); for (final Object o : childrenAsBeans) { c.add(o); } if (Comparable.class.isAssignableFrom(nodeType) && c instanceof List) { sort((List) c); } } else if (childrenAsBeans.size() > 0) { final Object value = childrenAsBeans.iterator().next(); descriptor.getWriteMethod().invoke(bean, value); } } } private void fillBeanLazyProperties(final StorageNode cached, final Object bean, final List<PropertyDescriptor> lazyPropertiesDescriptors) throws Exception { for (final PropertyDescriptor property : lazyPropertiesDescriptors) { final String propertyName = property.getName(); final LazyProperty<?> lazyProperty = (LazyProperty<?>) property.getReadMethod().invoke(bean); lazyProperty.getMetadata().setParentKey(cached.getKey()); lazyProperty.getMetadata().setSavedNode(cached); lazyProperty.getMetadata().setPropertyName(propertyName); final String sha1 = cached.getPropertyValueAsString(currentSession, format(SHA1_PROPERTY_NAME, propertyName)); lazyProperty.getMetadata().internalSetSha1(sha1); } } private void fillBeanParent(final List<PropertyDescriptor> parentPropertyDescriptors, final Object bean, final Object beanParent) throws Exception { if (beanParent != null) { final Class<?> parentType = beanParent.getClass(); for (final PropertyDescriptor descriptor : parentPropertyDescriptors) { if (descriptor.getPropertyType().isAssignableFrom(parentType)) { descriptor.getWriteMethod().invoke(bean, beanParent); break; } } } } private <T> void fillBeanSimpleProperties(final StorageNode node, final T bean, final List<PropertyDescriptor> simplePropertiesDescriptor) throws Exception { for (final PropertyDescriptor descriptor : simplePropertiesDescriptor) { final String value = node.getPropertyValueAsString(currentSession, descriptor.getName()); if (value == null && descriptor.getPropertyType().isPrimitive()) { continue; } descriptor.getWriteMethod().invoke(bean, Conversion.convert(value, descriptor.getPropertyType())); } } private <T> void fillBeanStreamProperties(final StorageNode node, final T bean, final List<PropertyDescriptor> streamPropertiesDescriptor) throws Exception { for (final PropertyDescriptor descriptor : streamPropertiesDescriptor) { final Class<?> propertyType = descriptor.getPropertyType(); if (InputStream.class.isAssignableFrom(propertyType)) { final InputStream value = node.getPropertyValueAsStream(currentSession, descriptor.getName()); descriptor.getWriteMethod().invoke(bean, value); } else if (Serializable.class.isAssignableFrom(propertyType) || Collection.class.isAssignableFrom(propertyType) || Map.class.isAssignableFrom(propertyType)) { final Serializable value = asObject( node.getPropertyValueAsStream(currentSession, descriptor.getName())); descriptor.getWriteMethod().invoke(bean, getInternalMethods().beforeUnConvert((SimpleNodeType) bean, value, descriptor.getReadMethod())); } else { throw new IllegalStateException("wrong type"); } } } private <T> void fillNodeChildrenProperties(final ConversionToNodeContext context, final T bean, final List<PropertyDescriptor> childrenPropertiesDescriptor, final StorageNode newNodeEntry) throws Exception { final Map<String, BeanToNodeChildData> nodesToConvert = newHashMap(); for (final PropertyDescriptor property : childrenPropertiesDescriptor) { final String propertyName = property.getName(); BeanToNodeChildData data = nodesToConvert.get(propertyName); final Class<?> propertyType = property.getPropertyType(); final Object value = property.getReadMethod().invoke(bean); if (SimpleNodeType.class.isAssignableFrom(propertyType)) { if (data == null) { data = new BeanToNodeChildData(propertyName, Collection.class.isAssignableFrom(propertyType), propertyType); nodesToConvert.put(propertyName, data); } data.childrenToSave.add((SimpleNodeType) value); } else if (Collection.class.isAssignableFrom(propertyType)) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodInformation = unwrapCollectionFromMethodReturn( property.getReadMethod()); if (data == null) { data = new BeanToNodeChildData(propertyName, Collection.class.isAssignableFrom(propertyType), methodInformation.getItemType()); nodesToConvert.put(propertyName, data); } if (List.class.isAssignableFrom(methodInformation.getCollectionType())) { for (final SimpleNodeType t : (List<SimpleNodeType>) value) { data.childrenToSave.add(t); } } else if (Set.class.isAssignableFrom(methodInformation.getCollectionType())) { for (final SimpleNodeType t : (Set<SimpleNodeType>) value) { data.childrenToSave.add(t); } } else { throw new IllegalStateException("invalid collection type"); } } else { throw new IllegalStateException("invalid type:" + property.getPropertyType()); } } for (final BeanToNodeChildData data : nodesToConvert.values()) { if (!data.multiple && data.childrenToSave.size() > 1) { throw new IllegalStateException("single property with more than one child"); } for (final SimpleNodeType beanBeenSaved : data.childrenToSave) { internalConvertBeanToNode(context, data.propertyName, beanBeenSaved, newNodeEntry); } context.allNodes.addAll(iterableToList(newNodeEntry.getChildren(currentPartition, currentSession, internalGetNodeName(data.nodeType)))); } } private void fillNodeLazyProperties(final ConversionToNodeContext context, final SimpleNodeType bean, final List<PropertyDescriptor> lazyPropertiesDescriptor, final StorageNode nodeEntry) throws Exception { for (final PropertyDescriptor descriptor : lazyPropertiesDescriptor) { final LazyProperty<?> property = (LazyProperty<?>) descriptor.getReadMethod().invoke(bean); if (property != null && property.getMetadata().needsSave()) { final String propertyName = descriptor.getName(); final Object value = property.getMetadata().getTransient(); if (value instanceof InputStream) { nodeEntry.setSimpleProperty(currentSession, propertyName, (InputStream) value); } else if (value instanceof Set) { nodeEntry.setSimpleProperty(currentSession, propertyName, asStream(beforeSerializeSet((Set<? extends Serializable>) value, null))); } else if (value instanceof List) { nodeEntry.setSimpleProperty(currentSession, propertyName, asStream(beforeSerializeList((List<? extends Serializable>) value, null))); } else if (value instanceof Map) { nodeEntry.setSimpleProperty(currentSession, propertyName, asStream( beforeSerializeMap((Map<? extends Serializable, ? extends Serializable>) value, null))); } else {// Serializable nodeEntry.setSimpleProperty(currentSession, propertyName, asStream(beforeSerializeSerializable((Serializable) value))); } nodeEntry.setSimpleProperty(currentSession, format(SHA1_PROPERTY_NAME, propertyName), property.getMetadata().getSha1()); property.getMetadata().markAsSaved(); } ; } } private <T> void fillNodeSimpleProperties(final T bean, final List<PropertyDescriptor> simplePropertiesDescriptor, final StorageNode newNodeEntry) throws Exception { for (final PropertyDescriptor property : simplePropertiesDescriptor) { if (property.getReadMethod().isAnnotationPresent(IndexedProperty.class)) { newNodeEntry.setIndexedProperty(currentSession, property.getName(), Conversion.convert(property.getReadMethod().invoke(bean), String.class)); } else { newNodeEntry.setSimpleProperty(currentSession, property.getName(), Conversion.convert(property.getReadMethod().invoke(bean), String.class)); } } } private <T> void fillNodeStreamProperties(final T bean, final List<PropertyDescriptor> streamPropertiesDescriptor, final StorageNode newNodeEntry) throws Exception { for (final PropertyDescriptor property : streamPropertiesDescriptor) { final Class<?> propertyType = property.getPropertyType(); final Method readMethod = property.getReadMethod(); final Object value = readMethod.invoke(bean); if (InputStream.class.isAssignableFrom(propertyType)) { newNodeEntry.setSimpleProperty(currentSession, property.getName(), (InputStream) value); } else if (Collection.class.isAssignableFrom(propertyType)) { final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodInformation = unwrapCollectionFromMethodReturn( property.getReadMethod()); if (List.class.isAssignableFrom(methodInformation.getCollectionType())) { newNodeEntry.setSimpleProperty(currentSession, property.getName(), asStream(beforeSerializeList((List<? extends Serializable>) value, readMethod))); } else if (Set.class.isAssignableFrom(methodInformation.getCollectionType())) { newNodeEntry.setSimpleProperty(currentSession, property.getName(), asStream(beforeSerializeSet((Set<? extends Serializable>) value, readMethod))); } else { throw new IllegalStateException("invalid collection type"); } } else if (Map.class.isAssignableFrom(propertyType)) { newNodeEntry.setSimpleProperty(currentSession, property.getName(), asStream(beforeSerializeMap((Map<? extends Serializable, ? extends Serializable>) value, readMethod))); } else if (Serializable.class.isAssignableFrom(propertyType)) { if (propertyType.equals(String.class) || Number.class.isAssignableFrom(propertyType) || propertyType.isPrimitive() || Boolean.class.equals(propertyType) || Character.class.equals(propertyType) || Date.class.equals(propertyType)) { newNodeEntry.setSimpleProperty(currentSession, property.getName(), Conversion.convert(value, String.class)); } else { newNodeEntry.setSimpleProperty(currentSession, property.getName(), asStream(beforeSerializeSerializable((Serializable) value))); } } else { throw new IllegalStateException("invalid type"); } } } private Iterable<StorageNode> filterChildrenWithProperty(final Iterable<StorageNode> children, final String name) { if (name == null) { return children; } final List<StorageNode> filtered = newLinkedList(); for (final StorageNode e : children) { final String propertyValue = e.getPropertyValueAsString(currentSession, NODE_PROPERTY_NAME); if (name.equals(propertyValue)) { filtered.add(e); } } return filtered; } private Class<?> findClassFromNode(final StorageNode nodeEntry) throws Exception { return forName(nodeEntry.getPropertyValueAsString(currentSession, NODE_ENTRY_TYPE)); } private <T> StorageNode internalConvertBeanToNode(final ConversionToNodeContext context, final String propertyName, final SimpleNodeType bean, final StorageNode parentNode) throws Exception { final boolean firstInvocation = bean == null; if (firstInvocation) { StorageNode currentParentNode = parentNode; final Descriptors currentBeanDescriptors = Descriptors.fillDescriptors(context.bean); final List<Pair<String, SimpleNodeType>> rootObjects = currentBeanDescriptors.getRootObjects(); for (final Pair<String, SimpleNodeType> pair : rootObjects) { currentParentNode = internalConvertBeanToNode(context, pair.getK1(), pair.getK2(), currentParentNode); } context.allNodes.removeAll(context.nodesConverted.values()); for (final StorageNode unusedNode : context.allNodes) { currentSession.removeNode(unusedNode); } return context.nodeReference.getWrapped(); } else { StorageNode cached = context.nodesConverted.get(bean); if (cached == null) { final Descriptors parentDescriptors = Descriptors.fillDescriptors(bean); cached = createNewNode(context, parentNode, parentDescriptors, propertyName); context.nodesConverted.put(bean, cached); fillNodeSimpleProperties(bean, parentDescriptors.simplePropertiesDescriptor, cached); fillNodeStreamProperties(bean, parentDescriptors.streamPropertiesDescriptor, cached); fillNodeChildrenProperties(context, bean, parentDescriptors.childrenPropertiesDescriptor, cached); fillNodeLazyProperties(context, bean, parentDescriptors.lazyPropertiesDescriptor, cached); if (bean == context.bean) { context.nodeReference.setWrapped(cached); } } return cached; } } private <T> StorageNode internalConvertBeanToNode(final StorageNode parent, final T bean) throws Exception { final ConversionToNodeContext context = new ConversionToNodeContext((SimpleNodeType) bean); internalConvertBeanToNode(context, null, null, parent); final StorageNode result = context.nodeReference.getWrapped(); checkNotNull("result", result); return result; } private <T> Iterable<T> internalConvertNodesToBeans(final Iterable<StorageNode> nodes) throws Exception { final IteratorBuilder.SimpleIteratorBuilder<T, StorageNode> b = IteratorBuilder.createIteratorBuilder(); b.withConverter(new IteratorBuilder.Converter<T, StorageNode>() { @Override public T convert(final StorageNode nodeEntry) throws Exception { return (T) convertNodeToBean(nodeEntry); } }); final Iterable<T> result = b.withItems(nodes).andBuild(); return result; } private Object internalConvertNodeToBean(final ConversionToBeanContext context, final StorageNode nodeEntry, final Object beanParent) throws Exception { if (nodeEntry == null) { final List<StorageNode> parents = newLinkedList(); StorageNode currentParent = context.node; while (currentParent != null && isSimpleNode(currentParent)) { parents.add(currentParent); currentParent = currentParent.getParent(currentSession); } reverse(parents); Object currentParentAsBean = null; for (final StorageNode parentEntry : parents) { currentParentAsBean = internalConvertNodeToBean(context, parentEntry, currentParentAsBean); } return context.beanReference.getWrapped(); } else { Object cached = context.beansConverted.get(nodeEntry); if (cached == null) { final Class<?> beanType = findClassFromNode(nodeEntry); cached = beanType.newInstance(); context.beansConverted.put(nodeEntry, cached); final Descriptors descriptors = Descriptors.fillDescriptors(beanType); fillBeanParent(descriptors.parentPropertiesDescriptor, cached, beanParent); fillBeanSimpleProperties(nodeEntry, cached, descriptors.keyPropertiesDescriptor); fillBeanSimpleProperties(nodeEntry, cached, descriptors.simplePropertiesDescriptor); fillBeanStreamProperties(nodeEntry, cached, descriptors.streamPropertiesDescriptor); fillBeanLazyProperties(nodeEntry, cached, descriptors.lazyPropertiesDescriptor); fillBeanChildren(context, nodeEntry, cached, descriptors.childrenPropertiesDescriptor); if (nodeEntry.equals(context.node)) { context.beanReference.setWrapped((SimpleNodeType) cached); } } return cached; } } private <T> String internalGetNodeName(final Class<T> beanType) { final Name annotation = beanType.getAnnotation(Name.class); return annotation != null ? annotation.value() : beanType.getName(); } private <T> String internalGetNodeName(final T bean) { return this.<T>internalGetNodeName((Class<T>) bean.getClass()); } private <T> String internalGetNodeType(final Class<T> beanType) { return beanType.getName(); } private String internalGetNodeType(final StorageNode node) { return node.getType(); } private <T> String internalGetNodeType(final T bean) { return this.<T>internalGetNodeType((Class<T>) bean.getClass()); } private boolean isSimpleNode(final StorageNode currentParent) { return currentParent.getPropertyValueAsString(currentSession, NODE_ENTRY_TYPE) != null; } @Override public void closeResources() { currentSession.closeResources(); } @Override public <T> Iterable<StorageNode> convertBeansToNodes(final Iterable<T> beans) { return convertBeansToNodes(null, beans); } @Override public <T> Iterable<StorageNode> convertBeansToNodes(final StorageNode parent, final Iterable<T> beans) { try { final List<StorageNode> itemsConverted = newArrayList(); for (final T bean : beans) { itemsConverted.add(convertBeanToNode(parent, bean)); } return itemsConverted; } catch (final Exception e) { throw logAndReturnNew(e, SLRuntimeException.class); } } @Override public <T> StorageNode convertBeanToNode(final StorageNode parent, final T bean) { try { return internalConvertBeanToNode(parent, bean); } catch (final Exception e) { throw logAndReturnNew(e, SLRuntimeException.class); } } @Override public <T> StorageNode convertBeanToNode(final T bean) throws Exception { return convertBeanToNode(null, bean); } @Override public <T> Iterable<T> convertNodesToBeans(final Iterable<StorageNode> nodes) { try { return internalConvertNodesToBeans(nodes); } catch (final Exception e) { throw logAndReturnNew(e, SLRuntimeException.class); } } @Override public <T> T convertNodeToBean(final StorageNode node) throws Exception { final T bean = (T) internalConvertNodeToBean(new ConversionToBeanContext(node), null, null); return bean; } @Override public <T> Iterable<T> findAll(final Class<T> beanType) { return findByProperties(beanType, EMPTY_NAMES, EMPTY_VALUES); } @Override public <T> Iterable<T> findAll(final StorageNode parentNode, final Class<T> beanType) { return findByProperties(parentNode, beanType, EMPTY_NAMES, EMPTY_VALUES); } @Override public <T> Iterable<T> findByProperties(final Class<T> beanType, final String[] propertyNames, final Object[] propertyValues) { return findByProperties(null, beanType, propertyNames, propertyValues); } @Override public <T> Iterable<T> findByProperties(final StorageNode parent, final Class<T> beanType, final String[] propertyNames, final Object[] propertyValues) { try { checkNotNull("currentPartition", currentPartition); checkNotNull("currentSession", currentSession); checkNotNull("beanType", beanType); checkNotNull("propertyNames", propertyNames); checkNotNull("propertyValues", propertyValues); checkCondition("namesAndValues:sameSize", propertyNames.length == propertyValues.length); final NodeCriteriaBuilder builder = currentSession.withPartition(currentPartition).createCriteria() .withNodeType(internalGetNodeName(beanType)); final Map<String, PropertyDescriptor> allDescriptors = createMapWith( PropertyUtils.getPropertyDescriptors(beanType)); for (int i = 0, size = propertyNames.length; i < size; i++) { final PropertyDescriptor descriptor = allDescriptors.get(propertyNames[i]); if (descriptor == null) { throw new SLRuntimeException("invalid property:" + propertyNames[i]); } builder.withProperty(propertyNames[i]) .equalsTo(Conversion.convert(propertyValues[i], String.class)); } final Iterable<StorageNode> foundItems = builder.buildCriteria().andSearch(currentSession); final IteratorBuilder.SimpleIteratorBuilder<T, StorageNode> b = IteratorBuilder .<T, StorageNode>createIteratorBuilder(); b.withConverter(new IteratorBuilder.Converter<T, StorageNode>() { @Override public T convert(final StorageNode nodeEntry) throws Exception { return (T) convertNodeToBean(nodeEntry); } }); b.withReferee(new IteratorBuilder.NextItemReferee<StorageNode>() { @Override public boolean canAcceptAsNewItem(final StorageNode nodeEntry) { final String typeAsString = nodeEntry.getPropertyValueAsString(currentSession, NODE_ENTRY_TYPE); if (typeAsString != null && typeAsString.equals(beanType.getName())) { if (parent != null) { StorageNode parentNode = nodeEntry; while (parentNode != null) { if (parentNode.getKey().equals(parent.getKey())) { return true; } parentNode = parentNode.getParent(currentSession); } return false; } return true; } return false; } }); final Iterable<T> result = b.withItems(foundItems).andBuild(); return result; } catch (final Exception e) { throw logAndReturnNew(e, SLRuntimeException.class); } } @Override public <T> T findUnique(final Class<T> beanType) { return findUniqueByProperties(beanType, EMPTY_NAMES, EMPTY_VALUES); } @Override public <T> T findUnique(final StorageNode parentNode, final Class<T> beanType) { return findUniqueByProperties(parentNode, beanType, EMPTY_NAMES, EMPTY_VALUES); } @Override public <T> T findUniqueByProperties(final Class<T> beanType, final String[] propertyNames, final Object[] propertyValues) { return findUniqueByProperties(null, beanType, propertyNames, propertyValues); } @Override public <T> T findUniqueByProperties(final StorageNode parent, final Class<T> beanType, final String[] propertyNames, final Object[] propertyValues) { final Iterable<T> result = findByProperties(parent, beanType, propertyNames, propertyValues); final Iterator<T> it = result.iterator(); final T resultAsBean = it.hasNext() ? it.next() : null; return resultAsBean; } @Override public Partition getCurrentPartition() { return currentPartition; } @Override public StorageSession getCurrentSession() { return currentSession; } @Override public InternalMethods getInternalMethods() { return internalMethods; } @Override public StorageSession.PartitionMethods getPartitionMethods() { return currentSession.withPartition(currentPartition); } }