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.svn;
025
026 import java.io.ByteArrayInputStream;
027 import java.io.ByteArrayOutputStream;
028 import java.io.OutputStream;
029 import java.util.Collection;
030 import java.util.Collections;
031 import java.util.Date;
032 import java.util.HashSet;
033 import java.util.Set;
034 import org.jboss.dna.common.i18n.I18n;
035 import org.jboss.dna.common.util.Logger;
036 import org.jboss.dna.connector.scm.ScmAction;
037 import org.jboss.dna.connector.scm.ScmActionFactory;
038 import org.jboss.dna.graph.ExecutionContext;
039 import org.jboss.dna.graph.JcrLexicon;
040 import org.jboss.dna.graph.JcrNtLexicon;
041 import org.jboss.dna.graph.Location;
042 import org.jboss.dna.graph.connector.RepositorySourceException;
043 import org.jboss.dna.graph.property.Binary;
044 import org.jboss.dna.graph.property.DateTimeFactory;
045 import org.jboss.dna.graph.property.Name;
046 import org.jboss.dna.graph.property.NameFactory;
047 import org.jboss.dna.graph.property.Path;
048 import org.jboss.dna.graph.property.PathFactory;
049 import org.jboss.dna.graph.property.PathNotFoundException;
050 import org.jboss.dna.graph.property.Property;
051 import org.jboss.dna.graph.property.PropertyFactory;
052 import org.jboss.dna.graph.property.ValueFactory;
053 import org.jboss.dna.graph.request.CloneWorkspaceRequest;
054 import org.jboss.dna.graph.request.CopyBranchRequest;
055 import org.jboss.dna.graph.request.CreateNodeRequest;
056 import org.jboss.dna.graph.request.CreateWorkspaceRequest;
057 import org.jboss.dna.graph.request.DeleteBranchRequest;
058 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
059 import org.jboss.dna.graph.request.GetWorkspacesRequest;
060 import org.jboss.dna.graph.request.InvalidRequestException;
061 import org.jboss.dna.graph.request.MoveBranchRequest;
062 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
063 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
064 import org.jboss.dna.graph.request.RenameNodeRequest;
065 import org.jboss.dna.graph.request.Request;
066 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
067 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
068 import org.jboss.dna.graph.request.processor.RequestProcessor;
069 import org.tmatesoft.svn.core.SVNDirEntry;
070 import org.tmatesoft.svn.core.SVNErrorCode;
071 import org.tmatesoft.svn.core.SVNErrorMessage;
072 import org.tmatesoft.svn.core.SVNException;
073 import org.tmatesoft.svn.core.SVNNodeKind;
074 import org.tmatesoft.svn.core.SVNProperties;
075 import org.tmatesoft.svn.core.SVNProperty;
076 import org.tmatesoft.svn.core.io.ISVNEditor;
077 import org.tmatesoft.svn.core.io.SVNRepository;
078 import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
079
080 /**
081 * The {@link RequestProcessor} implementation for the file subversion repository connector. This is the class that does the bulk
082 * of the work in the subversion repository connector, since it processes all requests.
083 *
084 * @author Serge Emmanuel Pagop
085 */
086 public class SVNRepositoryRequestProcessor extends RequestProcessor implements ScmActionFactory {
087
088 protected static final String BACK_SLASH = "/";
089
090 private final String defaultNamespaceUri;
091 private final boolean updatesAllowed;
092 private SVNRepository repository;
093 protected final Logger logger;
094
095 /**
096 * @param sourceName
097 * @param context
098 * @param repository
099 * @param updatesAllowed true if this connector supports updating the subversion repository, or false if the connector is read
100 * only
101 */
102 protected SVNRepositoryRequestProcessor( String sourceName,
103 ExecutionContext context,
104 SVNRepository repository,
105 boolean updatesAllowed ) {
106 super(sourceName, context);
107 this.defaultNamespaceUri = getExecutionContext().getNamespaceRegistry().getDefaultNamespaceUri();
108 this.updatesAllowed = updatesAllowed;
109 this.repository = repository;
110 this.logger = getExecutionContext().getLogger(getClass());
111 }
112
113 /**
114 * {@inheritDoc}
115 *
116 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
117 */
118 @Override
119 public void process( CopyBranchRequest request ) {
120 logger.trace(request.toString());
121 verifyUpdatesAllowed();
122
123 }
124
125 /**
126 * {@inheritDoc}
127 *
128 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
129 */
130 @Override
131 public void process( CreateNodeRequest request ) {
132 logger.trace(request.toString());
133 verifyUpdatesAllowed();
134 // get the parent location of the new node
135 Location myLocation = request.under();
136 Path parent = getPathFor(myLocation, request);
137 try {
138 String root = parent.getString(getExecutionContext().getNamespaceRegistry());
139 SVNNodeKind rootKind = repository.checkPath(root, -1);
140 if (rootKind == SVNNodeKind.UNKNOWN) {
141 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
142 "path with name '{0}' is unknown in the repository",
143 root);
144 SVNException ex = new SVNException(err);
145 request.setError(ex);
146 } else if (rootKind == SVNNodeKind.NONE) {
147 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
148 "path with name '{0}' is missing in the repository",
149 root);
150 SVNException ex = new SVNException(err);
151 request.setError(ex);
152 } else if (rootKind == SVNNodeKind.FILE) {
153 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
154 "pretended root item with name '{0}' is a file",
155 root);
156 SVNException ex = new SVNException(err);
157 request.setError(ex);
158 } else if (rootKind == SVNNodeKind.DIR) {
159 Collection<Property> childNodeProperties = request.properties();
160 Object[] objs = values(childNodeProperties);
161 for (Object object : objs) {
162 if (object instanceof Name && ((Name)object).compareTo(JcrNtLexicon.FOLDER) == 0) {
163 // process folder creation
164 // if the node is a directory
165 String folderName = request.named().getString(getExecutionContext().getNamespaceRegistry());
166 if (root.length() == 1 && root.charAt(0) == '/') {
167 // test if so a directory does not exist.
168 mkdir("", folderName, request.toString());
169 } else {
170 if (root.length() > 1 && root.charAt(0) == '/') {
171 // test if so a directory does not exist.
172 mkdir(root.substring(1), folderName, request.toString());
173 }
174 }
175 } else if (object instanceof Name && ((Name)object).compareTo(JcrNtLexicon.FILE) == 0) {
176 String fileName = request.named().getString(getExecutionContext().getNamespaceRegistry());
177 byte[] content = getContent(objs);
178 // TODO: what is with the created on
179 // Date createdOn = getCreatedOn(objs);
180 // commit in to the repository
181 newFile(root, fileName, content, request.toString());
182 }
183 }
184 }
185
186 } catch (SVNException e) {
187 request.setError(e);
188 }
189 }
190
191 /**
192 * {@inheritDoc}
193 *
194 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
195 */
196 @Override
197 public void process( DeleteBranchRequest request ) {
198 logger.trace(request.toString());
199 verifyUpdatesAllowed();
200 }
201
202 /**
203 * {@inheritDoc}
204 *
205 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
206 */
207 @Override
208 public void process( MoveBranchRequest request ) {
209 logger.trace(request.toString());
210 verifyUpdatesAllowed();
211 }
212
213 /**
214 * {@inheritDoc}
215 *
216 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
217 */
218 @SuppressWarnings( "unchecked" )
219 @Override
220 public void process( ReadAllChildrenRequest request ) {
221 logger.trace(request.toString());
222 Location myLocation = request.of();
223 Path nodePath = getPathFor(myLocation, request);
224 try {
225 SVNNodeKind kind = validateNodeKind(nodePath);
226 String requestedNodePath = nodePath.getString(getExecutionContext().getNamespaceRegistry());
227 if (kind == SVNNodeKind.FILE) { // the requested node is a file.
228 SVNDirEntry entry = getEntryInfo(requestedNodePath);
229 if (!nodePath.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
230 String localName = entry.getName();
231 Name childName = nameFactory().create(defaultNamespaceUri, localName);
232 String url = entry.getURL().toString();
233 Property idProperty = propertyFactory().create(childName, url);
234 request.addChild(Location.create(pathFactory().create(nodePath, JcrLexicon.CONTENT), idProperty));
235 }
236 } else if (kind == SVNNodeKind.DIR) { // the requested node is a directory.
237 final Collection<SVNDirEntry> dirEntries = getRepository().getDir(requestedNodePath,
238 -1,
239 null,
240 (Collection<SVNDirEntry>)null);
241 for (SVNDirEntry dirEntry : dirEntries) {
242 if (dirEntry.getKind() == SVNNodeKind.FILE) {
243 String localName = dirEntry.getName();
244 Path newPath = pathFactory().create(requestedNodePath + BACK_SLASH + localName);
245 if (!newPath.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
246 Name childName = nameFactory().create(defaultNamespaceUri, localName);
247 String url = dirEntry.getURL().toString();
248 Property idProperty = propertyFactory().create(childName, url);
249 Location location = Location.create(pathFactory().create(newPath, JcrLexicon.CONTENT), idProperty);
250 request.addChild(location);
251 }
252 } else if (dirEntry.getKind() == SVNNodeKind.DIR) {
253 String localName = dirEntry.getName();
254 Name childName = nameFactory().create(defaultNamespaceUri, localName);
255 Path childPath = pathFactory().create(nodePath, childName);
256 String url = dirEntry.getURL().toString();
257 Property idProperty = propertyFactory().create(childName, url);
258 request.addChild(childPath, idProperty);
259 }
260 }
261 }
262 request.setActualLocationOfNode(myLocation);
263 } catch (SVNException e) {
264 request.setError(e);
265 }
266
267 }
268
269 /**
270 * {@inheritDoc}
271 *
272 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
273 */
274 @Override
275 public void process( ReadAllPropertiesRequest request ) {
276 logger.trace(request.toString());
277 Location myLocation = request.at();
278 Path nodePath = getPathFor(myLocation, request);
279 if (nodePath.isRoot()) {
280 // There are no properties on the root ...
281 request.setActualLocationOfNode(myLocation);
282 return;
283 }
284 try {
285 // See if the path is a "jcr:content" node ...
286 if (nodePath.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
287 // //"jcr:primaryType" property value of "nt:resource",
288 // "jcr:data" property whose value are the contents of the file
289 // and a few other properties, like "jcr:encoding", "jcr:mimeType" and "jcr:lastModified" and
290 // also "jcr:created" property
291 Path parent = nodePath.getParent();
292 ByteArrayOutputStream os = new ByteArrayOutputStream();
293 SVNProperties fileProperties = new SVNProperties();
294 getData(parent.getString(getExecutionContext().getNamespaceRegistry()), fileProperties, os);
295 Property ntResourceproperty = propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.RESOURCE);
296 request.addProperty(ntResourceproperty);
297 String mimeType = fileProperties.getStringValue(SVNProperty.MIME_TYPE);
298 if (mimeType != null) {
299 Property jcrMimeTypeProperty = propertyFactory().create(JcrLexicon.MIMETYPE, mimeType);
300 request.addProperty(jcrMimeTypeProperty);
301 }
302 SVNDirEntry entry = getEntryInfo(parent.getString(getExecutionContext().getNamespaceRegistry()));
303 Date lastModified = entry.getDate();
304 if (lastModified != null) {
305 Property jcrLastModifiedProperty = propertyFactory().create(JcrLexicon.LAST_MODIFIED,
306 dateFactory().create(lastModified));
307 request.addProperty(jcrLastModifiedProperty);
308 }
309 if (os.toByteArray().length > 0) {
310 Property jcrDataProperty = propertyFactory().create(JcrLexicon.DATA, binaryFactory().create(os.toByteArray()));
311 request.addProperty(jcrDataProperty);
312 }
313 } else {
314 SVNNodeKind kind = validateNodeKind(nodePath);
315 if (kind == SVNNodeKind.FILE) {
316 // "jcr:primaryType" property whose value is "nt:file".
317 Property ntFileProperty = propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE);
318 request.addProperty(ntFileProperty);
319 ByteArrayOutputStream os = new ByteArrayOutputStream();
320 SVNProperties fileProperties = new SVNProperties();
321 getData(nodePath.getString(getExecutionContext().getNamespaceRegistry()), fileProperties, os);
322 String created = fileProperties.getStringValue(SVNProperty.COMMITTED_DATE);
323 if (created != null) {
324 Property jcrCreatedProperty = propertyFactory().create(JcrLexicon.CREATED, created);
325 request.addProperty(jcrCreatedProperty);
326 }
327
328 } else if (kind == SVNNodeKind.DIR) {
329 // A directory maps to a single node with a name that represents the name of the directory and a
330 // "jcr:primaryType" property whose value is "nt:folder"
331 Property jcrPrimaryTypeProp = propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER);
332 request.addProperty(jcrPrimaryTypeProp);
333 SVNDirEntry dirEntry = getEntryInfo(nodePath.getString(getExecutionContext().getNamespaceRegistry()));
334 Property jcrCreatedProp = propertyFactory().create(JcrLexicon.CREATED,
335 dateFactory().create(dirEntry.getDate()));
336 request.addProperty(jcrCreatedProp);
337 }
338 }
339 request.setActualLocationOfNode(myLocation);
340
341 } catch (SVNException e) {
342 request.setError(e);
343 }
344 }
345
346 /**
347 * {@inheritDoc}
348 *
349 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
350 */
351 @Override
352 public void process( RenameNodeRequest request ) {
353 logger.trace(request.toString());
354 verifyUpdatesAllowed();
355 super.process(request);
356 }
357
358 /**
359 * {@inheritDoc}
360 *
361 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
362 */
363 @Override
364 public void process( UpdatePropertiesRequest request ) {
365 logger.trace(request.toString());
366 verifyUpdatesAllowed();
367 }
368
369 /**
370 * {@inheritDoc}
371 *
372 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
373 */
374 @Override
375 public void process( VerifyWorkspaceRequest request ) {
376 // This does the job of converting a null workspace name to a valid workspace
377 String workspaceName = request.workspaceName();
378 if (workspaceName == null) workspaceName = "default";
379 request.setActualRootLocation(Location.create(pathFactory().createRootPath()));
380 request.setActualWorkspaceName(workspaceName);
381 }
382
383 /**
384 * {@inheritDoc}
385 *
386 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
387 */
388 @Override
389 public void process( GetWorkspacesRequest request ) {
390 request.setAvailableWorkspaceNames(Collections.singleton("default"));
391 }
392
393 /**
394 * {@inheritDoc}
395 *
396 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
397 */
398 @Override
399 public void process( CreateWorkspaceRequest request ) {
400 String msg = SVNRepositoryConnectorI18n.sourceDoesNotSupportCreatingWorkspaces.text(getSourceName());
401 request.setError(new InvalidRequestException(msg));
402 }
403
404 /**
405 * {@inheritDoc}
406 *
407 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
408 */
409 @Override
410 public void process( CloneWorkspaceRequest request ) {
411 String msg = SVNRepositoryConnectorI18n.sourceDoesNotSupportCloningWorkspaces.text(getSourceName());
412 request.setError(new InvalidRequestException(msg));
413 }
414
415 /**
416 * {@inheritDoc}
417 *
418 * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
419 */
420 @Override
421 public void process( DestroyWorkspaceRequest request ) {
422 String msg = SVNRepositoryConnectorI18n.sourceDoesNotSupportDeletingWorkspaces.text(getSourceName());
423 request.setError(new InvalidRequestException(msg));
424 }
425
426 /**
427 * Verify if change is allowed on a specific source.
428 *
429 * @throws RepositorySourceException if change on that repository source is not allowed.
430 */
431 protected void verifyUpdatesAllowed() {
432 if (!updatesAllowed) {
433 throw new InvalidRequestException(SVNRepositoryConnectorI18n.sourceIsReadOnly.text(getSourceName()));
434 }
435 }
436
437 /**
438 * Factory for sample name.
439 *
440 * @return the name factory
441 */
442 protected NameFactory nameFactory() {
443 return getExecutionContext().getValueFactories().getNameFactory();
444 }
445
446 /**
447 * Factory for path creation.
448 *
449 * @return a path factory.
450 */
451 protected PathFactory pathFactory() {
452 return getExecutionContext().getValueFactories().getPathFactory();
453 }
454
455 /**
456 * Factory for property creation.
457 *
458 * @return the property factory.
459 */
460 protected PropertyFactory propertyFactory() {
461 return getExecutionContext().getPropertyFactory();
462 }
463
464 /**
465 * Factory for date creation.
466 *
467 * @return the date factory.
468 */
469 protected DateTimeFactory dateFactory() {
470 return getExecutionContext().getValueFactories().getDateFactory();
471 }
472
473 /**
474 * Factory for binary creation.
475 *
476 * @return the binary factory..
477 */
478 protected ValueFactory<Binary> binaryFactory() {
479 return getExecutionContext().getValueFactories().getBinaryFactory();
480 }
481
482 /**
483 * Get the path for a locarion and check if the path is null or not.
484 *
485 * @param location - the location.
486 * @param request - the requested path.
487 * @return the path.
488 * @throws RepositorySourceException if the path of a location is null.
489 */
490 protected Path getPathFor( Location location,
491 Request request ) {
492 Path path = location.getPath();
493 if (path == null) {
494 I18n msg = SVNRepositoryConnectorI18n.locationInRequestMustHavePath;
495 throw new RepositorySourceException(getSourceName(), msg.text(getSourceName(), request));
496 }
497 return path;
498 }
499
500 /**
501 * Get the content of a file.
502 *
503 * @param path - the path to that file.
504 * @param properties - the properties of the file.
505 * @param os - the output stream where to store the content.
506 * @throws SVNException - throws if such path is not at that revision or in case of a connection problem.
507 */
508 protected void getData( String path,
509 SVNProperties properties,
510 OutputStream os ) throws SVNException {
511 getRepository().getFile(path, -1, properties, os);
512
513 }
514
515 /**
516 * Get the repository driver.
517 *
518 * @return repository
519 */
520 public SVNRepository getRepository() {
521 return repository;
522 }
523
524 /**
525 * Validate the kind of node and throws an exception if necessary.
526 *
527 * @param requestedPath
528 * @return the kind.
529 */
530 protected SVNNodeKind validateNodeKind( final Path requestedPath ) {
531 String myPath = requestedPath.getString(getExecutionContext().getNamespaceRegistry());
532 SVNNodeKind kind = null;
533 try {
534 kind = getRepository().checkPath(myPath, -1);
535 if (kind == SVNNodeKind.NONE) {
536 // node does not exist or requested node is not correct.
537 throw new PathNotFoundException(Location.create(requestedPath), null,
538 SVNRepositoryConnectorI18n.nodeDoesNotExist.text(myPath));
539 } else if (kind == SVNNodeKind.UNKNOWN) {
540 // node is unknown
541 throw new PathNotFoundException(Location.create(requestedPath), null,
542 SVNRepositoryConnectorI18n.nodeIsActuallyUnknow.text(myPath));
543 }
544 } catch (SVNException e) {
545 throw new RepositorySourceException(
546 getSourceName(),
547 SVNRepositoryConnectorI18n.connectingFailureOrUserAuthenticationProblem.text(getSourceName()));
548 }
549
550 return kind;
551 }
552
553 /**
554 * Get some important informations of a path
555 *
556 * @param path - the path
557 * @return - the {@link SVNDirEntry}.
558 */
559 protected SVNDirEntry getEntryInfo( String path ) {
560 assert path != null;
561 SVNDirEntry entry = null;
562 try {
563 entry = getRepository().info(path, -1);
564 } catch (SVNException e) {
565 throw new RepositorySourceException(
566 getSourceName(),
567 SVNRepositoryConnectorI18n.connectingFailureOrUserAuthenticationProblem.text(getSourceName()));
568 }
569 return entry;
570 }
571
572 /**
573 * Open the directories where change has to be made.
574 *
575 * @param editor - abstract editor.
576 * @param rootPath - the pa to open.
577 * @throws SVNException when a error occur.
578 */
579 protected static void openDirectories( ISVNEditor editor,
580 String rootPath ) throws SVNException {
581 assert rootPath != null;
582 int pos = rootPath.indexOf('/', 0);
583 while (pos != -1) {
584 String dir = rootPath.substring(0, pos);
585 editor.openDir(dir, -1);
586 pos = rootPath.indexOf('/', pos + 1);
587 }
588 String dir = rootPath.substring(0, rootPath.length());
589 editor.openDir(dir, -1);
590 }
591
592 /**
593 * Close the directories where change was made.
594 *
595 * @param editor - the abstract editor.
596 * @param path - the directories to open.
597 * @throws SVNException when a error occur.
598 */
599 protected static void closeDirectories( ISVNEditor editor,
600 String path ) throws SVNException {
601 int length = path.length() - 1;
602 int pos = path.lastIndexOf('/', length);
603 editor.closeDir();
604 while (pos != -1) {
605 editor.closeDir();
606 pos = path.lastIndexOf('/', pos - 1);
607 }
608 }
609
610 /**
611 * Get the last revision.
612 *
613 * @return the last revision number.
614 * @throws Exception
615 */
616 public long getLatestRevision() throws Exception {
617 try {
618 return repository.getLatestRevision();
619 } catch (SVNException e) {
620 e.printStackTrace();
621 // logger.error( "svn error: " );
622 throw e;
623 }
624 }
625
626 /**
627 * Add directory in a repository
628 *
629 * @param repository - the repository.
630 * @param root - the root path has to exist.
631 * @param child - new path to be added.
632 * @param message - information about the change action.
633 * @throws SVNException when a error occur.
634 */
635 protected void addDirEntry( SVNRepository repository,
636 String root,
637 String child,
638 String message ) throws SVNException {
639 assert root.trim().length() != 0;
640 SVNNodeKind rootKind = repository.checkPath(root, -1);
641 if (rootKind == SVNNodeKind.UNKNOWN) {
642 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
643 "path with name '{0}' is unknown in the repository",
644 root);
645 throw new SVNException(err);
646 } else if (rootKind == SVNNodeKind.NONE) {
647 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
648 "path with name '{0}' is missing in the repository",
649 root);
650 throw new SVNException(err);
651 } else if (rootKind == SVNNodeKind.FILE) {
652 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
653 "path with name '{0}' is a file, you need a directory",
654 root);
655 throw new SVNException(err);
656 } else if (rootKind == SVNNodeKind.DIR) {
657 ISVNEditor editor = repository.getCommitEditor(message, null, true, null);
658 if (root.length() == 1 && root.charAt(0) == '/') {
659 addProcess(editor, root, "", child);
660 } else {
661 String rootPath = root.substring(1);
662 addProcess(editor, rootPath, null, child);
663 }
664 }
665 }
666
667 private void addProcess( ISVNEditor editor,
668 String rootPath,
669 String editedRoot,
670 String childSegmentName ) throws SVNException {
671 openDirectories(editor, editedRoot);
672 // test if so a directory does not exist.
673 SVNNodeKind childKind = repository.checkPath(childSegmentName, -1);
674 if (childKind == SVNNodeKind.NONE) {
675 editor.addDir(childSegmentName, null, -1);
676 closeDirectories(editor, childSegmentName);
677 if (editedRoot != null) {
678 closeDirectories(editor, editedRoot);
679 } else {
680 closeDirectories(editor, rootPath);
681 }
682
683 } else {
684 closeDirectories(editor, childSegmentName);
685 if (editedRoot != null) {
686 closeDirectories(editor, editedRoot);
687 } else {
688 closeDirectories(editor, rootPath);
689 }
690 }
691 }
692
693 /**
694 * Create a directory .
695 *
696 * @param root - the root directory where the created directory will reside
697 * @param childName - the name of the created directory.
698 * @param message - comment for the creation.
699 * @throws SVNException - if during the creation, there is an error.
700 */
701 private void mkdir( String root,
702 String childName,
703 String message ) throws SVNException {
704 SVNNodeKind childKind = repository.checkPath(childName, -1);
705 if (childKind == SVNNodeKind.NONE) {
706 ScmAction addNodeAction = addDirectory(root, childName);
707 SVNActionExecutor executor = new SVNActionExecutor(repository);
708 executor.execute(addNodeAction, message);
709 } else {
710 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Item with name '{0}' can't be created", childName);
711 throw new SVNException(err);
712 }
713 }
714
715 /**
716 * Create a file.
717 *
718 * @param path
719 * @param file
720 * @param content
721 * @param message
722 * @throws SVNException
723 */
724 private void newFile( String path,
725 String file,
726 byte[] content,
727 String message ) throws SVNException {
728 SVNNodeKind childKind = repository.checkPath(file, -1);
729 if (childKind == SVNNodeKind.NONE) {
730 ScmAction addFileNodeAction = addFile(path, file, content);
731 SVNActionExecutor executor = new SVNActionExecutor(repository);
732 executor.execute(addFileNodeAction, message);
733 } else {
734 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
735 "Item with name '{0}' can't be created (already exist)",
736 file);
737 throw new SVNException(err);
738 }
739 }
740
741 /**
742 * {@inheritDoc}
743 *
744 * @see org.jboss.dna.connector.scm.ScmActionFactory#addDirectory(java.lang.String, java.lang.String)
745 */
746 public ScmAction addDirectory( String root,
747 String path ) {
748 return new AddDirectory(root, path);
749 }
750
751 /**
752 * {@inheritDoc}
753 *
754 * @see org.jboss.dna.connector.scm.ScmActionFactory#addFile(java.lang.String, java.lang.String, byte[])
755 */
756 public ScmAction addFile( String path,
757 String file,
758 byte[] content ) {
759 return new AddFile(path, file, content);
760 }
761
762 /**
763 * {@inheritDoc}
764 *
765 * @see org.jboss.dna.connector.scm.ScmActionFactory#copyDirectory(java.lang.String, java.lang.String, long)
766 */
767 public ScmAction copyDirectory( String path,
768 String newPath,
769 long revision ) {
770 return null;
771 }
772
773 /**
774 * {@inheritDoc}
775 *
776 * @see org.jboss.dna.connector.scm.ScmActionFactory#deleteDirectory(java.lang.String)
777 */
778 public ScmAction deleteDirectory( String path ) {
779 return null;
780 }
781
782 /**
783 * {@inheritDoc}
784 *
785 * @see org.jboss.dna.connector.scm.ScmActionFactory#deleteFile(java.lang.String, java.lang.String)
786 */
787 public ScmAction deleteFile( String path,
788 String file ) {
789 return null;
790 }
791
792 /**
793 * root should be the last, previously created, parent folder. Each directory in the path will be created.
794 */
795 public static class AddDirectory implements ScmAction {
796 private String root;
797 private String path;
798
799 public AddDirectory( String root,
800 String path ) {
801 this.root = root;
802 this.path = path;
803 }
804
805 public void applyAction( Object context ) throws SVNException {
806
807 ISVNEditor editor = (ISVNEditor)context;
808
809 openDirectories(editor, this.root);
810 String[] paths = this.path.split("/");
811 String newPath = this.root;
812 for (int i = 0, length = paths.length; i < length; i++) {
813 newPath = (newPath.length() != 0) ? newPath + "/" + paths[i] : paths[i];
814
815 editor.addDir(newPath, null, -1);
816 }
817
818 closeDirectories(editor, path);
819 closeDirectories(editor, this.root);
820 }
821 }
822
823 public static class AddFile implements ScmAction {
824 private String path;
825 private String file;
826 private byte[] content;
827
828 public AddFile( String path,
829 String file,
830 byte[] content ) {
831 this.path = path;
832 this.file = file;
833 this.content = content;
834 }
835
836 public void applyAction( Object context ) throws Exception {
837 ISVNEditor editor = (ISVNEditor)context;
838 openDirectories(editor, path);
839
840 editor.addFile(path + "/" + file, null, -1);
841 editor.applyTextDelta(path + "/" + file, null);
842 SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
843 String checksum = deltaGenerator.sendDelta(path + "/" + file, new ByteArrayInputStream(this.content), editor, true);
844 editor.closeFile(path + "/" + file, checksum);
845
846 closeDirectories(editor, path);
847
848 }
849
850 }
851
852 // private Date getCreatedOn( Object[] objs ) {
853 // Date createdOn = null;
854 // for (Object object : objs) {
855 // if (object instanceof Date) {
856 // createdOn = (Date)object;
857 //
858 // }
859 // }
860 // return createdOn;
861 // }
862
863 private byte[] getContent( Object[] objs ) {
864 byte[] content = null;
865 for (Object object : objs) {
866 if (object != null && object instanceof Binary) {
867 Binary buf = (Binary)object;
868 content = buf.getBytes();
869 }
870 }
871 return content;
872 }
873
874 private Object[] values( Collection<Property> childNodeProperties ) {
875 Set<Object> result = new HashSet<Object>();
876 for (Property property : childNodeProperties) {
877 result.add(property.getFirstValue());
878 }
879 return result.toArray();
880 }
881 }