001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.connector.federation.merge.strategy;
025
026 import java.util.Iterator;
027 import java.util.List;
028 import java.util.UUID;
029 import net.jcip.annotations.ThreadSafe;
030 import org.jboss.dna.connector.federation.contribution.Contribution;
031 import org.jboss.dna.connector.federation.merge.FederatedNode;
032 import org.jboss.dna.connector.federation.merge.MergePlan;
033 import org.jboss.dna.graph.DnaLexicon;
034 import org.jboss.dna.graph.ExecutionContext;
035 import org.jboss.dna.graph.Location;
036 import org.jboss.dna.graph.property.Path;
037 import org.jboss.dna.graph.property.PathFactory;
038 import org.jboss.dna.graph.property.Property;
039 import org.jboss.dna.graph.property.ValueFormatException;
040
041 /**
042 * A merge strategy that is optimized for merging when there is a single contribution.
043 *
044 * @author Randall Hauch
045 */
046 @ThreadSafe
047 public class OneContributionMergeStrategy implements MergeStrategy {
048
049 /**
050 * {@inheritDoc}
051 * <p>
052 * This method only uses the one and only one non-null {@link Contribution} in the <code>contributions</code>.
053 * </p>
054 *
055 * @see org.jboss.dna.connector.federation.merge.strategy.MergeStrategy#merge(org.jboss.dna.connector.federation.merge.FederatedNode,
056 * java.util.List, org.jboss.dna.graph.ExecutionContext)
057 */
058 public void merge( FederatedNode federatedNode,
059 List<Contribution> contributions,
060 ExecutionContext context ) {
061 assert federatedNode != null;
062 assert context != null;
063 assert contributions != null;
064 assert contributions.size() > 0;
065 Contribution contribution = contributions.get(0);
066 assert contribution != null;
067 final PathFactory pathFactory = context.getValueFactories().getPathFactory();
068 Location location = federatedNode.getActualLocationOfNode();
069
070 // Copy the children ...
071 Iterator<Location> childIterator = contribution.getChildren();
072 while (childIterator.hasNext()) {
073 Location child = translateChildFromSourceToRepository(pathFactory, location, childIterator.next());
074 federatedNode.addChild(child);
075 }
076
077 // Copy the properties ...
078 Property uuidProperty = null;
079 Property dnaUuidProperty = null;
080 Iterator<Property> propertyIterator = contribution.getProperties();
081 while (propertyIterator.hasNext()) {
082 Property property = propertyIterator.next();
083 federatedNode.addProperty(property);
084 if (property.isSingle()) {
085 if (property.getName().equals(DnaLexicon.UUID) && hasUuidValue(context, property)) {
086 dnaUuidProperty = property;
087 } else if (property.getName().getLocalName().equals("uuid") && hasUuidValue(context, property)) {
088 uuidProperty = property;
089 }
090 }
091 }
092 if (dnaUuidProperty != null) uuidProperty = dnaUuidProperty; // use "dna:uuid" if there is one
093
094 if (federatedNode.at().getUuid() != null) {
095 federatedNode.setActualLocationOfNode(federatedNode.at());
096 } else {
097 // Look for the UUID property on the properties, and update the federated node ...
098 if (uuidProperty != null && !uuidProperty.isEmpty()) {
099 UUID uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getFirstValue());
100 uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid); // Use the "dna:uuid" name
101 federatedNode.setActualLocationOfNode(federatedNode.at().with(uuidProperty));
102 } else {
103 // Make sure there's a UUID for an identification property ...
104 if (location.getUuid() == null) {
105 location = location.with(UUID.randomUUID());
106 }
107 // Set the UUID as a property (it wasn't set already) ...
108 uuidProperty = location.getIdProperty(DnaLexicon.UUID);
109 assert uuidProperty != null; // there should be one!
110 federatedNode.addProperty(uuidProperty);
111 federatedNode.setActualLocationOfNode(location);
112 }
113 }
114
115 // Assign the merge plan ...
116 MergePlan mergePlan = MergePlan.create(contributions);
117 federatedNode.setMergePlan(mergePlan);
118 Property mergePlanProperty = context.getPropertyFactory().create(DnaLexicon.MERGE_PLAN, (Object)mergePlan);
119 federatedNode.addProperty(mergePlanProperty);
120 }
121
122 private boolean hasUuidValue( ExecutionContext context,
123 Property property ) {
124 try {
125 context.getValueFactories().getUuidFactory().create(property.getFirstValue());
126 return true;
127 } catch (ValueFormatException e) {
128 return false;
129 }
130 }
131
132 /**
133 * Utility method to translate the list of locations of the children so that the locations all are correctly relative to
134 * parent location of the federated node.
135 *
136 * @param factory the path factory
137 * @param parent the parent of the child
138 * @param childInSource the child to be translated, with a source-specific location
139 * @return the list of locations of each child
140 */
141 protected Location translateChildFromSourceToRepository( PathFactory factory,
142 Location parent,
143 Location childInSource ) {
144 // Convert the locations of the children (relative to the source) to be relative to this node
145 Path parentPath = parent.getPath();
146 if (parentPath == null) return childInSource;
147 Path newPath = factory.create(parentPath, childInSource.getPath().getLastSegment());
148 return childInSource.with(newPath);
149 }
150 }