001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 package org.jboss.dna.connector.federation.merge.strategy; 023 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.UUID; 027 import net.jcip.annotations.ThreadSafe; 028 import org.jboss.dna.connector.federation.contribution.Contribution; 029 import org.jboss.dna.connector.federation.merge.FederatedNode; 030 import org.jboss.dna.connector.federation.merge.MergePlan; 031 import org.jboss.dna.graph.DnaLexicon; 032 import org.jboss.dna.graph.ExecutionContext; 033 import org.jboss.dna.graph.Location; 034 import org.jboss.dna.graph.properties.Path; 035 import org.jboss.dna.graph.properties.PathFactory; 036 import org.jboss.dna.graph.properties.Property; 037 import org.jboss.dna.graph.properties.ValueFormatException; 038 039 /** 040 * A merge strategy that is optimized for merging when there is a single contribution. 041 * 042 * @author Randall Hauch 043 */ 044 @ThreadSafe 045 public class OneContributionMergeStrategy implements MergeStrategy { 046 047 /** 048 * {@inheritDoc} 049 * <p> 050 * This method only uses the one and only one non-null {@link Contribution} in the <code>contributions</code>. 051 * </p> 052 * 053 * @see org.jboss.dna.connector.federation.merge.strategy.MergeStrategy#merge(org.jboss.dna.connector.federation.merge.FederatedNode, 054 * java.util.List, org.jboss.dna.graph.ExecutionContext) 055 */ 056 public void merge( FederatedNode federatedNode, 057 List<Contribution> contributions, 058 ExecutionContext context ) { 059 assert federatedNode != null; 060 assert context != null; 061 assert contributions != null; 062 assert contributions.size() > 0; 063 Contribution contribution = contributions.get(0); 064 assert contribution != null; 065 final PathFactory pathFactory = context.getValueFactories().getPathFactory(); 066 final Location location = federatedNode.getActualLocationOfNode(); 067 068 // Copy the children ... 069 Iterator<Location> childIterator = contribution.getChildren(); 070 while (childIterator.hasNext()) { 071 Location child = translateChildFromSourceToRepository(pathFactory, location, childIterator.next()); 072 federatedNode.addChild(child); 073 } 074 075 // Copy the properties ... 076 Property uuidProperty = null; 077 Property dnaUuidProperty = null; 078 Iterator<Property> propertyIterator = contribution.getProperties(); 079 while (propertyIterator.hasNext()) { 080 Property property = propertyIterator.next(); 081 federatedNode.addProperty(property); 082 if (property.isSingle()) { 083 if (property.getName().equals(DnaLexicon.UUID) && hasUuidValue(context, property)) { 084 dnaUuidProperty = property; 085 } else if (property.getName().getLocalName().equals("uuid") && hasUuidValue(context, property)) { 086 uuidProperty = property; 087 } 088 } 089 } 090 if (dnaUuidProperty != null) uuidProperty = dnaUuidProperty; // use "dna:uuid" if there is one 091 092 // Look for the UUID property on the properties, and update the federated node ... 093 if (uuidProperty != null && !uuidProperty.isEmpty()) { 094 UUID uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getValues().next()); 095 federatedNode.setUuid(uuid); 096 if (dnaUuidProperty == null) { 097 uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid); // Use the "dna:uuid" name 098 } 099 federatedNode.setActualLocationOfNode(federatedNode.getActualLocationOfNode().with(uuidProperty)); 100 } else { 101 // See if there is a UUID property on the location and update the federated node with it... 102 uuidProperty = federatedNode.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID); 103 if (uuidProperty == null || uuidProperty.isEmpty()) { 104 // Generate a new UUID property and add to the node ... 105 UUID uuid = federatedNode.getUuid(); 106 if (uuid == null) { 107 uuid = context.getValueFactories().getUuidFactory().create(); 108 federatedNode.setUuid(uuid); 109 } 110 uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid); 111 } 112 // Set the UUID as a property ... 113 federatedNode.addProperty(uuidProperty); 114 } 115 116 // Assign the merge plan ... 117 MergePlan mergePlan = MergePlan.create(contributions); 118 federatedNode.setMergePlan(mergePlan); 119 } 120 121 private boolean hasUuidValue( ExecutionContext context, 122 Property property ) { 123 assert property.isSingle(); 124 try { 125 context.getValueFactories().getUuidFactory().create(property.getValues().next()); 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 }