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.repository.sequencers; 023 024 import java.io.InputStream; 025 import java.math.BigDecimal; 026 import java.util.Calendar; 027 import java.util.Date; 028 import java.util.Set; 029 import javax.jcr.Node; 030 import javax.jcr.PathNotFoundException; 031 import javax.jcr.Property; 032 import javax.jcr.RepositoryException; 033 import javax.jcr.Session; 034 import org.jboss.dna.common.monitor.ProgressMonitor; 035 import org.jboss.dna.common.util.Logger; 036 import org.jboss.dna.graph.properties.Binary; 037 import org.jboss.dna.graph.properties.DateTime; 038 import org.jboss.dna.graph.properties.Name; 039 import org.jboss.dna.graph.properties.NamespaceRegistry; 040 import org.jboss.dna.graph.properties.Path; 041 import org.jboss.dna.graph.properties.PathFactory; 042 import org.jboss.dna.graph.sequencers.StreamSequencer; 043 import org.jboss.dna.repository.RepositoryI18n; 044 import org.jboss.dna.repository.observation.NodeChange; 045 import org.jboss.dna.repository.util.JcrExecutionContext; 046 import org.jboss.dna.repository.util.RepositoryNodePath; 047 048 /** 049 * An adapter class that wraps a {@link StreamSequencer} instance to be a {@link Sequencer}. 050 * 051 * @author Randall Hauch 052 */ 053 public class StreamSequencerAdapter implements Sequencer { 054 055 private SequencerConfig configuration; 056 private final StreamSequencer streamSequencer; 057 058 public StreamSequencerAdapter( StreamSequencer streamSequencer ) { 059 this.streamSequencer = streamSequencer; 060 } 061 062 /** 063 * {@inheritDoc} 064 */ 065 public SequencerConfig getConfiguration() { 066 return this.configuration; 067 } 068 069 /** 070 * {@inheritDoc} 071 */ 072 public void setConfiguration( SequencerConfig configuration ) { 073 this.configuration = configuration; 074 } 075 076 /** 077 * {@inheritDoc} 078 */ 079 public void execute( Node input, 080 String sequencedPropertyName, 081 NodeChange changes, 082 Set<RepositoryNodePath> outputPaths, 083 JcrExecutionContext execContext, 084 ProgressMonitor progressMonitor ) throws RepositoryException, SequencerException { 085 // 'sequencedPropertyName' contains the name of the modified property on 'input' that resulted in the call to this 086 // sequencer. 087 // 'changes' contains all of the changes to this node that occurred in the transaction. 088 // 'outputPaths' contains the paths of the node(s) where this sequencer is to save it's data. 089 090 try { 091 progressMonitor.beginTask(100, RepositoryI18n.sequencingPropertyOnNode, sequencedPropertyName, input.getPath()); 092 093 // Get the property that contains the data, given by 'propertyName' ... 094 Property sequencedProperty = null; 095 try { 096 sequencedProperty = input.getProperty(sequencedPropertyName); 097 } catch (PathNotFoundException e) { 098 String msg = RepositoryI18n.unableToFindPropertyForSequencing.text(sequencedPropertyName, input.getPath()); 099 throw new SequencerException(msg, e); 100 } 101 progressMonitor.worked(10); 102 103 // Get the binary property with the image content, and build the image metadata from the image ... 104 SequencerOutputMap output = new SequencerOutputMap(execContext.getValueFactories()); 105 InputStream stream = null; 106 Throwable firstError = null; 107 ProgressMonitor sequencingMonitor = progressMonitor.createSubtask(50); 108 try { 109 stream = sequencedProperty.getStream(); 110 SequencerNodeContext sequencerContext = new SequencerNodeContext(input, sequencedProperty, execContext); 111 this.streamSequencer.sequence(stream, output, sequencerContext, sequencingMonitor); 112 } catch (Throwable t) { 113 // Record the error ... 114 firstError = t; 115 } finally { 116 sequencingMonitor.done(); 117 if (stream != null) { 118 // Always close the stream, recording the error if we've not yet seen an error 119 try { 120 stream.close(); 121 } catch (Throwable t) { 122 if (firstError == null) firstError = t; 123 } finally { 124 stream = null; 125 } 126 } 127 if (firstError != null) { 128 // Wrap and throw the first error that we saw ... 129 throw new SequencerException(firstError); 130 } 131 } 132 133 // Find each output node and save the image metadata there ... 134 ProgressMonitor writingProgress = progressMonitor.createSubtask(40); 135 writingProgress.beginTask(outputPaths.size(), 136 RepositoryI18n.writingOutputSequencedFromPropertyOnNodes, 137 sequencedPropertyName, 138 input.getPath(), 139 outputPaths.size()); 140 for (RepositoryNodePath outputPath : outputPaths) { 141 Session session = null; 142 try { 143 // Get the name of the repository workspace and the path to the output node 144 final String repositoryWorkspaceName = outputPath.getRepositoryWorkspaceName(); 145 final String nodePath = outputPath.getNodePath(); 146 147 // Create a session to the repository where the data should be written ... 148 session = execContext.getSessionFactory().createSession(repositoryWorkspaceName); 149 150 // Find or create the output node in this session ... 151 Node outputNode = execContext.getTools().findOrCreateNode(session, nodePath); 152 153 // Now save the image metadata to the output node ... 154 if (saveOutput(outputNode, output, execContext)) { 155 session.save(); 156 } 157 } finally { 158 writingProgress.worked(1); 159 // Always close the session ... 160 if (session != null) session.logout(); 161 } 162 } 163 writingProgress.done(); 164 } finally { 165 progressMonitor.done(); 166 } 167 } 168 169 /** 170 * Save the sequencing output to the supplied node. This method does not need to save the output, as that is done by the 171 * caller of this method. 172 * 173 * @param outputNode the existing node onto (or below) which the output is to be written; never null 174 * @param output the (immutable) sequencing output; never null 175 * @param context the execution context for this sequencing operation; never null 176 * @return true if the output was written to the node, or false if no information was written 177 * @throws RepositoryException 178 */ 179 protected boolean saveOutput( Node outputNode, 180 SequencerOutputMap output, 181 JcrExecutionContext context ) throws RepositoryException { 182 if (output.isEmpty()) return false; 183 final PathFactory pathFactory = context.getValueFactories().getPathFactory(); 184 final NamespaceRegistry namespaceRegistry = context.getNamespaceRegistry(); 185 final Path outputNodePath = pathFactory.create(outputNode.getPath()); 186 final Name jcrPrimaryTypePropertyName = context.getValueFactories().getNameFactory().create("jcr:primaryType"); 187 188 // Iterate over the entries in the output, in Path's natural order (shorter paths first and in lexicographical order by 189 // prefix and name) 190 for (SequencerOutputMap.Entry entry : output) { 191 Path targetNodePath = entry.getPath(); 192 Name primaryType = entry.getPrimaryTypeValue(); 193 194 // Resolve this path relative to the output node path, handling any parent or self references ... 195 Path absolutePath = targetNodePath.isAbsolute() ? targetNodePath : outputNodePath.resolve(targetNodePath); 196 Path relativePath = absolutePath.relativeTo(outputNodePath); 197 198 // Find or add the node (which may involve adding intermediate nodes) ... 199 Node targetNode = outputNode; 200 for (int i = 0, max = relativePath.size(); i != max; ++i) { 201 Path.Segment segment = relativePath.getSegment(i); 202 String qualifiedName = segment.getString(namespaceRegistry); 203 if (targetNode.hasNode(qualifiedName)) { 204 targetNode = targetNode.getNode(qualifiedName); 205 } else { 206 // It doesn't exist, so create it ... 207 if (segment.hasIndex()) { 208 // Use a name without an index ... 209 qualifiedName = segment.getName().getString(namespaceRegistry); 210 } 211 // We only have the primary type for the final one ... 212 if (i == (max - 1) && primaryType != null) { 213 targetNode = targetNode.addNode(qualifiedName, primaryType.getString(namespaceRegistry, 214 Path.NO_OP_ENCODER)); 215 } else { 216 targetNode = targetNode.addNode(qualifiedName); 217 } 218 } 219 assert targetNode != null; 220 } 221 assert targetNode != null; 222 223 // Set all of the properties on this 224 for (SequencerOutputMap.PropertyValue property : entry.getPropertyValues()) { 225 String propertyName = property.getName().getString(namespaceRegistry, Path.NO_OP_ENCODER); 226 Object value = property.getValue(); 227 if (jcrPrimaryTypePropertyName.equals(property.getName())) { 228 // Skip the primary type property (which is protected in Jackrabbit 1.5) 229 Logger.getLogger(this.getClass()).trace("Skipping property {0}/{1}={2}", 230 targetNode.getPath(), 231 propertyName, 232 value); 233 continue; 234 } 235 Logger.getLogger(this.getClass()).trace("Writing property {0}/{1}={2}", targetNode.getPath(), propertyName, value); 236 if (value instanceof Boolean) { 237 targetNode.setProperty(propertyName, ((Boolean)value).booleanValue()); 238 } else if (value instanceof String) { 239 targetNode.setProperty(propertyName, (String)value); 240 } else if (value instanceof String[]) { 241 targetNode.setProperty(propertyName, (String[])value); 242 } else if (value instanceof Integer) { 243 targetNode.setProperty(propertyName, ((Integer)value).intValue()); 244 } else if (value instanceof Short) { 245 targetNode.setProperty(propertyName, ((Short)value).shortValue()); 246 } else if (value instanceof Long) { 247 targetNode.setProperty(propertyName, ((Long)value).longValue()); 248 } else if (value instanceof Float) { 249 targetNode.setProperty(propertyName, ((Float)value).floatValue()); 250 } else if (value instanceof Double) { 251 targetNode.setProperty(propertyName, ((Double)value).doubleValue()); 252 } else if (value instanceof Binary) { 253 Binary binaryValue = (Binary)value; 254 try { 255 binaryValue.acquire(); 256 targetNode.setProperty(propertyName, binaryValue.getStream()); 257 } finally { 258 binaryValue.release(); 259 } 260 } else if (value instanceof BigDecimal) { 261 targetNode.setProperty(propertyName, ((BigDecimal)value).doubleValue()); 262 } else if (value instanceof DateTime) { 263 targetNode.setProperty(propertyName, ((DateTime)value).toCalendar()); 264 } else if (value instanceof Date) { 265 DateTime instant = context.getValueFactories().getDateFactory().create((Date)value); 266 targetNode.setProperty(propertyName, instant.toCalendar()); 267 } else if (value instanceof Calendar) { 268 targetNode.setProperty(propertyName, (Calendar)value); 269 } else if (value instanceof Name) { 270 Name nameValue = (Name)value; 271 String stringValue = nameValue.getString(namespaceRegistry); 272 targetNode.setProperty(propertyName, stringValue); 273 } else if (value instanceof Path) { 274 // Find the path to reference node ... 275 Path pathToReferencedNode = (Path)value; 276 if (!pathToReferencedNode.isAbsolute()) { 277 // Resolve the path relative to the output node ... 278 pathToReferencedNode = outputNodePath.resolve(pathToReferencedNode); 279 } 280 // Find the referenced node ... 281 try { 282 Node referencedNode = outputNode.getNode(pathToReferencedNode.getString()); 283 targetNode.setProperty(propertyName, referencedNode); 284 } catch (PathNotFoundException e) { 285 String msg = RepositoryI18n.errorGettingNodeRelativeToNode.text(value, outputNode.getPath()); 286 throw new SequencerException(msg, e); 287 } 288 } else if (value == null) { 289 // Remove the property ... 290 targetNode.setProperty(propertyName, (String)null); 291 } else { 292 String msg = RepositoryI18n.unknownPropertyValueType.text(value, value.getClass().getName()); 293 throw new SequencerException(msg); 294 } 295 } 296 } 297 298 return true; 299 } 300 301 protected String[] extractMixinTypes( Object value ) { 302 if (value instanceof String[]) return (String[])value; 303 if (value instanceof String) return new String[] {(String)value}; 304 return null; 305 } 306 307 }