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 }