This project has retired. For details please refer to its
Attic page.
FileShareRepository xref
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.fileshare;
20
21 import java.io.BufferedInputStream;
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.math.BigDecimal;
31 import java.math.BigInteger;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.GregorianCalendar;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.LinkedHashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41 import java.util.TimeZone;
42
43 import javax.xml.bind.JAXBElement;
44 import javax.xml.bind.Marshaller;
45 import javax.xml.bind.Unmarshaller;
46
47 import org.apache.chemistry.opencmis.commons.PropertyIds;
48 import org.apache.chemistry.opencmis.commons.data.Ace;
49 import org.apache.chemistry.opencmis.commons.data.Acl;
50 import org.apache.chemistry.opencmis.commons.data.AllowableActions;
51 import org.apache.chemistry.opencmis.commons.data.ContentStream;
52 import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
53 import org.apache.chemistry.opencmis.commons.data.ObjectData;
54 import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
55 import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
56 import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
57 import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
58 import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
59 import org.apache.chemistry.opencmis.commons.data.Properties;
60 import org.apache.chemistry.opencmis.commons.data.PropertyData;
61 import org.apache.chemistry.opencmis.commons.data.PropertyDateTime;
62 import org.apache.chemistry.opencmis.commons.data.PropertyId;
63 import org.apache.chemistry.opencmis.commons.data.PropertyString;
64 import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
65 import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
66 import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
67 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
68 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
69 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
70 import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
71 import org.apache.chemistry.opencmis.commons.enums.Action;
72 import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
73 import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
74 import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
75 import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
76 import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
77 import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
78 import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
79 import org.apache.chemistry.opencmis.commons.enums.SupportedPermissions;
80 import org.apache.chemistry.opencmis.commons.enums.Updatability;
81 import org.apache.chemistry.opencmis.commons.enums.VersioningState;
82 import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
83 import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
84 import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
85 import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
86 import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
87 import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
88 import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
89 import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
90 import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
91 import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
92 import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
93 import org.apache.chemistry.opencmis.commons.impl.Converter;
94 import org.apache.chemistry.opencmis.commons.impl.JaxBHelper;
95 import org.apache.chemistry.opencmis.commons.impl.MimeTypes;
96 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl;
97 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
98 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl;
99 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AclCapabilitiesDataImpl;
100 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AllowableActionsImpl;
101 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
102 import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
103 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
104 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderContainerImpl;
105 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
106 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
107 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
108 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionDefinitionDataImpl;
109 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionMappingDataImpl;
110 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
111 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
112 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
113 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalImpl;
114 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlImpl;
115 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
116 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
117 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
118 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl;
119 import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl;
120 import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl;
121 import org.apache.chemistry.opencmis.commons.impl.jaxb.CmisObjectType;
122 import org.apache.chemistry.opencmis.commons.impl.jaxb.CmisProperty;
123 import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
124 import org.apache.chemistry.opencmis.commons.server.CallContext;
125 import org.apache.chemistry.opencmis.commons.server.ObjectInfoHandler;
126 import org.apache.chemistry.opencmis.commons.spi.Holder;
127 import org.apache.commons.codec.binary.Base64;
128 import org.apache.commons.logging.Log;
129 import org.apache.commons.logging.LogFactory;
130
131
132
133
134 public class FileShareRepository {
135
136 private static final String ROOT_ID = "@root@";
137 private static final String SHADOW_EXT = ".cmis.xml";
138 private static final String SHADOW_FOLDER = "cmis.xml";
139
140 private static final String USER_UNKNOWN = "<unknown>";
141
142 private static final String CMIS_READ = "cmis:read";
143 private static final String CMIS_WRITE = "cmis:write";
144 private static final String CMIS_ALL = "cmis:all";
145
146 private static final int BUFFER_SIZE = 64 * 1024;
147
148 private static final Log log = LogFactory.getLog(FileShareRepository.class);
149
150
151 private final String repositoryId;
152
153 private final File root;
154
155 private final TypeManager types;
156
157 private final Map<String, Boolean> userMap;
158
159 private final RepositoryInfoImpl repositoryInfo;
160
161
162
163
164
165
166
167
168
169
170
171 public FileShareRepository(String repId, String rootPath, TypeManager types) {
172
173 if ((repId == null) || (repId.trim().length() == 0)) {
174 throw new IllegalArgumentException("Invalid repository id!");
175 }
176
177 repositoryId = repId;
178
179
180 if ((rootPath == null) || (rootPath.trim().length() == 0)) {
181 throw new IllegalArgumentException("Invalid root folder!");
182 }
183
184 root = new File(rootPath);
185 if (!root.isDirectory()) {
186 throw new IllegalArgumentException("Root is not a directory!");
187 }
188
189
190 this.types = types;
191
192
193 userMap = new HashMap<String, Boolean>();
194
195
196 repositoryInfo = new RepositoryInfoImpl();
197
198 repositoryInfo.setId(repositoryId);
199 repositoryInfo.setName(repositoryId);
200 repositoryInfo.setDescription(repositoryId);
201
202 repositoryInfo.setCmisVersionSupported("1.0");
203
204 repositoryInfo.setProductName("OpenCMIS FileShare");
205 repositoryInfo.setProductVersion("0.1");
206 repositoryInfo.setVendorName("OpenCMIS");
207
208 repositoryInfo.setRootFolder(ROOT_ID);
209
210 repositoryInfo.setThinClientUri("");
211
212 RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
213 capabilities.setCapabilityAcl(CapabilityAcl.DISCOVER);
214 capabilities.setAllVersionsSearchable(false);
215 capabilities.setCapabilityJoin(CapabilityJoin.NONE);
216 capabilities.setSupportsMultifiling(false);
217 capabilities.setSupportsUnfiling(false);
218 capabilities.setSupportsVersionSpecificFiling(false);
219 capabilities.setIsPwcSearchable(false);
220 capabilities.setIsPwcUpdatable(false);
221 capabilities.setCapabilityQuery(CapabilityQuery.NONE);
222 capabilities.setCapabilityChanges(CapabilityChanges.NONE);
223 capabilities.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.ANYTIME);
224 capabilities.setSupportsGetDescendants(true);
225 capabilities.setSupportsGetFolderTree(true);
226 capabilities.setCapabilityRendition(CapabilityRenditions.NONE);
227
228 repositoryInfo.setCapabilities(capabilities);
229
230 AclCapabilitiesDataImpl aclCapability = new AclCapabilitiesDataImpl();
231 aclCapability.setSupportedPermissions(SupportedPermissions.BASIC);
232 aclCapability.setAclPropagation(AclPropagation.OBJECTONLY);
233
234
235 List<PermissionDefinition> permissions = new ArrayList<PermissionDefinition>();
236 permissions.add(createPermission(CMIS_READ, "Read"));
237 permissions.add(createPermission(CMIS_WRITE, "Write"));
238 permissions.add(createPermission(CMIS_ALL, "All"));
239 aclCapability.setPermissionDefinitionData(permissions);
240
241
242 List<PermissionMapping> list = new ArrayList<PermissionMapping>();
243 list.add(createMapping(PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER, CMIS_READ));
244 list.add(createMapping(PermissionMapping.CAN_CREATE_FOLDER_FOLDER, CMIS_READ));
245 list.add(createMapping(PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT, CMIS_WRITE));
246 list.add(createMapping(PermissionMapping.CAN_DELETE_OBJECT, CMIS_ALL));
247 list.add(createMapping(PermissionMapping.CAN_DELETE_TREE_FOLDER, CMIS_ALL));
248 list.add(createMapping(PermissionMapping.CAN_GET_ACL_OBJECT, CMIS_READ));
249 list.add(createMapping(PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES, CMIS_READ));
250 list.add(createMapping(PermissionMapping.CAN_GET_CHILDREN_FOLDER, CMIS_READ));
251 list.add(createMapping(PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, CMIS_READ));
252 list.add(createMapping(PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, CMIS_READ));
253 list.add(createMapping(PermissionMapping.CAN_GET_PARENTS_FOLDER, CMIS_READ));
254 list.add(createMapping(PermissionMapping.CAN_GET_PROPERTIES_OBJECT, CMIS_READ));
255 list.add(createMapping(PermissionMapping.CAN_MOVE_OBJECT, CMIS_WRITE));
256 list.add(createMapping(PermissionMapping.CAN_MOVE_SOURCE, CMIS_READ));
257 list.add(createMapping(PermissionMapping.CAN_MOVE_TARGET, CMIS_WRITE));
258 list.add(createMapping(PermissionMapping.CAN_SET_CONTENT_DOCUMENT, CMIS_WRITE));
259 list.add(createMapping(PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, CMIS_WRITE));
260 list.add(createMapping(PermissionMapping.CAN_VIEW_CONTENT_OBJECT, CMIS_READ));
261 Map<String, PermissionMapping> map = new LinkedHashMap<String, PermissionMapping>();
262 for (PermissionMapping pm : list) {
263 map.put(pm.getKey(), pm);
264 }
265 aclCapability.setPermissionMappingData(map);
266
267 repositoryInfo.setAclCapabilities(aclCapability);
268 }
269
270 private static PermissionDefinition createPermission(String permission, String description) {
271 PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
272 pd.setPermission(permission);
273 pd.setDescription(description);
274
275 return pd;
276 }
277
278 private static PermissionMapping createMapping(String key, String permission) {
279 PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
280 pm.setKey(key);
281 pm.setPermissions(Collections.singletonList(permission));
282
283 return pm;
284 }
285
286
287
288
289 public void addUser(String user, boolean readOnly) {
290 if ((user == null) || (user.length() == 0)) {
291 return;
292 }
293
294 userMap.put(user, readOnly);
295 }
296
297
298
299
300
301
302 public String getRepositoryId() {
303 return repositoryId;
304 }
305
306
307
308
309 public RepositoryInfo getRepositoryInfo(CallContext context) {
310 debug("getRepositoryInfo");
311 checkUser(context, false);
312
313 return repositoryInfo;
314 }
315
316
317
318
319 public TypeDefinitionList getTypesChildren(CallContext context, String typeId, boolean includePropertyDefinitions,
320 BigInteger maxItems, BigInteger skipCount) {
321 debug("getTypesChildren");
322 checkUser(context, false);
323
324 return types.getTypesChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
325 }
326
327
328
329
330 public TypeDefinition getTypeDefinition(CallContext context, String typeId) {
331 debug("getTypeDefinition");
332 checkUser(context, false);
333
334 return types.getTypeDefinition(context, typeId);
335 }
336
337
338
339
340 public List<TypeDefinitionContainer> getTypesDescendants(CallContext context, String typeId, BigInteger depth,
341 Boolean includePropertyDefinitions) {
342 debug("getTypesDescendants");
343 checkUser(context, false);
344
345 return types.getTypesDescendants(context, typeId, depth, includePropertyDefinitions);
346 }
347
348
349
350
351 public ObjectData create(CallContext context, Properties properties, String folderId, ContentStream contentStream,
352 VersioningState versioningState, ObjectInfoHandler objectInfos) {
353 debug("create");
354 boolean userReadOnly = checkUser(context, true);
355
356 String typeId = getTypeId(properties);
357 TypeDefinition type = types.getType(typeId);
358 if (type == null) {
359 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
360 }
361
362 String objectId = null;
363 if (type.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT) {
364 objectId = createDocument(context, properties, folderId, contentStream, versioningState);
365 } else if (type.getBaseTypeId() == BaseTypeId.CMIS_FOLDER) {
366 objectId = createFolder(context, properties, folderId);
367 } else {
368 throw new CmisObjectNotFoundException("Cannot create object of type '" + typeId + "'!");
369 }
370
371 return compileObjectType(context, getFile(objectId), null, false, false, userReadOnly, objectInfos);
372 }
373
374
375
376
377 public String createDocument(CallContext context, Properties properties, String folderId,
378 ContentStream contentStream, VersioningState versioningState) {
379 debug("createDocument");
380 checkUser(context, true);
381
382
383 if ((properties == null) || (properties.getProperties() == null)) {
384 throw new CmisInvalidArgumentException("Properties must be set!");
385 }
386
387
388 if (VersioningState.NONE != versioningState) {
389 throw new CmisConstraintException("Versioning not supported!");
390 }
391
392
393 String typeId = getTypeId(properties);
394 TypeDefinition type = types.getType(typeId);
395 if (type == null) {
396 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
397 }
398
399
400 Properties props = compileProperties(typeId, context.getUsername(),
401 millisToCalendar(System.currentTimeMillis()), context.getUsername(), properties);
402
403
404 String name = getStringProperty(properties, PropertyIds.NAME);
405 if (!isValidName(name)) {
406 throw new CmisNameConstraintViolationException("Name is not valid!");
407 }
408
409
410 File parent = getFile(folderId);
411 if (!parent.isDirectory()) {
412 throw new CmisObjectNotFoundException("Parent is not a folder!");
413 }
414
415
416 File newFile = new File(parent, name);
417 if (newFile.exists()) {
418 throw new CmisNameConstraintViolationException("Document already exists!");
419 }
420
421
422 try {
423 newFile.createNewFile();
424 } catch (IOException e) {
425 throw new CmisStorageException("Could not create file: " + e.getMessage());
426 }
427
428
429 if ((contentStream != null) && (contentStream.getStream() != null)) {
430 try {
431 OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile), BUFFER_SIZE);
432 InputStream in = new BufferedInputStream(contentStream.getStream(), BUFFER_SIZE);
433
434 byte[] buffer = new byte[BUFFER_SIZE];
435 int b;
436 while ((b = in.read(buffer)) > -1) {
437 out.write(buffer, 0, b);
438 }
439
440 out.flush();
441 out.close();
442 in.close();
443 } catch (Exception e) {
444 throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
445 }
446 }
447
448
449 writePropertiesFile(newFile, props);
450
451 return getId(newFile);
452 }
453
454
455
456
457 public String createDocumentFromSource(CallContext context, String sourceId, Properties properties,
458 String folderId, VersioningState versioningState) {
459
460
461 if (VersioningState.NONE != versioningState) {
462 throw new CmisConstraintException("Versioning not supported!");
463 }
464
465
466 File parent = getFile(folderId);
467 if (!parent.isDirectory()) {
468 throw new CmisObjectNotFoundException("Parent is not a folder!");
469 }
470
471
472 File source = getFile(sourceId);
473 if (!source.isFile()) {
474 throw new CmisObjectNotFoundException("Source is not a document!");
475 }
476
477
478 String name = source.getName();
479
480
481 PropertiesImpl sourceProperties = new PropertiesImpl();
482 readCustomProperties(source, sourceProperties, null, new ObjectInfoImpl());
483
484
485 String typeId = getIdProperty(sourceProperties, PropertyIds.OBJECT_TYPE_ID);
486 if (typeId == null) {
487 typeId = TypeManager.DOCUMENT_TYPE_ID;
488 }
489
490
491 PropertiesImpl newProperties = new PropertiesImpl();
492 for (PropertyData<?> prop : sourceProperties.getProperties().values()) {
493 if ((prop.getId().equals(PropertyIds.OBJECT_TYPE_ID)) || (prop.getId().equals(PropertyIds.CREATED_BY))
494 || (prop.getId().equals(PropertyIds.CREATION_DATE))
495 || (prop.getId().equals(PropertyIds.LAST_MODIFIED_BY))) {
496 continue;
497 }
498
499 newProperties.addProperty(prop);
500 }
501
502
503 if (properties != null) {
504
505 String newName = getStringProperty(properties, PropertyIds.NAME);
506 if (newName != null) {
507 if (!isValidName(newName)) {
508 throw new CmisNameConstraintViolationException("Name is not valid!");
509 }
510 name = newName;
511 }
512
513
514 TypeDefinition type = types.getType(typeId);
515 if (type == null) {
516 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
517 }
518
519
520 for (PropertyData<?> prop : properties.getProperties().values()) {
521 PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
522
523
524 if (propType == null) {
525 throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
526 }
527
528
529 if ((propType.getUpdatability() != Updatability.READWRITE)) {
530 throw new CmisConstraintException("Property '" + prop.getId() + "' cannot be updated!");
531 }
532
533
534 if (isEmptyProperty(prop)) {
535 throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
536 }
537
538 newProperties.addProperty(prop);
539 }
540 }
541
542 addPropertyId(newProperties, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
543 addPropertyString(newProperties, typeId, null, PropertyIds.CREATED_BY, context.getUsername());
544 addPropertyDateTime(newProperties, typeId, null, PropertyIds.CREATION_DATE,
545 millisToCalendar(System.currentTimeMillis()));
546 addPropertyString(newProperties, typeId, null, PropertyIds.LAST_MODIFIED_BY, context.getUsername());
547
548
549 File newFile = new File(parent, name);
550 if (newFile.exists()) {
551 throw new CmisNameConstraintViolationException("Document already exists.");
552 }
553
554
555 try {
556 newFile.createNewFile();
557 } catch (IOException e) {
558 throw new CmisStorageException("Could not create file: " + e.getMessage(), e);
559 }
560
561
562 try {
563 OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
564 InputStream in = new BufferedInputStream(new FileInputStream(source));
565
566 byte[] buffer = new byte[BUFFER_SIZE];
567 int b;
568 while ((b = in.read(buffer)) > -1) {
569 out.write(buffer, 0, b);
570 }
571
572 out.flush();
573 out.close();
574 in.close();
575 } catch (Exception e) {
576 throw new CmisStorageException("Could not roead or write content: " + e.getMessage(), e);
577 }
578
579
580 writePropertiesFile(newFile, newProperties);
581
582 return getId(newFile);
583 }
584
585
586
587
588 public String createFolder(CallContext context, Properties properties, String folderId) {
589 debug("createFolder");
590 checkUser(context, true);
591
592
593 if ((properties == null) || (properties.getProperties() == null)) {
594 throw new CmisInvalidArgumentException("Properties must be set!");
595 }
596
597
598 String typeId = getTypeId(properties);
599 TypeDefinition type = types.getType(typeId);
600 if (type == null) {
601 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
602 }
603
604
605 Properties props = compileProperties(typeId, context.getUsername(),
606 millisToCalendar(System.currentTimeMillis()), context.getUsername(), properties);
607
608
609 String name = getStringProperty(properties, PropertyIds.NAME);
610 if (!isValidName(name)) {
611 throw new CmisNameConstraintViolationException("Name is not valid.");
612 }
613
614
615 File parent = getFile(folderId);
616 if (!parent.isDirectory()) {
617 throw new CmisObjectNotFoundException("Parent is not a folder!");
618 }
619
620
621 File newFolder = new File(parent, name);
622 if (!newFolder.mkdir()) {
623 throw new CmisStorageException("Could not create folder!");
624 }
625
626
627 writePropertiesFile(newFolder, props);
628
629 return getId(newFolder);
630 }
631
632
633
634
635 public ObjectData moveObject(CallContext context, Holder<String> objectId, String targetFolderId,
636 ObjectInfoHandler objectInfos) {
637 debug("moveObject");
638 boolean userReadOnly = checkUser(context, true);
639
640 if (objectId == null) {
641 throw new CmisInvalidArgumentException("Id is not valid!");
642 }
643
644
645 File file = getFile(objectId.getValue());
646 File parent = getFile(targetFolderId);
647
648
649 File newFile = new File(parent, file.getName());
650 if (newFile.exists()) {
651 throw new CmisStorageException("Object already exists!");
652 }
653
654
655 if (!file.renameTo(newFile)) {
656 throw new CmisStorageException("Move failed!");
657 } else {
658
659 objectId.setValue(getId(newFile));
660
661
662 if (newFile.isFile()) {
663 File propFile = getPropertiesFile(file);
664 if (propFile.exists()) {
665 File newPropFile = new File(parent, propFile.getName());
666 propFile.renameTo(newPropFile);
667 }
668 }
669 }
670
671 return compileObjectType(context, newFile, null, false, false, userReadOnly, objectInfos);
672 }
673
674
675
676
677 public void setContentStream(CallContext context, Holder<String> objectId, Boolean overwriteFlag,
678 ContentStream contentStream) {
679 debug("setContentStream or deleteContentStream");
680 checkUser(context, true);
681
682 if (objectId == null) {
683 throw new CmisInvalidArgumentException("Id is not valid!");
684 }
685
686
687 File file = getFile(objectId.getValue());
688 if (!file.isFile()) {
689 throw new CmisStreamNotSupportedException("Not a file!");
690 }
691
692
693 boolean owf = (overwriteFlag == null ? true : overwriteFlag.booleanValue());
694 if (!owf && file.length() > 0) {
695 throw new CmisContentAlreadyExistsException("Content already exists!");
696 }
697
698 try {
699 OutputStream out = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE);
700
701 if ((contentStream == null) || (contentStream.getStream() == null)) {
702
703 out.write(new byte[0]);
704 } else {
705
706 InputStream in = new BufferedInputStream(contentStream.getStream(), BUFFER_SIZE);
707
708 byte[] buffer = new byte[BUFFER_SIZE];
709 int b;
710 while ((b = in.read(buffer)) > -1) {
711 out.write(buffer, 0, b);
712 }
713
714 in.close();
715 }
716
717 out.close();
718 } catch (Exception e) {
719 throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
720 }
721 }
722
723
724
725
726 public void deleteObject(CallContext context, String objectId) {
727 debug("deleteObject");
728 checkUser(context, true);
729
730
731 File file = getFile(objectId);
732 if (!file.exists()) {
733 throw new CmisObjectNotFoundException("Object not found!");
734 }
735
736
737 if (!isFolderEmpty(file)) {
738 throw new CmisConstraintException("Folder is not empty!");
739 }
740
741
742 getPropertiesFile(file).delete();
743 if (!file.delete()) {
744 throw new CmisStorageException("Deletion failed!");
745 }
746 }
747
748
749
750
751 public FailedToDeleteData deleteTree(CallContext context, String folderId, Boolean continueOnFailure) {
752 debug("deleteTree");
753 checkUser(context, true);
754
755 boolean cof = (continueOnFailure == null ? false : continueOnFailure.booleanValue());
756
757
758 File file = getFile(folderId);
759
760 FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
761 result.setIds(new ArrayList<String>());
762
763
764 if (file.isDirectory()) {
765 deleteFolder(file, cof, result);
766 } else {
767 getPropertiesFile(file).delete();
768 if (!file.delete()) {
769 result.getIds().add(getId(file));
770 }
771 }
772
773 return result;
774 }
775
776
777
778
779 public ObjectData updateProperties(CallContext context, Holder<String> objectId, Properties properties,
780 ObjectInfoHandler objectInfos) {
781 debug("updateProperties");
782 boolean userReadOnly = checkUser(context, true);
783
784 if (objectId == null) {
785 throw new CmisInvalidArgumentException("Id is not valid!");
786 }
787
788
789 File file = getFile(objectId.getValue());
790
791
792 String newName = getStringProperty(properties, PropertyIds.NAME);
793 boolean isRename = (newName != null) && (!file.getName().equals(newName));
794 if (isRename && !isValidName(newName)) {
795 throw new CmisNameConstraintViolationException("Name is not valid!");
796 }
797
798
799 PropertiesImpl oldProperties = new PropertiesImpl();
800 readCustomProperties(file, oldProperties, null, new ObjectInfoImpl());
801
802
803 String typeId = getIdProperty(oldProperties, PropertyIds.OBJECT_TYPE_ID);
804 if (typeId == null) {
805 typeId = (file.isDirectory() ? TypeManager.FOLDER_TYPE_ID : TypeManager.DOCUMENT_TYPE_ID);
806 }
807
808
809 String creator = getStringProperty(oldProperties, PropertyIds.CREATED_BY);
810 if (creator == null) {
811 creator = context.getUsername();
812 }
813
814
815 GregorianCalendar creationDate = getDateTimeProperty(oldProperties, PropertyIds.CREATION_DATE);
816 if (creationDate == null) {
817 creationDate = millisToCalendar(file.lastModified());
818 }
819
820
821 Properties props = updateProperties(typeId, creator, creationDate, context.getUsername(), oldProperties,
822 properties);
823
824
825 writePropertiesFile(file, props);
826
827
828 File newFile = file;
829 if (isRename) {
830 File parent = file.getParentFile();
831 File propFile = getPropertiesFile(file);
832 newFile = new File(parent, newName);
833 if (!file.renameTo(newFile)) {
834
835 throw new CmisUpdateConflictException("Could not rename object!");
836 } else {
837
838 objectId.setValue(getId(newFile));
839
840
841 if (newFile.isFile()) {
842 if (propFile.exists()) {
843 File newPropFile = new File(parent, newName + SHADOW_EXT);
844 propFile.renameTo(newPropFile);
845 }
846 }
847 }
848 }
849
850 return compileObjectType(context, newFile, null, false, false, userReadOnly, objectInfos);
851 }
852
853
854
855
856 public ObjectData getObject(CallContext context, String objectId, String versionServicesId, String filter,
857 Boolean includeAllowableActions, Boolean includeAcl, ObjectInfoHandler objectInfos) {
858 debug("getObject");
859 boolean userReadOnly = checkUser(context, false);
860
861
862 if ((objectId == null) && (versionServicesId == null)) {
863 throw new CmisInvalidArgumentException("Object Id must be set.");
864 }
865
866 if (objectId == null) {
867
868
869 objectId = versionServicesId;
870 }
871
872
873 File file = getFile(objectId);
874
875
876 boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
877 boolean iacl = (includeAcl == null ? false : includeAcl.booleanValue());
878
879
880 Set<String> filterCollection = splitFilter(filter);
881
882
883 return compileObjectType(context, file, filterCollection, iaa, iacl, userReadOnly, objectInfos);
884 }
885
886
887
888
889 public AllowableActions getAllowableActions(CallContext context, String objectId) {
890 debug("getAllowableActions");
891 boolean userReadOnly = checkUser(context, false);
892
893 File file = getFile(objectId);
894 if (!file.exists()) {
895 throw new CmisObjectNotFoundException("Object not found!");
896 }
897
898 return compileAllowableActions(file, userReadOnly);
899 }
900
901
902
903
904 public Acl getAcl(CallContext context, String objectId) {
905 debug("getAcl");
906 checkUser(context, false);
907
908
909 File file = getFile(objectId);
910 if (!file.exists()) {
911 throw new CmisObjectNotFoundException("Object not found!");
912 }
913
914 return compileAcl(file);
915 }
916
917
918
919
920 public ContentStream getContentStream(CallContext context, String objectId, BigInteger offset, BigInteger length) {
921 debug("getContentStream");
922 checkUser(context, false);
923
924 if ((offset != null) || (length != null)) {
925 throw new CmisInvalidArgumentException("Offset and Length are not supported!");
926 }
927
928
929 final File file = getFile(objectId);
930 if (!file.isFile()) {
931 throw new CmisStreamNotSupportedException("Not a file!");
932 }
933
934 if (file.length() == 0) {
935 throw new CmisConstraintException("Document has no content!");
936 }
937
938 InputStream stream = null;
939 try {
940 stream = new BufferedInputStream(new FileInputStream(file), 4 * 1024);
941 } catch (FileNotFoundException e) {
942 throw new CmisObjectNotFoundException(e.getMessage(), e);
943 }
944
945
946 ContentStreamImpl result = new ContentStreamImpl();
947 result.setFileName(file.getName());
948 result.setLength(BigInteger.valueOf(file.length()));
949 result.setMimeType(MimeTypes.getMIMEType(file));
950 result.setStream(stream);
951
952 return result;
953 }
954
955
956
957
958 public ObjectInFolderList getChildren(CallContext context, String folderId, String filter,
959 Boolean includeAllowableActions, Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount,
960 ObjectInfoHandler objectInfos) {
961 debug("getChildren");
962 boolean userReadOnly = checkUser(context, false);
963
964
965 Set<String> filterCollection = splitFilter(filter);
966
967
968 boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
969 boolean ips = (includePathSegment == null ? false : includePathSegment.booleanValue());
970
971
972 int skip = (skipCount == null ? 0 : skipCount.intValue());
973 if (skip < 0) {
974 skip = 0;
975 }
976
977 int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
978 if (max < 0) {
979 max = Integer.MAX_VALUE;
980 }
981
982
983 File folder = getFile(folderId);
984 if (!folder.isDirectory()) {
985 throw new CmisObjectNotFoundException("Not a folder!");
986 }
987
988
989 if (context.isObjectInfoRequired()) {
990 compileObjectType(context, folder, null, false, false, userReadOnly, objectInfos);
991 }
992
993
994 ObjectInFolderListImpl result = new ObjectInFolderListImpl();
995 result.setObjects(new ArrayList<ObjectInFolderData>());
996 result.setHasMoreItems(false);
997 int count = 0;
998
999
1000 for (File child : folder.listFiles()) {
1001
1002 if (child.isHidden() || child.getName().equals(SHADOW_FOLDER) || child.getPath().endsWith(SHADOW_EXT)) {
1003 continue;
1004 }
1005
1006 count++;
1007
1008 if (skip > 0) {
1009 skip--;
1010 continue;
1011 }
1012
1013 if (result.getObjects().size() >= max) {
1014 result.setHasMoreItems(true);
1015 continue;
1016 }
1017
1018
1019 ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
1020 objectInFolder.setObject(compileObjectType(context, child, filterCollection, iaa, false, userReadOnly,
1021 objectInfos));
1022 if (ips) {
1023 objectInFolder.setPathSegment(child.getName());
1024 }
1025
1026 result.getObjects().add(objectInFolder);
1027 }
1028
1029 result.setNumItems(BigInteger.valueOf(count));
1030
1031 return result;
1032 }
1033
1034
1035
1036
1037 public List<ObjectInFolderContainer> getDescendants(CallContext context, String folderId, BigInteger depth,
1038 String filter, Boolean includeAllowableActions, Boolean includePathSegment, ObjectInfoHandler objectInfos,
1039 boolean foldersOnly) {
1040 debug("getDescendants or getFolderTree");
1041 boolean userReadOnly = checkUser(context, false);
1042
1043
1044 int d = (depth == null ? 2 : depth.intValue());
1045 if (d == 0) {
1046 throw new CmisInvalidArgumentException("Depth must not be 0!");
1047 }
1048 if (d < -1) {
1049 d = -1;
1050 }
1051
1052
1053 Set<String> filterCollection = splitFilter(filter);
1054
1055
1056 boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
1057 boolean ips = (includePathSegment == null ? false : includePathSegment.booleanValue());
1058
1059
1060 File folder = getFile(folderId);
1061 if (!folder.isDirectory()) {
1062 throw new CmisObjectNotFoundException("Not a folder!");
1063 }
1064
1065
1066 if (context.isObjectInfoRequired()) {
1067 compileObjectType(context, folder, null, false, false, userReadOnly, objectInfos);
1068 }
1069
1070
1071 List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
1072 gatherDescendants(context, folder, result, foldersOnly, d, filterCollection, iaa, ips, userReadOnly,
1073 objectInfos);
1074
1075 return result;
1076 }
1077
1078
1079
1080
1081 public ObjectData getFolderParent(CallContext context, String folderId, String filter, ObjectInfoHandler objectInfos) {
1082 List<ObjectParentData> parents = getObjectParents(context, folderId, filter, false, false, objectInfos);
1083
1084 if (parents.size() == 0) {
1085 throw new CmisInvalidArgumentException("The root folder has no parent!");
1086 }
1087
1088 return parents.get(0).getObject();
1089 }
1090
1091
1092
1093
1094 public List<ObjectParentData> getObjectParents(CallContext context, String objectId, String filter,
1095 Boolean includeAllowableActions, Boolean includeRelativePathSegment, ObjectInfoHandler objectInfos) {
1096 debug("getObjectParents");
1097 boolean userReadOnly = checkUser(context, false);
1098
1099
1100 Set<String> filterCollection = splitFilter(filter);
1101
1102
1103 boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
1104 boolean irps = (includeRelativePathSegment == null ? false : includeRelativePathSegment.booleanValue());
1105
1106
1107 File file = getFile(objectId);
1108
1109
1110 if (root.equals(file)) {
1111 return Collections.emptyList();
1112 }
1113
1114
1115 if (context.isObjectInfoRequired()) {
1116 compileObjectType(context, file, null, false, false, userReadOnly, objectInfos);
1117 }
1118
1119
1120 File parent = file.getParentFile();
1121 ObjectData object = compileObjectType(context, parent, filterCollection, iaa, false, userReadOnly, objectInfos);
1122
1123 ObjectParentDataImpl result = new ObjectParentDataImpl();
1124 result.setObject(object);
1125 if (irps) {
1126 result.setRelativePathSegment(file.getName());
1127 }
1128
1129 return Collections.singletonList((ObjectParentData) result);
1130 }
1131
1132
1133
1134
1135 public ObjectData getObjectByPath(CallContext context, String folderPath, String filter,
1136 boolean includeAllowableActions, boolean includeACL, ObjectInfoHandler objectInfos) {
1137 debug("getObjectByPath");
1138 boolean userReadOnly = checkUser(context, false);
1139
1140
1141 Set<String> filterCollection = splitFilter(filter);
1142
1143
1144 if ((folderPath == null) || (!folderPath.startsWith("/"))) {
1145 throw new CmisInvalidArgumentException("Invalid folder path!");
1146 }
1147
1148
1149 File file = null;
1150 if (folderPath.length() == 1) {
1151 file = root;
1152 } else {
1153 String path = folderPath.replace('/', File.separatorChar).substring(1);
1154 file = new File(root, path);
1155 }
1156
1157 if (!file.exists()) {
1158 throw new CmisObjectNotFoundException("Path doesn't exist.");
1159 }
1160
1161 return compileObjectType(context, file, filterCollection, includeAllowableActions, includeACL, userReadOnly,
1162 objectInfos);
1163 }
1164
1165
1166
1167
1168
1169
1170 private void gatherDescendants(CallContext context, File folder, List<ObjectInFolderContainer> list,
1171 boolean foldersOnly, int depth, Set<String> filter, boolean includeAllowableActions,
1172 boolean includePathSegments, boolean userReadOnly, ObjectInfoHandler objectInfos) {
1173
1174 for (File child : folder.listFiles()) {
1175
1176 if (child.isHidden() || child.getName().equals(SHADOW_FOLDER) || child.getPath().endsWith(SHADOW_EXT)) {
1177 continue;
1178 }
1179
1180
1181 if (foldersOnly && !child.isDirectory()) {
1182 continue;
1183 }
1184
1185
1186 ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
1187 objectInFolder.setObject(compileObjectType(context, child, filter, includeAllowableActions, false,
1188 userReadOnly, objectInfos));
1189 if (includePathSegments) {
1190 objectInFolder.setPathSegment(child.getName());
1191 }
1192
1193 ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl();
1194 container.setObject(objectInFolder);
1195
1196 list.add(container);
1197
1198
1199 if ((depth != 1) && child.isDirectory()) {
1200 container.setChildren(new ArrayList<ObjectInFolderContainer>());
1201 gatherDescendants(context, child, container.getChildren(), foldersOnly, depth - 1, filter,
1202 includeAllowableActions, includePathSegments, userReadOnly, objectInfos);
1203 }
1204 }
1205 }
1206
1207
1208
1209
1210
1211
1212 private boolean deleteFolder(File folder, boolean continueOnFailure, FailedToDeleteDataImpl ftd) {
1213 boolean success = true;
1214
1215 for (File file : folder.listFiles()) {
1216 if (file.isDirectory()) {
1217 if (!deleteFolder(file, continueOnFailure, ftd)) {
1218 if (!continueOnFailure) {
1219 return false;
1220 }
1221 success = false;
1222 }
1223 } else {
1224 if (!file.delete()) {
1225 ftd.getIds().add(getId(file));
1226 if (!continueOnFailure) {
1227 return false;
1228 }
1229 success = false;
1230 }
1231 }
1232 }
1233
1234 if (!folder.delete()) {
1235 ftd.getIds().add(getId(folder));
1236 success = false;
1237 }
1238
1239 return success;
1240 }
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251 private static boolean isValidName(String name) {
1252 if ((name == null) || (name.length() == 0) || (name.indexOf(File.separatorChar) != -1)
1253 || (name.indexOf(File.pathSeparatorChar) != -1)) {
1254 return false;
1255 }
1256
1257 return true;
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269 private static boolean isFolderEmpty(File folder) {
1270 if (!folder.isDirectory()) {
1271 return true;
1272 }
1273
1274 String[] fileNames = folder.list();
1275
1276 if ((fileNames == null) || (fileNames.length == 0)) {
1277 return true;
1278 }
1279
1280 if ((fileNames.length == 1) && (fileNames[0].equals(SHADOW_FOLDER))) {
1281 return true;
1282 }
1283
1284 return false;
1285 }
1286
1287
1288
1289
1290 private ObjectData compileObjectType(CallContext context, File file, Set<String> filter,
1291 boolean includeAllowableActions, boolean includeAcl, boolean userReadOnly, ObjectInfoHandler objectInfos) {
1292 ObjectDataImpl result = new ObjectDataImpl();
1293 ObjectInfoImpl objectInfo = new ObjectInfoImpl();
1294
1295 result.setProperties(compileProperties(file, filter, objectInfo));
1296
1297 if (includeAllowableActions) {
1298 result.setAllowableActions(compileAllowableActions(file, userReadOnly));
1299 }
1300
1301 if (includeAcl) {
1302 result.setAcl(compileAcl(file));
1303 result.setIsExactAcl(true);
1304 }
1305
1306 if (context.isObjectInfoRequired()) {
1307 objectInfo.setObject(result);
1308 objectInfos.addObjectInfo(objectInfo);
1309 }
1310
1311 return result;
1312 }
1313
1314
1315
1316
1317 private Properties compileProperties(File file, Set<String> orgfilter, ObjectInfoImpl objectInfo) {
1318 if (file == null) {
1319 throw new IllegalArgumentException("File must not be null!");
1320 }
1321
1322
1323 if (!file.exists()) {
1324 throw new CmisObjectNotFoundException("Object not found!");
1325 }
1326
1327
1328 Set<String> filter = (orgfilter == null ? null : new HashSet<String>(orgfilter));
1329
1330
1331 String typeId = null;
1332
1333 if (file.isDirectory()) {
1334 typeId = TypeManager.FOLDER_TYPE_ID;
1335 objectInfo.setBaseType(BaseTypeId.CMIS_FOLDER);
1336 objectInfo.setTypeId(typeId);
1337 objectInfo.setContentType(null);
1338 objectInfo.setFileName(null);
1339 objectInfo.setHasAcl(true);
1340 objectInfo.setHasContent(false);
1341 objectInfo.setVersionSeriesId(null);
1342 objectInfo.setIsCurrentVersion(true);
1343 objectInfo.setRelationshipSourceIds(null);
1344 objectInfo.setRelationshipTargetIds(null);
1345 objectInfo.setRenditionInfos(null);
1346 objectInfo.setSupportsDescendants(true);
1347 objectInfo.setSupportsFolderTree(true);
1348 objectInfo.setSupportsPolicies(false);
1349 objectInfo.setSupportsRelationships(false);
1350 objectInfo.setWorkingCopyId(null);
1351 objectInfo.setWorkingCopyOriginalId(null);
1352 } else {
1353 typeId = TypeManager.DOCUMENT_TYPE_ID;
1354 objectInfo.setBaseType(BaseTypeId.CMIS_DOCUMENT);
1355 objectInfo.setTypeId(typeId);
1356 objectInfo.setHasAcl(true);
1357 objectInfo.setHasContent(true);
1358 objectInfo.setHasParent(true);
1359 objectInfo.setVersionSeriesId(null);
1360 objectInfo.setIsCurrentVersion(true);
1361 objectInfo.setRelationshipSourceIds(null);
1362 objectInfo.setRelationshipTargetIds(null);
1363 objectInfo.setRenditionInfos(null);
1364 objectInfo.setSupportsDescendants(false);
1365 objectInfo.setSupportsFolderTree(false);
1366 objectInfo.setSupportsPolicies(false);
1367 objectInfo.setSupportsRelationships(false);
1368 objectInfo.setWorkingCopyId(null);
1369 objectInfo.setWorkingCopyOriginalId(null);
1370 }
1371
1372
1373 try {
1374 PropertiesImpl result = new PropertiesImpl();
1375
1376
1377 String id = fileToId(file);
1378 addPropertyId(result, typeId, filter, PropertyIds.OBJECT_ID, id);
1379 objectInfo.setId(id);
1380
1381
1382 String name = file.getName();
1383 addPropertyString(result, typeId, filter, PropertyIds.NAME, name);
1384 objectInfo.setName(name);
1385
1386
1387 addPropertyString(result, typeId, filter, PropertyIds.CREATED_BY, USER_UNKNOWN);
1388 addPropertyString(result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, USER_UNKNOWN);
1389 objectInfo.setCreatedBy(USER_UNKNOWN);
1390
1391
1392 GregorianCalendar lastModified = millisToCalendar(file.lastModified());
1393 addPropertyDateTime(result, typeId, filter, PropertyIds.CREATION_DATE, lastModified);
1394 addPropertyDateTime(result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified);
1395 objectInfo.setCreationDate(lastModified);
1396 objectInfo.setLastModificationDate(lastModified);
1397
1398
1399 addPropertyString(result, typeId, filter, PropertyIds.CHANGE_TOKEN, null);
1400
1401
1402 if (file.isDirectory()) {
1403
1404 addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
1405 addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, TypeManager.FOLDER_TYPE_ID);
1406 String path = getRepositoryPath(file);
1407 addPropertyString(result, typeId, filter, PropertyIds.PATH, (path.length() == 0 ? "/" : path));
1408
1409
1410 if (!root.equals(file)) {
1411 addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID,
1412 (root.equals(file.getParentFile()) ? ROOT_ID : fileToId(file.getParentFile())));
1413 objectInfo.setHasParent(true);
1414 } else {
1415 addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID, null);
1416 objectInfo.setHasParent(false);
1417 }
1418
1419 addPropertyIdList(result, typeId, filter, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, null);
1420 } else {
1421
1422 addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
1423 addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, TypeManager.DOCUMENT_TYPE_ID);
1424
1425
1426 addPropertyBoolean(result, typeId, filter, PropertyIds.IS_IMMUTABLE, false);
1427 addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_VERSION, true);
1428 addPropertyBoolean(result, typeId, filter, PropertyIds.IS_MAJOR_VERSION, true);
1429 addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, true);
1430 addPropertyString(result, typeId, filter, PropertyIds.VERSION_LABEL, file.getName());
1431 addPropertyId(result, typeId, filter, PropertyIds.VERSION_SERIES_ID, fileToId(file));
1432 addPropertyBoolean(result, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, false);
1433 addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null);
1434 addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null);
1435 addPropertyString(result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "");
1436
1437 if (file.length() == 0) {
1438 addPropertyBigInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, null);
1439 addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE, null);
1440 addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, null);
1441
1442 objectInfo.setHasContent(false);
1443 objectInfo.setContentType(null);
1444 objectInfo.setFileName(null);
1445 } else {
1446 addPropertyInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, file.length());
1447 addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE,
1448 MimeTypes.getMIMEType(file));
1449 addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, file.getName());
1450
1451 objectInfo.setHasContent(true);
1452 objectInfo.setContentType(MimeTypes.getMIMEType(file));
1453 objectInfo.setFileName(file.getName());
1454 }
1455
1456 addPropertyId(result, typeId, filter, PropertyIds.CONTENT_STREAM_ID, null);
1457 }
1458
1459
1460 readCustomProperties(file, result, filter, objectInfo);
1461
1462 if (filter != null) {
1463 if (!filter.isEmpty()) {
1464 debug("Unknown filter properties: " + filter.toString(), null);
1465 }
1466 }
1467
1468 return result;
1469 } catch (Exception e) {
1470 if (e instanceof CmisBaseException) {
1471 throw (CmisBaseException) e;
1472 }
1473 throw new CmisRuntimeException(e.getMessage(), e);
1474 }
1475 }
1476
1477
1478
1479
1480 @SuppressWarnings("unchecked")
1481 private void readCustomProperties(File file, PropertiesImpl properties, Set<String> filter,
1482 ObjectInfoImpl objectInfo) {
1483 File propFile = getPropertiesFile(file);
1484
1485
1486 if (!propFile.exists()) {
1487 return;
1488 }
1489
1490
1491 JAXBElement<CmisObjectType> obj = null;
1492 try {
1493 Unmarshaller u = JaxBHelper.createUnmarshaller();
1494 obj = (JAXBElement<CmisObjectType>) u.unmarshal(propFile);
1495 } catch (Exception e) {
1496 warn("Unvalid CMIS properties: " + propFile.getAbsolutePath(), e);
1497 }
1498
1499 if ((obj == null) || (obj.getValue() == null) || (obj.getValue().getProperties() == null)) {
1500 return;
1501 }
1502
1503
1504 for (CmisProperty cmisProp : obj.getValue().getProperties().getProperty()) {
1505 PropertyData<?> prop = Converter.convert(cmisProp);
1506
1507
1508 if (prop instanceof PropertyString) {
1509 String firstValueStr = ((PropertyString) prop).getFirstValue();
1510 if (PropertyIds.NAME.equals(prop.getId())) {
1511 objectInfo.setName(firstValueStr);
1512 } else if (PropertyIds.OBJECT_TYPE_ID.equals(prop.getId())) {
1513 objectInfo.setTypeId(firstValueStr);
1514 } else if (PropertyIds.CREATED_BY.equals(prop.getId())) {
1515 objectInfo.setCreatedBy(firstValueStr);
1516 } else if (PropertyIds.CONTENT_STREAM_MIME_TYPE.equals(prop.getId())) {
1517 objectInfo.setContentType(firstValueStr);
1518 } else if (PropertyIds.CONTENT_STREAM_FILE_NAME.equals(prop.getId())) {
1519 objectInfo.setFileName(firstValueStr);
1520 }
1521 }
1522
1523 if (prop instanceof PropertyDateTime) {
1524 GregorianCalendar firstValueCal = ((PropertyDateTime) prop).getFirstValue();
1525 if (PropertyIds.CREATION_DATE.equals(prop.getId())) {
1526 objectInfo.setCreationDate(firstValueCal);
1527 } else if (PropertyIds.LAST_MODIFICATION_DATE.equals(prop.getId())) {
1528 objectInfo.setLastModificationDate(firstValueCal);
1529 }
1530 }
1531
1532
1533 if (filter != null) {
1534 if (!filter.contains(prop.getId())) {
1535 continue;
1536 } else {
1537 filter.remove(prop.getId());
1538 }
1539 }
1540
1541
1542 if (PropertyIds.OBJECT_ID.equals(prop.getId())) {
1543 continue;
1544 }
1545
1546
1547 if (PropertyIds.BASE_TYPE_ID.equals(prop.getId())) {
1548 continue;
1549 }
1550
1551
1552 properties.addProperty(prop);
1553 }
1554 }
1555
1556
1557
1558
1559 private Properties compileProperties(String typeId, String creator, GregorianCalendar creationDate,
1560 String modifier, Properties properties) {
1561 PropertiesImpl result = new PropertiesImpl();
1562 Set<String> addedProps = new HashSet<String>();
1563
1564 if ((properties == null) || (properties.getProperties() == null)) {
1565 throw new CmisConstraintException("No properties!");
1566 }
1567
1568
1569 TypeDefinition type = types.getType(typeId);
1570 if (type == null) {
1571 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
1572 }
1573
1574
1575 for (PropertyData<?> prop : properties.getProperties().values()) {
1576 PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
1577
1578
1579 if (propType == null) {
1580 throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
1581 }
1582
1583
1584 if ((propType.getUpdatability() == Updatability.READONLY)) {
1585 throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
1586 }
1587
1588
1589 if (isEmptyProperty(prop)) {
1590 throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
1591 }
1592
1593
1594 result.addProperty(prop);
1595 addedProps.add(prop.getId());
1596 }
1597
1598
1599 for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values()) {
1600 if (!addedProps.contains(propDef.getId()) && (propDef.getUpdatability() != Updatability.READONLY)) {
1601 if (!addPropertyDefault(result, propDef) && propDef.isRequired()) {
1602 throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
1603 }
1604 }
1605 }
1606
1607 addPropertyId(result, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
1608 addPropertyString(result, typeId, null, PropertyIds.CREATED_BY, creator);
1609 addPropertyDateTime(result, typeId, null, PropertyIds.CREATION_DATE, creationDate);
1610 addPropertyString(result, typeId, null, PropertyIds.LAST_MODIFIED_BY, modifier);
1611
1612 return result;
1613 }
1614
1615
1616
1617
1618 private Properties updateProperties(String typeId, String creator, GregorianCalendar creationDate, String modifier,
1619 Properties oldProperties, Properties properties) {
1620 PropertiesImpl result = new PropertiesImpl();
1621
1622 if (properties == null) {
1623 throw new CmisConstraintException("No properties!");
1624 }
1625
1626
1627 TypeDefinition type = types.getType(typeId);
1628 if (type == null) {
1629 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
1630 }
1631
1632
1633 for (PropertyData<?> prop : oldProperties.getProperties().values()) {
1634 PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
1635
1636
1637 if (propType == null) {
1638 throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
1639 }
1640
1641
1642 if ((propType.getUpdatability() != Updatability.READWRITE)) {
1643 continue;
1644 }
1645
1646 result.addProperty(prop);
1647 }
1648
1649
1650 for (PropertyData<?> prop : properties.getProperties().values()) {
1651 PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
1652
1653
1654 if (propType == null) {
1655 throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
1656 }
1657
1658
1659 if ((propType.getUpdatability() == Updatability.READONLY)) {
1660 throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
1661 }
1662
1663 if ((propType.getUpdatability() == Updatability.ONCREATE)) {
1664 throw new CmisConstraintException("Property '" + prop.getId() + "' can only be set on create!");
1665 }
1666
1667
1668 if (isEmptyProperty(prop)) {
1669 addPropertyDefault(result, propType);
1670 } else {
1671 result.addProperty(prop);
1672 }
1673 }
1674
1675 addPropertyId(result, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
1676 addPropertyString(result, typeId, null, PropertyIds.CREATED_BY, creator);
1677 addPropertyDateTime(result, typeId, null, PropertyIds.CREATION_DATE, creationDate);
1678 addPropertyString(result, typeId, null, PropertyIds.LAST_MODIFIED_BY, modifier);
1679
1680 return result;
1681 }
1682
1683 private static boolean isEmptyProperty(PropertyData<?> prop) {
1684 if ((prop == null) || (prop.getValues() == null)) {
1685 return true;
1686 }
1687
1688 return prop.getValues().isEmpty();
1689 }
1690
1691 private void addPropertyId(PropertiesImpl props, String typeId, Set<String> filter, String id, String value) {
1692 if (!checkAddProperty(props, typeId, filter, id)) {
1693 return;
1694 }
1695
1696 props.addProperty(new PropertyIdImpl(id, value));
1697 }
1698
1699 private void addPropertyIdList(PropertiesImpl props, String typeId, Set<String> filter, String id,
1700 List<String> value) {
1701 if (!checkAddProperty(props, typeId, filter, id)) {
1702 return;
1703 }
1704
1705 props.addProperty(new PropertyIdImpl(id, value));
1706 }
1707
1708 private void addPropertyString(PropertiesImpl props, String typeId, Set<String> filter, String id, String value) {
1709 if (!checkAddProperty(props, typeId, filter, id)) {
1710 return;
1711 }
1712
1713 props.addProperty(new PropertyStringImpl(id, value));
1714 }
1715
1716 private void addPropertyInteger(PropertiesImpl props, String typeId, Set<String> filter, String id, long value) {
1717 addPropertyBigInteger(props, typeId, filter, id, BigInteger.valueOf(value));
1718 }
1719
1720 private void addPropertyBigInteger(PropertiesImpl props, String typeId, Set<String> filter, String id,
1721 BigInteger value) {
1722 if (!checkAddProperty(props, typeId, filter, id)) {
1723 return;
1724 }
1725
1726 props.addProperty(new PropertyIntegerImpl(id, value));
1727 }
1728
1729 private void addPropertyBoolean(PropertiesImpl props, String typeId, Set<String> filter, String id, boolean value) {
1730 if (!checkAddProperty(props, typeId, filter, id)) {
1731 return;
1732 }
1733
1734 props.addProperty(new PropertyBooleanImpl(id, value));
1735 }
1736
1737 private void addPropertyDateTime(PropertiesImpl props, String typeId, Set<String> filter, String id,
1738 GregorianCalendar value) {
1739 if (!checkAddProperty(props, typeId, filter, id)) {
1740 return;
1741 }
1742
1743 props.addProperty(new PropertyDateTimeImpl(id, value));
1744 }
1745
1746 private boolean checkAddProperty(Properties properties, String typeId, Set<String> filter, String id) {
1747 if ((properties == null) || (properties.getProperties() == null)) {
1748 throw new IllegalArgumentException("Properties must not be null!");
1749 }
1750
1751 if (id == null) {
1752 throw new IllegalArgumentException("Id must not be null!");
1753 }
1754
1755 TypeDefinition type = types.getType(typeId);
1756 if (type == null) {
1757 throw new IllegalArgumentException("Unknown type: " + typeId);
1758 }
1759 if (!type.getPropertyDefinitions().containsKey(id)) {
1760 throw new IllegalArgumentException("Unknown property: " + id);
1761 }
1762
1763 String queryName = type.getPropertyDefinitions().get(id).getQueryName();
1764
1765 if ((queryName != null) && (filter != null)) {
1766 if (!filter.contains(queryName)) {
1767 return false;
1768 } else {
1769 filter.remove(queryName);
1770 }
1771 }
1772
1773 return true;
1774 }
1775
1776
1777
1778
1779 @SuppressWarnings("unchecked")
1780 private static boolean addPropertyDefault(PropertiesImpl props, PropertyDefinition<?> propDef) {
1781 if ((props == null) || (props.getProperties() == null)) {
1782 throw new IllegalArgumentException("Props must not be null!");
1783 }
1784
1785 if (propDef == null) {
1786 return false;
1787 }
1788
1789 List<?> defaultValue = propDef.getDefaultValue();
1790 if ((defaultValue != null) && (!defaultValue.isEmpty())) {
1791 switch (propDef.getPropertyType()) {
1792 case BOOLEAN:
1793 props.addProperty(new PropertyBooleanImpl(propDef.getId(), (List<Boolean>) defaultValue));
1794 break;
1795 case DATETIME:
1796 props.addProperty(new PropertyDateTimeImpl(propDef.getId(), (List<GregorianCalendar>) defaultValue));
1797 break;
1798 case DECIMAL:
1799 props.addProperty(new PropertyDecimalImpl(propDef.getId(), (List<BigDecimal>) defaultValue));
1800 break;
1801 case HTML:
1802 props.addProperty(new PropertyHtmlImpl(propDef.getId(), (List<String>) defaultValue));
1803 break;
1804 case ID:
1805 props.addProperty(new PropertyIdImpl(propDef.getId(), (List<String>) defaultValue));
1806 break;
1807 case INTEGER:
1808 props.addProperty(new PropertyIntegerImpl(propDef.getId(), (List<BigInteger>) defaultValue));
1809 break;
1810 case STRING:
1811 props.addProperty(new PropertyStringImpl(propDef.getId(), (List<String>) defaultValue));
1812 break;
1813 case URI:
1814 props.addProperty(new PropertyUriImpl(propDef.getId(), (List<String>) defaultValue));
1815 break;
1816 default:
1817 throw new RuntimeException("Unknown datatype! Spec change?");
1818 }
1819
1820 return true;
1821 }
1822
1823 return false;
1824 }
1825
1826
1827
1828
1829 private AllowableActions compileAllowableActions(File file, boolean userReadOnly) {
1830 if (file == null) {
1831 throw new IllegalArgumentException("File must not be null!");
1832 }
1833
1834
1835 if (!file.exists()) {
1836 throw new CmisObjectNotFoundException("Object not found!");
1837 }
1838
1839 boolean isReadOnly = !file.canWrite();
1840 boolean isFolder = file.isDirectory();
1841 boolean isRoot = root.equals(file);
1842
1843 Set<Action> aas = new HashSet<Action>();
1844
1845 addAction(aas, Action.CAN_GET_OBJECT_PARENTS, !isRoot);
1846 addAction(aas, Action.CAN_GET_PROPERTIES, true);
1847 addAction(aas, Action.CAN_UPDATE_PROPERTIES, !userReadOnly && !isReadOnly);
1848 addAction(aas, Action.CAN_MOVE_OBJECT, !userReadOnly);
1849 addAction(aas, Action.CAN_DELETE_OBJECT, !userReadOnly && !isReadOnly && !isRoot);
1850 addAction(aas, Action.CAN_GET_ACL, true);
1851
1852 if (isFolder) {
1853 addAction(aas, Action.CAN_GET_DESCENDANTS, true);
1854 addAction(aas, Action.CAN_GET_CHILDREN, true);
1855 addAction(aas, Action.CAN_GET_FOLDER_PARENT, !isRoot);
1856 addAction(aas, Action.CAN_GET_FOLDER_TREE, true);
1857 addAction(aas, Action.CAN_CREATE_DOCUMENT, !userReadOnly);
1858 addAction(aas, Action.CAN_CREATE_FOLDER, !userReadOnly);
1859 addAction(aas, Action.CAN_DELETE_TREE, !userReadOnly && !isReadOnly);
1860 } else {
1861 addAction(aas, Action.CAN_GET_CONTENT_STREAM, true);
1862 addAction(aas, Action.CAN_SET_CONTENT_STREAM, !userReadOnly && !isReadOnly);
1863 addAction(aas, Action.CAN_DELETE_CONTENT_STREAM, !userReadOnly && !isReadOnly);
1864 addAction(aas, Action.CAN_GET_ALL_VERSIONS, true);
1865 }
1866
1867 AllowableActionsImpl result = new AllowableActionsImpl();
1868 result.setAllowableActions(aas);
1869
1870 return result;
1871 }
1872
1873 private static void addAction(Set<Action> aas, Action action, boolean condition) {
1874 if (condition) {
1875 aas.add(action);
1876 }
1877 }
1878
1879
1880
1881
1882 private Acl compileAcl(File file) {
1883 AccessControlListImpl result = new AccessControlListImpl();
1884 result.setAces(new ArrayList<Ace>());
1885
1886 for (Map.Entry<String, Boolean> ue : userMap.entrySet()) {
1887
1888 AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl();
1889 principal.setPrincipalId(ue.getKey());
1890
1891
1892 AccessControlEntryImpl entry = new AccessControlEntryImpl();
1893 entry.setPrincipal(principal);
1894 entry.setPermissions(new ArrayList<String>());
1895 entry.getPermissions().add(CMIS_READ);
1896 if (!ue.getValue().booleanValue() && file.canWrite()) {
1897 entry.getPermissions().add(CMIS_WRITE);
1898 entry.getPermissions().add(CMIS_ALL);
1899 }
1900
1901 entry.setDirect(true);
1902
1903
1904 result.getAces().add(entry);
1905 }
1906
1907 return result;
1908 }
1909
1910
1911
1912
1913 private static void writePropertiesFile(File file, Properties properties) {
1914 File propFile = getPropertiesFile(file);
1915
1916
1917 if ((properties == null) || (properties.getProperties() == null) || (properties.getProperties().size() == 0)) {
1918 propFile.delete();
1919 return;
1920 }
1921
1922
1923 CmisObjectType object = new CmisObjectType();
1924 object.setProperties(Converter.convert(properties));
1925
1926
1927 try {
1928 JaxBHelper.CMIS_EXTRA_OBJECT_FACTORY.createObject(object);
1929 JAXBElement<CmisObjectType> objElement = JaxBHelper.CMIS_EXTRA_OBJECT_FACTORY.createObject(object);
1930
1931 Marshaller m = JaxBHelper.createMarshaller();
1932 m.setProperty("jaxb.formatted.output", true);
1933 m.marshal(objElement, propFile);
1934 } catch (Exception e) {
1935 throw new CmisStorageException("Couldn't store properties!", e);
1936 }
1937 }
1938
1939
1940
1941
1942
1943
1944 private static GregorianCalendar millisToCalendar(long millis) {
1945 GregorianCalendar result = new GregorianCalendar();
1946 result.setTimeZone(TimeZone.getTimeZone("GMT"));
1947 result.setTimeInMillis((long) (Math.ceil(millis / 1000) * 1000));
1948
1949 return result;
1950 }
1951
1952
1953
1954
1955
1956
1957 private static Set<String> splitFilter(String filter) {
1958 if (filter == null) {
1959 return null;
1960 }
1961
1962 if (filter.trim().length() == 0) {
1963 return null;
1964 }
1965
1966 Set<String> result = new HashSet<String>();
1967 for (String s : filter.split(",")) {
1968 s = s.trim();
1969 if (s.equals("*")) {
1970 return null;
1971 } else if (s.length() > 0) {
1972 result.add(s);
1973 }
1974 }
1975
1976
1977
1978 result.add(PropertyIds.OBJECT_ID);
1979 result.add(PropertyIds.OBJECT_TYPE_ID);
1980 result.add(PropertyIds.BASE_TYPE_ID);
1981
1982 return result;
1983 }
1984
1985
1986
1987
1988 private static String getTypeId(Properties properties) {
1989 PropertyData<?> typeProperty = properties.getProperties().get(PropertyIds.OBJECT_TYPE_ID);
1990 if (!(typeProperty instanceof PropertyId)) {
1991 throw new CmisInvalidArgumentException("Type id must be set!");
1992 }
1993
1994 String typeId = ((PropertyId) typeProperty).getFirstValue();
1995 if (typeId == null) {
1996 throw new CmisInvalidArgumentException("Type id must be set!");
1997 }
1998
1999 return typeId;
2000 }
2001
2002
2003
2004
2005 private static String getIdProperty(Properties properties, String name) {
2006 PropertyData<?> property = properties.getProperties().get(name);
2007 if (!(property instanceof PropertyId)) {
2008 return null;
2009 }
2010
2011 return ((PropertyId) property).getFirstValue();
2012 }
2013
2014
2015
2016
2017 private static String getStringProperty(Properties properties, String name) {
2018 PropertyData<?> property = properties.getProperties().get(name);
2019 if (!(property instanceof PropertyString)) {
2020 return null;
2021 }
2022
2023 return ((PropertyString) property).getFirstValue();
2024 }
2025
2026
2027
2028
2029 private static GregorianCalendar getDateTimeProperty(Properties properties, String name) {
2030 PropertyData<?> property = properties.getProperties().get(name);
2031 if (!(property instanceof PropertyDateTime)) {
2032 return null;
2033 }
2034
2035 return ((PropertyDateTime) property).getFirstValue();
2036 }
2037
2038
2039
2040
2041
2042 private boolean checkUser(CallContext context, boolean writeRequired) {
2043 if (context == null) {
2044 throw new CmisPermissionDeniedException("No user context!");
2045 }
2046
2047 Boolean readOnly = userMap.get(context.getUsername());
2048 if (readOnly == null) {
2049 throw new CmisPermissionDeniedException("Unknown user!");
2050 }
2051
2052 if (readOnly.booleanValue() && writeRequired) {
2053 throw new CmisPermissionDeniedException("No write permission!");
2054 }
2055
2056 return readOnly.booleanValue();
2057 }
2058
2059
2060
2061
2062 private static File getPropertiesFile(File file) {
2063 if (file.isDirectory()) {
2064 return new File(file, SHADOW_FOLDER);
2065 }
2066
2067 return new File(file.getAbsolutePath() + SHADOW_EXT);
2068 }
2069
2070
2071
2072
2073 private File getFile(String id) {
2074 try {
2075 return idToFile(id);
2076 } catch (Exception e) {
2077 throw new CmisObjectNotFoundException(e.getMessage(), e);
2078 }
2079 }
2080
2081
2082
2083
2084
2085 private File idToFile(String id) throws Exception {
2086 if ((id == null) || (id.length() == 0)) {
2087 throw new CmisInvalidArgumentException("Id is not valid!");
2088 }
2089
2090 if (id.equals(ROOT_ID)) {
2091 return root;
2092 }
2093
2094 return new File(root, (new String(Base64.decodeBase64(id.getBytes("ISO-8859-1")), "UTF-8")).replace('/',
2095 File.separatorChar));
2096 }
2097
2098
2099
2100
2101 private String getId(File file) {
2102 try {
2103 return fileToId(file);
2104 } catch (Exception e) {
2105 throw new CmisRuntimeException(e.getMessage(), e);
2106 }
2107 }
2108
2109
2110
2111
2112
2113 private String fileToId(File file) throws Exception {
2114 if (file == null) {
2115 throw new IllegalArgumentException("File is not valid!");
2116 }
2117
2118 if (root.equals(file)) {
2119 return ROOT_ID;
2120 }
2121
2122 String path = getRepositoryPath(file);
2123
2124 return new String(Base64.encodeBase64(path.getBytes("UTF-8")), "ISO-8859-1");
2125 }
2126
2127 private String getRepositoryPath(File file) {
2128 return file.getAbsolutePath().substring(root.getAbsolutePath().length()).replace(File.separatorChar, '/');
2129 }
2130
2131 private void warn(String msg, Throwable t) {
2132 log.warn("<" + repositoryId + "> " + msg, t);
2133 }
2134
2135 private void debug(String msg) {
2136 debug(msg, null);
2137 }
2138
2139 private void debug(String msg, Throwable t) {
2140 log.debug("<" + repositoryId + "> " + msg, t);
2141 }
2142 }