1 package org.modeshape.connector.svn;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.OutputStream;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collection;
8 import java.util.Collections;
9 import java.util.HashSet;
10 import java.util.LinkedList;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.UUID;
15 import org.modeshape.common.i18n.I18n;
16 import org.modeshape.connector.scm.ScmAction;
17 import org.modeshape.connector.svn.mgnt.AddDirectory;
18 import org.modeshape.connector.svn.mgnt.AddFile;
19 import org.modeshape.connector.svn.mgnt.DeleteEntry;
20 import org.modeshape.connector.svn.mgnt.UpdateFile;
21 import org.modeshape.graph.ExecutionContext;
22 import org.modeshape.graph.JcrLexicon;
23 import org.modeshape.graph.JcrNtLexicon;
24 import org.modeshape.graph.ModeShapeIntLexicon;
25 import org.modeshape.graph.ModeShapeLexicon;
26 import org.modeshape.graph.NodeConflictBehavior;
27 import org.modeshape.graph.connector.RepositorySourceException;
28 import org.modeshape.graph.connector.path.AbstractWritablePathWorkspace;
29 import org.modeshape.graph.connector.path.DefaultPathNode;
30 import org.modeshape.graph.connector.path.PathNode;
31 import org.modeshape.graph.connector.path.WritablePathRepository;
32 import org.modeshape.graph.connector.path.WritablePathWorkspace;
33 import org.modeshape.graph.connector.path.cache.WorkspaceCache;
34 import org.modeshape.graph.property.Binary;
35 import org.modeshape.graph.property.BinaryFactory;
36 import org.modeshape.graph.property.DateTimeFactory;
37 import org.modeshape.graph.property.Name;
38 import org.modeshape.graph.property.NameFactory;
39 import org.modeshape.graph.property.NamespaceRegistry;
40 import org.modeshape.graph.property.Path;
41 import org.modeshape.graph.property.PathFactory;
42 import org.modeshape.graph.property.Property;
43 import org.modeshape.graph.property.PropertyFactory;
44 import org.modeshape.graph.property.Path.Segment;
45 import org.modeshape.graph.request.InvalidRequestException;
46 import org.tmatesoft.svn.core.SVNDirEntry;
47 import org.tmatesoft.svn.core.SVNErrorCode;
48 import org.tmatesoft.svn.core.SVNErrorMessage;
49 import org.tmatesoft.svn.core.SVNException;
50 import org.tmatesoft.svn.core.SVNNodeKind;
51 import org.tmatesoft.svn.core.SVNProperties;
52 import org.tmatesoft.svn.core.SVNProperty;
53 import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
54 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
55 import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
56 import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
57 import org.tmatesoft.svn.core.io.SVNRepository;
58 import org.tmatesoft.svn.core.wc.SVNWCUtil;
59
60 public class SvnRepository extends WritablePathRepository {
61
62 private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
63 protected static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
64
65 protected final SvnRepositorySource source;
66
67 static {
68
69 DAVRepositoryFactory.setup();
70
71 FSRepositoryFactory.setup();
72
73 SVNRepositoryFactoryImpl.setup();
74 }
75
76 public SvnRepository( SvnRepositorySource source ) {
77 super(source);
78
79 this.source = source;
80 initialize();
81 }
82
83 @Override
84 protected void initialize() {
85 ExecutionContext context = source.getRepositoryContext().getExecutionContext();
86 for (String workspaceName : source.getPredefinedWorkspaceNames()) {
87 doCreateWorkspace(context, workspaceName);
88 }
89
90 String defaultWorkspaceName = source.getDefaultWorkspaceName();
91 if (defaultWorkspaceName != null && !workspaces.containsKey(defaultWorkspaceName)) {
92 doCreateWorkspace(context, defaultWorkspaceName);
93 }
94
95 }
96
97 public WorkspaceCache getCache( String workspaceName ) {
98 return source.getPathRepositoryCache().getCache(workspaceName);
99 }
100
101
102
103
104
105
106
107
108
109 private WritablePathWorkspace doCreateWorkspace( ExecutionContext context,
110 String name ) {
111 SvnWorkspace workspace = new SvnWorkspace(name, source.getRootNodeUuid());
112
113 workspaces.putIfAbsent(name, workspace);
114 return (WritablePathWorkspace)workspaces.get(name);
115
116 }
117
118 @Override
119 protected WritablePathWorkspace createWorkspace( ExecutionContext context,
120 String name ) {
121 if (!source.isCreatingWorkspacesAllowed()) {
122 String msg = SvnRepositoryConnectorI18n.unableToCreateWorkspaces.text(getSourceName(), name);
123 throw new InvalidRequestException(msg);
124 }
125
126 return doCreateWorkspace(context, name);
127 }
128
129 class SvnWorkspace extends AbstractWritablePathWorkspace {
130
131
132
133
134
135
136 private final Set<Name> ALLOWABLE_PROPERTIES_FOR_CONTENT = Collections.unmodifiableSet(new HashSet<Name>(
137 Arrays.asList(new Name[] {
138 JcrLexicon.PRIMARY_TYPE,
139 JcrLexicon.DATA,
140 JcrLexicon.ENCODED,
141 JcrLexicon.MIMETYPE,
142 JcrLexicon.LAST_MODIFIED,
143 JcrLexicon.UUID,
144 ModeShapeIntLexicon.NODE_DEFINITON})));
145
146
147
148
149 private final Set<Name> ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER = Collections.unmodifiableSet(new HashSet<Name>(
150 Arrays.asList(new Name[] {
151 JcrLexicon.PRIMARY_TYPE,
152 JcrLexicon.CREATED,
153 JcrLexicon.UUID,
154 ModeShapeIntLexicon.NODE_DEFINITON})));
155
156 private final SVNRepository workspaceRoot;
157
158 public SvnWorkspace( String name,
159 UUID rootNodeUuid ) {
160 super(name, rootNodeUuid);
161
162 workspaceRoot = getWorkspaceDirectory(name);
163
164 ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(source.getUsername(),
165 source.getPassword());
166 workspaceRoot.setAuthenticationManager(authManager);
167 }
168
169 public Path getLowestExistingPath( Path path ) {
170 do {
171 path = path.getParent();
172
173 if (getNode(path) != null) {
174 return path;
175 }
176 } while (path != null);
177
178 assert false : "workspace root path was not a valid path";
179 return null;
180 }
181
182 public PathNode getNode( Path path ) {
183 WorkspaceCache cache = getCache(getName());
184
185 PathNode node = cache.get(path);
186 if (node != null) return node;
187
188 ExecutionContext context = source.getRepositoryContext().getExecutionContext();
189 List<Property> properties = new LinkedList<Property>();
190 List<Segment> children = new LinkedList<Segment>();
191
192 try {
193 boolean result = readNode(context, this.getName(), path, properties, children);
194 if (!result) return null;
195 } catch (SVNException ex) {
196 return null;
197 }
198
199 UUID uuid = path.isRoot() ? source.getRootNodeUuid() : null;
200 node = new DefaultPathNode(path, uuid, properties, children);
201
202 cache.set(node);
203 return node;
204 }
205
206 public PathNode createNode( ExecutionContext context,
207 PathNode parentNode,
208 Name name,
209 Map<Name, Property> properties,
210 NodeConflictBehavior conflictBehavior ) {
211
212 NamespaceRegistry registry = context.getNamespaceRegistry();
213 NameFactory nameFactory = context.getValueFactories().getNameFactory();
214 PathFactory pathFactory = context.getValueFactories().getPathFactory();
215
216
217 String newName = name.getString(registry);
218
219 Property primaryTypeProp = properties.get(JcrLexicon.PRIMARY_TYPE);
220 Name primaryType = primaryTypeProp == null ? null : nameFactory.create(primaryTypeProp.getFirstValue());
221
222 Path parentPath = parentNode.getPath();
223 String parentPathAsString = parentPath.getString(registry);
224 Path newPath = pathFactory.create(parentPath, name);
225
226 String newChildPath = null;
227
228
229 if (JcrNtLexicon.FILE.equals(primaryType)) {
230 ensureValidProperties(context, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
231
232 boolean skipWrite = false;
233
234 if (parentPath.isRoot()) {
235 if (!source.getRepositoryRootUrl().equals(getName())) {
236 newChildPath = newName;
237 } else {
238 newChildPath = "/" + newName;
239 }
240 } else {
241 newChildPath = newPath.getString(registry);
242 if (!source.getRepositoryRootUrl().equals(getName())) {
243 newChildPath = newChildPath.substring(1);
244 }
245 }
246
247
248 try {
249 if (SvnRepositoryUtil.exists(workspaceRoot, newChildPath)) {
250 if (conflictBehavior.equals(NodeConflictBehavior.APPEND)) {
251 throw new InvalidRequestException(SvnRepositoryConnectorI18n.sameNameSiblingsAreNotAllowed.text());
252 } else if (conflictBehavior.equals(NodeConflictBehavior.DO_NOT_REPLACE)) {
253 skipWrite = true;
254 }
255 }
256 } catch (SVNException e1) {
257 throw new RepositorySourceException(getSourceName(), e1.getMessage());
258 }
259
260
261 if (!skipWrite) {
262
263 if (newChildPath != null) {
264 try {
265 String rootPath = null;
266 if (parentPath.isRoot()) {
267 rootPath = "";
268 } else {
269 rootPath = parentPathAsString;
270 }
271 newFile(rootPath, newName, EMPTY_BYTE_ARRAY, null, getName(), workspaceRoot);
272 } catch (SVNException e) {
273 I18n msg = SvnRepositoryConnectorI18n.couldNotCreateFile;
274 throw new RepositorySourceException(getSourceName(), msg.text(parentPathAsString,
275 getName(),
276 getSourceName(),
277 e.getMessage()), e);
278 }
279 }
280 }
281 } else if (JcrNtLexicon.RESOURCE.equals(primaryType) || ModeShapeLexicon.RESOURCE.equals(primaryType)) {
282 ensureValidProperties(context, properties.values(), ALLOWABLE_PROPERTIES_FOR_CONTENT);
283 if (parentPath.isRoot()) {
284 newChildPath = parentPathAsString;
285 if (!source.getRepositoryRootUrl().equals(getName())) {
286 newChildPath = parentPathAsString.substring(1);
287 }
288 } else {
289 newChildPath = parentPathAsString;
290 if (!source.getRepositoryRootUrl().equals(getName())) {
291 newChildPath = newChildPath.substring(1);
292 }
293 }
294
295 if (!JcrLexicon.CONTENT.equals(name)) {
296 I18n msg = SvnRepositoryConnectorI18n.invalidNameForResource;
297 throw new RepositorySourceException(getSourceName(), msg.text(parentPathAsString,
298 getName(),
299 getSourceName(),
300 newName));
301 }
302
303 Property parentPrimaryType = parentNode.getProperty(JcrLexicon.PRIMARY_TYPE);
304 Name parentPrimaryTypeName = parentPrimaryType == null ? null : nameFactory.create(parentPrimaryType.getFirstValue());
305 if (!JcrNtLexicon.FILE.equals(parentPrimaryTypeName)) {
306 I18n msg = SvnRepositoryConnectorI18n.invalidPathForResource;
307 throw new RepositorySourceException(getSourceName(), msg.text(parentPathAsString, getName(), getSourceName()));
308 }
309
310 boolean updateFileContent = true;
311 switch (conflictBehavior) {
312 case APPEND:
313 case REPLACE:
314 case UPDATE:
315
316
317
318
319
320 break;
321 case DO_NOT_REPLACE:
322
323 updateFileContent = false;
324 }
325
326 Property dataProperty = properties.get(JcrLexicon.DATA);
327 if (dataProperty == null) {
328 updateFileContent = false;
329 }
330 if (updateFileContent) {
331 BinaryFactory binaryFactory = context.getValueFactories().getBinaryFactory();
332 Binary binary = binaryFactory.create(properties.get(JcrLexicon.DATA).getFirstValue());
333
334 ByteArrayOutputStream contents = new ByteArrayOutputStream();
335 SVNProperties svnProperties = new SVNProperties();
336 try {
337 workspaceRoot.getFile(newChildPath, -1, svnProperties, contents);
338 byte[] oldData = contents.toByteArray();
339
340
341 if (oldData != null) {
342 String pathToFile;
343 if (parentPath.isRoot()) {
344 pathToFile = "";
345 } else {
346 pathToFile = parentPath.getParent().getString(registry);
347 }
348 String fileName = parentPath.getLastSegment().getString(registry);
349
350 modifyFile(pathToFile, fileName, oldData, binary.getBytes(), null, getName(), workspaceRoot);
351 }
352 } catch (SVNException e) {
353 I18n msg = SvnRepositoryConnectorI18n.couldNotReadData;
354 throw new RepositorySourceException(getSourceName(), msg.text(parentPathAsString,
355 getName(),
356 getSourceName(),
357 e.getMessage()), e);
358 }
359 }
360
361 } else if (JcrNtLexicon.FOLDER.equals(primaryType) || primaryType == null) {
362 ensureValidProperties(context, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
363 try {
364 mkdir(parentPathAsString, newName, null, getName(), workspaceRoot);
365 } catch (SVNException e) {
366 I18n msg = SvnRepositoryConnectorI18n.couldNotCreateFile;
367 throw new RepositorySourceException(getSourceName(), msg.text(parentPathAsString,
368 getName(),
369 getSourceName(),
370 e.getMessage()), e);
371 }
372 } else {
373 I18n msg = SvnRepositoryConnectorI18n.unsupportedPrimaryType;
374 throw new RepositorySourceException(getSourceName(), msg.text(primaryType.getString(registry),
375 parentPathAsString,
376 getName(),
377 getSourceName()));
378 }
379
380 PathNode node = getNode(newPath);
381
382 List<Segment> newChildren = new ArrayList<Segment>(parentNode.getChildSegments().size() + 1);
383 newChildren.addAll(parentNode.getChildSegments());
384 newChildren.add(node.getPath().getLastSegment());
385
386 WorkspaceCache cache = getCache(getName());
387 cache.set(new DefaultPathNode(parentNode.getPath(), parentNode.getUuid(), parentNode.getProperties(), newChildren));
388 cache.set(node);
389
390 return node;
391 }
392
393
394
395
396
397
398
399
400
401
402
403 private void mkdir( String rootDirPath,
404 String childDirPath,
405 String comment,
406 String inWorkspace,
407 SVNRepository currentRepository ) throws SVNException {
408
409 String tempParentPath = rootDirPath;
410 if (!source.getRepositoryRootUrl().equals(inWorkspace)) {
411 if (!tempParentPath.equals("/") && tempParentPath.startsWith("/")) {
412 tempParentPath = tempParentPath.substring(1);
413 } else if (tempParentPath.equals("/")) {
414 tempParentPath = "";
415 }
416 }
417 String checkPath = tempParentPath.length() == 0 ? childDirPath : tempParentPath + "/" + childDirPath;
418 SVNNodeKind nodeKind = null;
419 try {
420 nodeKind = currentRepository.checkPath(checkPath, -1);
421 } catch (SVNException e) {
422 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
423 "May be a Connecting problem to the repository or a user's authentication failure: {0}",
424 e.getMessage());
425 throw new SVNException(err);
426 }
427
428 if (nodeKind != null && nodeKind == SVNNodeKind.NONE) {
429 ScmAction addNodeAction = new AddDirectory(rootDirPath, childDirPath);
430 SvnActionExecutor executor = new SvnActionExecutor(currentRepository);
431 comment = comment == null ? "Create a new file " + childDirPath : comment;
432 executor.execute(addNodeAction, comment);
433 } else {
434 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
435 "Node with name '{0}' can't be created",
436 childDirPath);
437 throw new SVNException(err);
438 }
439 }
440
441
442
443
444
445
446
447
448
449
450
451
452 private void newFile( String rootDirPath,
453 String childFilePath,
454 byte[] content,
455 String comment,
456 String inWorkspace,
457 SVNRepository currentRepository ) throws SVNException {
458
459 String tempParentPath = rootDirPath;
460 if (!source.getRepositoryRootUrl().equals(inWorkspace)) {
461 if (!tempParentPath.equals("/") && tempParentPath.startsWith("/")) {
462 tempParentPath = tempParentPath.substring(1);
463 }
464 }
465 String checkPath = tempParentPath + "/" + childFilePath;
466 SVNNodeKind nodeKind = null;
467 try {
468 nodeKind = currentRepository.checkPath(checkPath, -1);
469 } catch (SVNException e) {
470 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
471 "May be a Connecting problem to the repository or a user's authentication failure: {0}",
472 e.getMessage());
473 throw new SVNException(err);
474 }
475
476 if (nodeKind != null && nodeKind == SVNNodeKind.NONE) {
477 ScmAction addFileNodeAction = new AddFile(rootDirPath, childFilePath, content);
478 SvnActionExecutor executor = new SvnActionExecutor(currentRepository);
479 comment = comment == null ? "Create a new file " + childFilePath : comment;
480 executor.execute(addFileNodeAction, comment);
481 } else {
482 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
483 "Item with name '{0}' can't be created (already exist)",
484 childFilePath);
485 throw new SVNException(err);
486 }
487 }
488
489
490
491
492
493
494
495
496
497
498
499
500
501 private void modifyFile( String rootPath,
502 String fileName,
503 byte[] oldData,
504 byte[] newData,
505 String comment,
506 String inWorkspace,
507 SVNRepository currentRepository ) throws SVNException {
508 assert rootPath != null;
509 assert fileName != null;
510 assert oldData != null;
511 assert inWorkspace != null;
512 assert currentRepository != null;
513
514 try {
515
516 if (!source.getRepositoryRootUrl().equals(inWorkspace)) {
517 if (rootPath.equals("/")) {
518 rootPath = "";
519 } else {
520 rootPath = rootPath.substring(1) + "/";
521 }
522 } else {
523 if (!rootPath.equals("/")) {
524 rootPath = rootPath + "/";
525 }
526 }
527 String path = rootPath + fileName;
528
529 SVNNodeKind nodeKind = currentRepository.checkPath(path, -1);
530 if (nodeKind == SVNNodeKind.NONE || nodeKind == SVNNodeKind.UNKNOWN) {
531 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND,
532 "Item with name '{0}' can't be found",
533 path);
534 throw new SVNException(err);
535 }
536
537 ScmAction modifyFileAction = new UpdateFile(rootPath, fileName, oldData, newData);
538 SvnActionExecutor executor = new SvnActionExecutor(currentRepository);
539 comment = comment == null ? "modify the " + fileName : comment;
540 executor.execute(modifyFileAction, comment);
541
542 } catch (SVNException e) {
543 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "This error is appeared: " + e.getMessage());
544 throw new SVNException(err, e);
545 }
546
547 }
548
549
550
551
552
553
554
555
556
557
558 private void eraseEntry( String path,
559 String comment,
560 String inWorkspace,
561 SVNRepository currentRepository ) throws SVNException {
562 assert path != null;
563 assert inWorkspace != null;
564 if (path.equals("/") || path.equals("")) {
565 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_URL, "The root directory cannot be deleted");
566 throw new SVNException(err);
567 }
568
569 try {
570 ScmAction deleteEntryAction = new DeleteEntry(path);
571 SvnActionExecutor executor = new SvnActionExecutor(currentRepository);
572 comment = comment == null ? "Delete the " + path : comment;
573 executor.execute(deleteEntryAction, comment);
574 } catch (SVNException e) {
575 SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN,
576 "unknow error during delete action: {0)",
577 e.getMessage());
578 throw new SVNException(err);
579 }
580 }
581
582 public boolean removeNode( ExecutionContext context,
583 Path nodePath ) {
584
585 NamespaceRegistry registry = context.getNamespaceRegistry();
586
587 boolean isContentNode = !nodePath.isRoot() && JcrLexicon.CONTENT.equals(nodePath.getLastSegment().getName());
588 Path actualPath = isContentNode ? nodePath.getParent() : nodePath;
589
590 try {
591 SVNNodeKind kind = getNodeKind(context, actualPath, source.getRepositoryRootUrl());
592
593 if (kind == SVNNodeKind.NONE) {
594 return false;
595 }
596
597 if (isContentNode) {
598 String rootPath = actualPath.getParent().getString(registry);
599 String fileName = actualPath.getLastSegment().getString(registry);
600 modifyFile(rootPath, fileName, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, null, getName(), workspaceRoot);
601 } else {
602 eraseEntry(actualPath.getString(registry), null, getName(), workspaceRoot);
603 }
604 } catch (SVNException e) {
605 throw new RepositorySourceException(getSourceName(),
606 SvnRepositoryConnectorI18n.deleteFailed.text(nodePath, getSourceName()));
607 }
608
609 getCache(getName()).invalidate(nodePath);
610
611 return true;
612 }
613
614 public PathNode setProperties( ExecutionContext context,
615 Path nodePath,
616 Map<Name, Property> properties ) {
617 PathNode targetNode = getNode(nodePath);
618 if (targetNode == null) return null;
619
620
621
622
623
624
625 NameFactory nameFactory = context.getValueFactories().getNameFactory();
626 Property primaryTypeProperty = targetNode.getProperty(JcrLexicon.PRIMARY_TYPE);
627 Name primaryTypeName = primaryTypeProperty == null ? null : nameFactory.create(primaryTypeProperty.getFirstValue());
628 if (ModeShapeLexicon.RESOURCE.equals(primaryTypeName)) {
629
630 for (Map.Entry<Name, Property> entry : properties.entrySet()) {
631 if (JcrLexicon.DATA.equals(entry.getKey())) {
632 NamespaceRegistry registry = context.getNamespaceRegistry();
633 byte[] data;
634 if (entry.getValue() == null) {
635 data = EMPTY_BYTE_ARRAY;
636 } else {
637 BinaryFactory binaryFactory = context.getValueFactories().getBinaryFactory();
638 data = binaryFactory.create(entry.getValue().getFirstValue()).getBytes();
639
640 }
641
642 try {
643 Path actualPath = nodePath.getParent();
644 modifyFile(actualPath.getParent().getString(registry),
645 actualPath.getLastSegment().getString(registry),
646 EMPTY_BYTE_ARRAY,
647 data,
648 "",
649 getName(),
650 workspaceRoot);
651
652 PathNode node = getNode(nodePath);
653 getCache(getName()).set(node);
654
655 return node;
656 } catch (SVNException ex) {
657 throw new RepositorySourceException(getSourceName(),
658 SvnRepositoryConnectorI18n.deleteFailed.text(nodePath,
659 getSourceName()), ex);
660 }
661 }
662 }
663 }
664
665 return targetNode;
666 }
667
668 protected boolean readNode( ExecutionContext context,
669 String workspaceName,
670 Path requestedPath,
671 List<Property> properties,
672 List<Segment> children ) throws SVNException {
673 PathFactory pathFactory = context.getValueFactories().getPathFactory();
674 NamespaceRegistry registry = context.getNamespaceRegistry();
675
676 if (requestedPath.isRoot()) {
677
678 if (children != null) {
679 final Collection<SVNDirEntry> entries = SvnRepositoryUtil.getDir(workspaceRoot, "");
680 for (SVNDirEntry entry : entries) {
681
682
683 children.add(pathFactory.createSegment(entry.getName()));
684 }
685 }
686
687 } else {
688
689 PropertyFactory factory = context.getPropertyFactory();
690 DateTimeFactory dateFactory = context.getValueFactories().getDateFactory();
691
692
693 SVNNodeKind kind = getNodeKind(context, requestedPath, source.getRepositoryRootUrl());
694 if (kind == SVNNodeKind.NONE) {
695
696 return false;
697 }
698 if (kind == SVNNodeKind.DIR) {
699 String directoryPath = requestedPath.getString(registry);
700 if (!source.getRepositoryRootUrl().equals(workspaceName)) {
701 directoryPath = directoryPath.substring(1);
702 }
703 if (children != null) {
704
705 Collection<SVNDirEntry> dirEntries = SvnRepositoryUtil.getDir(workspaceRoot, directoryPath);
706 for (SVNDirEntry entry : dirEntries) {
707
708
709 children.add(pathFactory.createSegment(entry.getName()));
710 }
711 }
712 if (properties != null) {
713
714 properties.add(factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER));
715 SVNDirEntry entry = getEntryInfo(workspaceRoot, directoryPath);
716 if (entry != null) {
717 properties.add(factory.create(JcrLexicon.CREATED, dateFactory.create(entry.getDate())));
718 }
719 }
720 } else {
721
722
723 if (requestedPath.endsWith(JcrLexicon.CONTENT)) {
724
725 if (properties != null) {
726 String contentPath = requestedPath.getParent().getString(registry);
727 if (!source.getRepositoryRootUrl().equals(workspaceName)) {
728 contentPath = contentPath.substring(1);
729 }
730 SVNDirEntry entry = getEntryInfo(workspaceRoot, contentPath);
731 if (entry != null) {
732
733
734
735
736
737 properties.add(factory.create(JcrLexicon.PRIMARY_TYPE, ModeShapeLexicon.RESOURCE));
738 properties.add(factory.create(JcrLexicon.LAST_MODIFIED, dateFactory.create(entry.getDate())));
739 }
740
741 ByteArrayOutputStream os = new ByteArrayOutputStream();
742 SVNProperties fileProperties = new SVNProperties();
743 getData(contentPath, fileProperties, os);
744 String mimeType = fileProperties.getStringValue(SVNProperty.MIME_TYPE);
745 if (mimeType == null) mimeType = DEFAULT_MIME_TYPE;
746 properties.add(factory.create(JcrLexicon.MIMETYPE, mimeType));
747
748 if (os.toByteArray().length > 0) {
749
750 BinaryFactory binaryFactory = context.getValueFactories().getBinaryFactory();
751 properties.add(factory.create(JcrLexicon.DATA, binaryFactory.create(os.toByteArray())));
752 }
753 }
754 } else {
755
756 String filePath = requestedPath.getString(registry);
757 if (!source.getRepositoryRootUrl().equals(workspaceName)) {
758 filePath = filePath.substring(1);
759 }
760 if (children != null) {
761
762 children.add(pathFactory.createSegment(JcrLexicon.CONTENT));
763 }
764 if (properties != null) {
765
766 properties.add(factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE));
767 ByteArrayOutputStream os = new ByteArrayOutputStream();
768 SVNProperties fileProperties = new SVNProperties();
769 getData(filePath, fileProperties, os);
770 String created = fileProperties.getStringValue(SVNProperty.COMMITTED_DATE);
771 properties.add(factory.create(JcrLexicon.CREATED, dateFactory.create(created)));
772 }
773 }
774 }
775 }
776 return true;
777 }
778
779
780
781
782
783
784
785
786 protected SVNDirEntry getEntryInfo( SVNRepository repos,
787 String path ) {
788 assert path != null;
789 SVNDirEntry entry = null;
790 try {
791 entry = repos.info(path, -1);
792 } catch (SVNException e) {
793 throw new RepositorySourceException(
794 getSourceName(),
795 SvnRepositoryConnectorI18n.connectingFailureOrUserAuthenticationProblem.text(getSourceName()));
796 }
797 return entry;
798 }
799
800
801
802
803
804
805
806
807
808 protected void getData( String path,
809 SVNProperties properties,
810 OutputStream os ) throws SVNException {
811 workspaceRoot.getFile(path, -1, properties, os);
812
813 }
814
815 protected SVNNodeKind getNodeKind( ExecutionContext context,
816 Path path,
817 String repositoryRootUrl ) throws SVNException {
818 assert path != null;
819 assert repositoryRootUrl != null;
820
821
822 if (path.endsWith(JcrLexicon.CONTENT)) {
823
824 path = path.getParent();
825 }
826 String pathAsString = path.getString(context.getNamespaceRegistry());
827 if (!repositoryRootUrl.equals(getName())) {
828 pathAsString = pathAsString.substring(1);
829 }
830
831 String absolutePath = pathAsString;
832 SVNNodeKind kind = workspaceRoot.checkPath(absolutePath, -1);
833 if (kind == SVNNodeKind.UNKNOWN) {
834
835 throw new RepositorySourceException(getSourceName(),
836 SvnRepositoryConnectorI18n.nodeIsActuallyUnknow.text(pathAsString));
837 }
838 return kind;
839 }
840
841 protected SVNRepository getWorkspaceDirectory( String workspaceName ) {
842 if (workspaceName == null) workspaceName = source.getDefaultWorkspaceName();
843
844 workspaceName = source.getRepositoryRootUrl() + workspaceName;
845
846 SVNRepository repository = null;
847 SVNRepository repos = SvnRepositoryUtil.createRepository(workspaceName, source.getUsername(), source.getPassword());
848 if (SvnRepositoryUtil.isDirectory(repos, "")) {
849 repository = repos;
850 } else {
851 return null;
852 }
853 return repository;
854 }
855
856
857
858
859
860
861
862
863
864
865
866 protected void ensureValidProperties( ExecutionContext context,
867 Collection<Property> properties,
868 Set<Name> validPropertyNames ) {
869 List<String> invalidNames = new LinkedList<String>();
870 NamespaceRegistry registry = context.getNamespaceRegistry();
871
872 for (Property property : properties) {
873 if (!validPropertyNames.contains(property.getName())) {
874 invalidNames.add(property.getName().getString(registry));
875 }
876 }
877
878 if (!invalidNames.isEmpty()) {
879 throw new RepositorySourceException(getSourceName(),
880 SvnRepositoryConnectorI18n.invalidPropertyNames.text(invalidNames.toString()));
881 }
882 }
883
884 }
885
886 }