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 }