/**
* Copyright (C) 2010 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xcmis.sp.inmemory;
import org.apache.tika.exception.TikaException;
import org.apache.tika.mime.MimeTypeException;
import org.xcmis.search.InvalidQueryException;
import org.xcmis.search.SearchService;
import org.xcmis.search.SearchServiceException;
import org.xcmis.search.Visitors;
import org.xcmis.search.config.IndexConfiguration;
import org.xcmis.search.config.SearchServiceConfiguration;
import org.xcmis.search.model.column.Column;
import org.xcmis.search.model.source.SelectorName;
import org.xcmis.search.parser.CmisQueryParser;
import org.xcmis.search.parser.QueryParser;
import org.xcmis.search.query.QueryExecutionException;
import org.xcmis.search.result.ScoredRow;
import org.xcmis.search.value.ToStringNameConverter;
import org.xcmis.sp.inmemory.query.CmisContentReader;
import org.xcmis.sp.inmemory.query.CmisSchema;
import org.xcmis.sp.inmemory.query.CmisSchemaTableResolver;
import org.xcmis.sp.inmemory.query.IndexListener;
import org.xcmis.spi.BaseItemsIterator;
import org.xcmis.spi.CmisConstants;
import org.xcmis.spi.CmisRuntimeException;
import org.xcmis.spi.ConstraintException;
import org.xcmis.spi.ContentStream;
import org.xcmis.spi.DocumentData;
import org.xcmis.spi.FolderData;
import org.xcmis.spi.InvalidArgumentException;
import org.xcmis.spi.ItemsIterator;
import org.xcmis.spi.LazyIterator;
import org.xcmis.spi.NameConstraintViolationException;
import org.xcmis.spi.ObjectData;
import org.xcmis.spi.ObjectDataVisitor;
import org.xcmis.spi.ObjectNotFoundException;
import org.xcmis.spi.PermissionService;
import org.xcmis.spi.PolicyData;
import org.xcmis.spi.QueryNameTypeManager;
import org.xcmis.spi.RelationshipData;
import org.xcmis.spi.RenditionManager;
import org.xcmis.spi.Storage;
import org.xcmis.spi.StorageException;
import org.xcmis.spi.TypeNotFoundException;
import org.xcmis.spi.UpdateConflictException;
import org.xcmis.spi.UserContext;
import org.xcmis.spi.VersioningException;
import org.xcmis.spi.model.ACLCapability;
import org.xcmis.spi.model.AccessControlEntry;
import org.xcmis.spi.model.AccessControlPropagation;
import org.xcmis.spi.model.AllowableActions;
import org.xcmis.spi.model.BaseType;
import org.xcmis.spi.model.CapabilityACL;
import org.xcmis.spi.model.CapabilityChanges;
import org.xcmis.spi.model.CapabilityContentStreamUpdatable;
import org.xcmis.spi.model.CapabilityJoin;
import org.xcmis.spi.model.CapabilityQuery;
import org.xcmis.spi.model.CapabilityRendition;
import org.xcmis.spi.model.ChangeEvent;
import org.xcmis.spi.model.ChangeType;
import org.xcmis.spi.model.ContentStreamAllowed;
import org.xcmis.spi.model.Permission;
import org.xcmis.spi.model.PermissionMapping;
import org.xcmis.spi.model.Property;
import org.xcmis.spi.model.PropertyDefinition;
import org.xcmis.spi.model.Rendition;
import org.xcmis.spi.model.RepositoryCapabilities;
import org.xcmis.spi.model.RepositoryInfo;
import org.xcmis.spi.model.SupportedPermissions;
import org.xcmis.spi.model.TypeDefinition;
import org.xcmis.spi.model.UnfileObject;
import org.xcmis.spi.model.Updatability;
import org.xcmis.spi.model.VersioningState;
import org.xcmis.spi.model.Permission.BasicPermissions;
import org.xcmis.spi.model.impl.StringProperty;
import org.xcmis.spi.query.Query;
import org.xcmis.spi.query.Result;
import org.xcmis.spi.query.Score;
import org.xcmis.spi.utils.CmisUtils;
import org.xcmis.spi.utils.Logger;
import org.xcmis.spi.utils.MimeType;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* In-memory implementation of xCMIS storage. This is storage is limited in
* number of items and total amount of content. Storage is not designed for high
* concurrency load. In some cases data in storage can be in inconsistency
* state.
*
* @author <a href="mailto:andrew00x@gmail.com">Andrey Parfonov</a>
* @version $Id: StorageImpl.java 804 2010-04-16 16:48:59Z
* alexey.zavizionov@gmail.com $
*/
public class StorageImpl implements Storage
{
private static final Logger LOG = Logger.getLogger(StorageImpl.class);
private static final String VENDOR_NAME = "eXo";
private static final String REPOSITORY_DESCRIPTION = "xCMIS (eXo InMemory SP)";
private static final String PRODUCT_VERSION = "1.2";
private static final String PRODUCT_NAME = "xCMIS (eXo InMemory SP)";
static final String ROOT_FOLDER_ID = "abcdef12-3456-7890-0987-654321fedcba";
static final Set<String> EMPTY_PARENTS = Collections.emptySet();
public static String generateId()
{
return UUID.randomUUID().toString();
}
final Map<String, Entry> entries;
final Map<String, Set<String>> children;
final Map<String, Set<String>> parents;
final Set<String> unfiled;
final Map<String, Set<String>> relationships;
final Map<String, List<String>> versions;
final Map<String, String> workingCopies;
final Map<String, TypeDefinition> types;
final Map<String, Set<String>> typeChildren;
final IndexListener indexListener;
/** Searche service. */
final SearchService searchService;
/** Cmis query parser. */
final QueryParser cmisQueryParser;
final List<ChangeEvent> changes;
RenditionManager renditionManager;
PermissionService permissionService;
private final RepositoryInfo repositoryInfo;
private final StorageConfiguration configuration;
public StorageImpl(StorageConfiguration configuration, RenditionManager manager,
PermissionService permissionService) throws TikaException
{
this(configuration);
this.renditionManager = manager;
this.permissionService = permissionService;
}
protected StorageImpl(StorageConfiguration configuration) throws TikaException
{
this.configuration = configuration;
this.entries = new ConcurrentHashMap<String, Entry>();
this.children = new ConcurrentHashMap<String, Set<String>>();
this.parents = new ConcurrentHashMap<String, Set<String>>();
this.versions = new ConcurrentHashMap<String, List<String>>();
this.workingCopies = new ConcurrentHashMap<String, String>();
this.unfiled = new CopyOnWriteArraySet<String>();
this.relationships = new ConcurrentHashMap<String, Set<String>>();
this.types = new ConcurrentHashMap<String, TypeDefinition>();
this.changes = new CopyOnWriteArrayList<ChangeEvent>();
PermissionMapping permissionMapping = new PermissionMapping();
permissionMapping.put(PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_FOLDER_TREE_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_CHILDREN_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_OBJECT_PARENTS_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_FOLDER_PARENT_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_CREATE_FOLDER_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_CREATE_RELATIONSHIP_SOURCE, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_CREATE_RELATIONSHIP_TARGET, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_PROPERTIES_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_CONTENT_STREAM_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_RENDITIONS_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_MOVE_OBJECT_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_MOVE_OBJECT_TARGET, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_MOVE_OBJECT_SOURCE, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_DELETE_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_DELETE_TREE_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_SET_CONTENT_DOCUMENT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_ADD_TO_FOLDER_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_ADD_TO_FOLDER_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_REMOVE_OBJECT_FROM_FOLDER_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_REMOVE_OBJECT_FROM_FOLDER_FOLDER, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_CHECKOUT_DOCUMENT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_CHECKIN_DOCUMENT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_GET_ALL_VERSIONS_DOCUMENT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_ADD_POLICY_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_ADD_POLICY_POLICY, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_REMOVE_POLICY_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_WRITE.value()));
permissionMapping.put(PermissionMapping.CAN_REMOVE_POLICY_POLICY, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_APPLIED_POLICIES_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_GET_ACL_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value()));
permissionMapping.put(PermissionMapping.CAN_APPLY_ACL_OBJECT, //
Arrays.asList(BasicPermissions.CMIS_READ.value(), BasicPermissions.CMIS_WRITE.value()));
List<Permission> supportedPermissions = new ArrayList<Permission>(4);
for (BasicPermissions b : BasicPermissions.values())
{
supportedPermissions.add(new Permission(b.value(), ""));
}
repositoryInfo =
new RepositoryInfo(getId(), getId(), ROOT_FOLDER_ID, CmisConstants.SUPPORTED_VERSION,
new RepositoryCapabilities(CapabilityACL.MANAGE, CapabilityChanges.ALL,
CapabilityContentStreamUpdatable.ANYTIME, CapabilityJoin.NONE, CapabilityQuery.BOTHCOMBINED,
CapabilityRendition.READ, false, true, true, true, false, true, true, false), new ACLCapability(
permissionMapping, Collections.unmodifiableList(supportedPermissions),
AccessControlPropagation.REPOSITORYDETERMINED, SupportedPermissions.BASIC), "anonymous", "any", Arrays
.asList(BaseType.DOCUMENT, BaseType.FOLDER, BaseType.POLICY, BaseType.RELATIONSHIP), null, false,
REPOSITORY_DESCRIPTION, VENDOR_NAME, PRODUCT_NAME, PRODUCT_VERSION, null);
types.put("cmis:document", //
new TypeDefinition("cmis:document", BaseType.DOCUMENT, "cmis:document", "cmis:document", "", null,
"cmis:document", "Cmis Document Type", true, true, true, true, true, true, true, true, null, null,
ContentStreamAllowed.ALLOWED, null));
types.put("cmis:folder", //
new TypeDefinition("cmis:folder", BaseType.FOLDER, "cmis:folder", "cmis:folder", "", null, "cmis:folder",
"Cmis Folder type", true, true, true, false, true, true, true, false, null, null,
ContentStreamAllowed.NOT_ALLOWED, null));
types.put("cmis:policy", //
new TypeDefinition("cmis:policy", BaseType.POLICY, "cmis:policy", "cmis:policy", "", null, "cmis:policy",
"Cmis Policy type", true, false, true, false, true, true, true, false, null, null,
ContentStreamAllowed.NOT_ALLOWED, null));
types.put("cmis:relationship", //
new TypeDefinition("cmis:relationship", BaseType.RELATIONSHIP, "cmis:relationship", "cmis:relationship", "",
null, "cmis:relationship", "Cmis Relationship type.", true, false, true, false, true, true, true, false,
null, null, ContentStreamAllowed.NOT_ALLOWED, null));
typeChildren = new ConcurrentHashMap<String, Set<String>>();
typeChildren.put("cmis:document", new HashSet<String>());
typeChildren.put("cmis:folder", new HashSet<String>());
typeChildren.put("cmis:policy", new HashSet<String>());
typeChildren.put("cmis:relationship", new HashSet<String>());
Map<String, Value> root = new ConcurrentHashMap<String, Value>();
root.put(CmisConstants.NAME, new StringValue(""));
root.put(CmisConstants.OBJECT_ID, new StringValue(ROOT_FOLDER_ID));
root.put(CmisConstants.OBJECT_TYPE_ID, new StringValue("cmis:folder"));
root.put(CmisConstants.BASE_TYPE_ID, new StringValue(BaseType.FOLDER.value()));
root.put(CmisConstants.CREATION_DATE, new DateValue(Calendar.getInstance()));
root.put(CmisConstants.CREATED_BY, new StringValue("system"));
root.put(CmisConstants.LAST_MODIFICATION_DATE, new DateValue(Calendar.getInstance()));
root.put(CmisConstants.LAST_MODIFIED_BY, new StringValue("system"));
Map<String, Set<String>> pm = new ConcurrentHashMap<String, Set<String>>();
Set<String> perms = new HashSet<String>();
perms.add("cmis:all");
pm.put("any", perms);
Entry rootEntry = new Entry(root, null, pm);
entries.put(rootEntry.getId(), rootEntry);
parents.put(ROOT_FOLDER_ID, EMPTY_PARENTS);
children.put(ROOT_FOLDER_ID, new CopyOnWriteArraySet<String>());
this.searchService = getInitializedSearchService();
this.indexListener = new IndexListener(searchService);
this.cmisQueryParser = new CmisQueryParser();
}
/**
* {@inheritDoc}
*/
public AllowableActions calculateAllowableActions(ObjectData object)
{
AllowableActions actions =
permissionService.calculateAllowableActions(object, getCurrentUser(), getRepositoryInfo());
return actions;
}
/**
* {@inheritDoc}
*/
public DocumentData copyDocument(DocumentData source, FolderData parent, Map<String, Property<?>> properties,
List<AccessControlEntry> acl, Collection<PolicyData> policies, VersioningState versioningState)
throws ConstraintException, NameConstraintViolationException, StorageException
{
// If name for copy is not provided then use name of source document.
TypeDefinition typeDefinition = source.getTypeDefinition();
Property<?> nameProperty = null;
if (properties == null)
{
properties = new HashMap<String, Property<?>>();
}
else
{
nameProperty = properties.get(CmisConstants.NAME);
}
String name = null;
if (nameProperty == null || nameProperty.getValues().size() == 0
|| (name = (String)nameProperty.getValues().get(0)) == null || name.length() == 0)
{
name = source.getName();
PropertyDefinition<?> namePropertyDefinition = typeDefinition.getPropertyDefinition(CmisConstants.NAME);
properties.put(namePropertyDefinition.getId(), new StringProperty(namePropertyDefinition.getId(),
namePropertyDefinition.getQueryName(), namePropertyDefinition.getLocalName(), namePropertyDefinition
.getDisplayName(), name));
}
try
{
return createDocument(parent, typeDefinition, properties, source.getContentStream(), acl, policies,
versioningState);
}
catch (IOException ioe)
{
throw new CmisRuntimeException("Unable copy content for new document. " + ioe.getMessage(), ioe);
}
}
/**
* {@inheritDoc}
*/
public DocumentData createDocument(FolderData parent, TypeDefinition typeDefinition,
Map<String, Property<?>> properties, ContentStream content, List<AccessControlEntry> acl,
Collection<PolicyData> policies, VersioningState versioningState) throws ConstraintException,
NameConstraintViolationException, IOException, StorageException
{
String name = null;
Property<?> nameProperty = properties.get(CmisConstants.NAME);
if (nameProperty != null && nameProperty.getValues().size() > 0)
{
name = (String)nameProperty.getValues().get(0);
}
if (name == null && content != null)
{
name = content.getFileName();
}
if (name == null || name.length() == 0)
{
throw new NameConstraintViolationException("Name for new document must be provided.");
}
if (parent != null)
{
for (ItemsIterator<ObjectData> iterator = parent.getChildren(null); iterator.hasNext();)
{
if (name.equals(iterator.next().getName()))
{
throw new NameConstraintViolationException("Object with name " + name
+ " already exists in parent folder.");
}
}
}
Entry docEntry = new Entry();
docEntry.setValue(CmisConstants.OBJECT_TYPE_ID, new StringValue(typeDefinition.getId()));
docEntry.setValue(CmisConstants.BASE_TYPE_ID, new StringValue(typeDefinition.getBaseId().value()));
docEntry.setValue(CmisConstants.IS_IMMUTABLE, new BooleanValue(false));
String docId = StorageImpl.generateId();
String verSerId = StorageImpl.generateId();
docEntry.setValue(CmisConstants.OBJECT_ID, new StringValue(docId));
docEntry.setValue(CmisConstants.VERSION_SERIES_ID, new StringValue(verSerId));
String userId = getCurrentUser();
docEntry.setValue(CmisConstants.CREATED_BY, new StringValue(userId));
docEntry.setValue(CmisConstants.LAST_MODIFIED_BY, new StringValue(userId));
Calendar cal = Calendar.getInstance();
docEntry.setValue(CmisConstants.CREATION_DATE, new DateValue(cal));
docEntry.setValue(CmisConstants.LAST_MODIFICATION_DATE, new DateValue(cal));
docEntry.setValue(CmisConstants.IS_LATEST_VERSION, new BooleanValue(true));
docEntry.setValue(CmisConstants.IS_MAJOR_VERSION, new BooleanValue(versioningState == VersioningState.MAJOR));
docEntry.setValue(CmisConstants.IS_LATEST_MAJOR_VERSION, new BooleanValue(
versioningState == VersioningState.MAJOR));
// TODO : support for checked-out initial state
docEntry.setValue(CmisConstants.VERSION_LABEL, new StringValue(PropertyDefinitions.LATEST_LABEL));
docEntry.setValue(CmisConstants.IS_VERSION_SERIES_CHECKED_OUT, new BooleanValue(false));
// docEntry.setValue(CmisConstants.VERSION_SERIES_CHECKED_OUT_ID, new StringValue());
// docEntry.setValue(CmisConstants.VERSION_SERIES_CHECKED_OUT_BY, new StringValue());
if (content != null)
{
ByteArrayValue cv = ByteArrayValue.fromStream(content.getStream());
docEntry.setValue(PropertyDefinitions.CONTENT, cv);
MimeType mimeType = content.getMediaType();
docEntry.setValue(CmisConstants.CONTENT_STREAM_MIME_TYPE, new StringValue(mimeType.getBaseType()));
String charset = mimeType.getParameter(CmisConstants.CHARSET);
if (charset != null)
{
docEntry.setValue(CmisConstants.CHARSET, new StringValue(charset));
}
docEntry.setValue(CmisConstants.CONTENT_STREAM_LENGTH, new IntegerValue(BigInteger
.valueOf(cv.getBytes().length)));
docEntry.setValue(CmisConstants.CONTENT_STREAM_ID, new StringValue(docId));
Property<?> contentFileNameProperty = properties.get(CmisConstants.CONTENT_STREAM_FILE_NAME);
if (contentFileNameProperty == null || contentFileNameProperty.getValues().isEmpty())
{
docEntry.setValue(CmisConstants.CONTENT_STREAM_FILE_NAME, new StringValue(name));
}
else
{
String value = (String)contentFileNameProperty.getValues().get(0);
docEntry.setValue(CmisConstants.CONTENT_STREAM_FILE_NAME, new StringValue(value));
}
}
for (Property<?> property : properties.values())
{
PropertyDefinition<?> definition = typeDefinition.getPropertyDefinition(property.getId());
Updatability updatability = definition.getUpdatability();
if (updatability == Updatability.READWRITE || updatability == Updatability.ONCREATE)
{
docEntry.setProperty(property);
}
}
if (policies != null && policies.size() > 0)
{
for (PolicyData policy : policies)
{
docEntry.addPolicy(policy);
}
}
if (acl != null && acl.size() > 0)
{
CmisUtils.addAclToPermissionMap(docEntry.getPermissions(), acl);
}
if (parent != null)
{
children.get(parent.getObjectId()).add(docId);
Set<String> set = new CopyOnWriteArraySet<String>();
set.add(parent.getObjectId());
parents.put(docId, set);
}
else
{
unfiled.add(docId);
parents.put(docId, new CopyOnWriteArraySet<String>());
}
List<String> set = new CopyOnWriteArrayList<String>();
set.add(docId);
versions.put(verSerId, set);
entries.put(docId, docEntry);
DocumentDataImpl document = new DocumentDataImpl(docEntry, typeDefinition, this);
indexListener.created(document);
changes.add(new ChangeEvent(generateId(), docId, ChangeType.CREATED, (Calendar)cal.clone()));
return document;
}
/**
* {@inheritDoc}
*/
public FolderData createFolder(FolderData parent, TypeDefinition typeDefinition,
Map<String, Property<?>> properties, List<AccessControlEntry> acl, Collection<PolicyData> policies)
throws ConstraintException, NameConstraintViolationException, StorageException
{
if (parent == null)
{
throw new ConstraintException("Parent folder must be provided.");
}
String name = null;
Property<?> nameProperty = properties.get(CmisConstants.NAME);
if (nameProperty != null && nameProperty.getValues().size() > 0)
{
name = (String)nameProperty.getValues().get(0);
}
if (name == null || name.length() == 0)
{
throw new NameConstraintViolationException("Name for new folder must be provided.");
}
for (ItemsIterator<ObjectData> iterator = parent.getChildren(null); iterator.hasNext();)
{
if (name.equals(iterator.next().getName()))
{
throw new NameConstraintViolationException("Object with name " + name + " already exists in parent folder.");
}
}
Entry folderEntry = new Entry();
folderEntry.setValue(CmisConstants.OBJECT_TYPE_ID, new StringValue(typeDefinition.getId()));
folderEntry.setValue(CmisConstants.BASE_TYPE_ID, new StringValue(typeDefinition.getBaseId().value()));
String folderId = StorageImpl.generateId();
folderEntry.setValue(CmisConstants.OBJECT_ID, new StringValue(folderId));
String userId = getCurrentUser();
folderEntry.setValue(CmisConstants.CREATED_BY, new StringValue(userId));
folderEntry.setValue(CmisConstants.LAST_MODIFIED_BY, new StringValue(userId));
Calendar cal = Calendar.getInstance();
folderEntry.setValue(CmisConstants.CREATION_DATE, new DateValue(cal));
folderEntry.setValue(CmisConstants.LAST_MODIFICATION_DATE, new DateValue(cal));
for (Property<?> property : properties.values())
{
PropertyDefinition<?> definition = typeDefinition.getPropertyDefinition(property.getId());
Updatability updatability = definition.getUpdatability();
if (updatability == Updatability.READWRITE || updatability == Updatability.ONCREATE)
{
folderEntry.setProperty(property);
}
}
if (policies != null && policies.size() > 0)
{
for (PolicyData policy : policies)
{
folderEntry.addPolicy(policy);
}
}
if (acl != null && acl.size() > 0)
{
CmisUtils.addAclToPermissionMap(folderEntry.getPermissions(), acl);
}
children.get(parent.getObjectId()).add(folderId);
Set<String> set = new CopyOnWriteArraySet<String>();
set.add(parent.getObjectId());
parents.put(folderId, set);
entries.put(folderId, folderEntry);
children.put(folderId, new CopyOnWriteArraySet<String>());
FolderDataImpl folder = new FolderDataImpl(folderEntry, typeDefinition, this);
indexListener.created(folder);
changes.add(new ChangeEvent(generateId(), folderId, ChangeType.CREATED, (Calendar)cal.clone()));
return folder;
}
/**
* {@inheritDoc}
*/
public PolicyData createPolicy(FolderData parent, TypeDefinition typeDefinition,
Map<String, Property<?>> properties, List<AccessControlEntry> acl, Collection<PolicyData> policies)
throws ConstraintException, NameConstraintViolationException, StorageException
{
String name = null;
Property<?> nameProperty = properties.get(CmisConstants.NAME);
if (nameProperty != null && nameProperty.getValues().size() > 0)
{
name = (String)nameProperty.getValues().get(0);
}
if (name == null || name.length() == 0)
{
throw new NameConstraintViolationException("Name for new policy must be provided.");
}
for (Entry next : entries.values())
{
String typeId = next.getTypeId();
try
{
TypeDefinition type = getTypeDefinition(typeId, false);
if (type.getBaseId() == BaseType.POLICY)
{
String name1 = next.getValue(CmisConstants.NAME).getStrings()[0];
if (name.equals(name1))
{
throw new NameConstraintViolationException("Policy with name " + name + " already exists.");
}
}
}
catch (TypeNotFoundException e)
{
// Should never thrown thins we have objects of this type.
}
}
Entry policyEntry = new Entry();
policyEntry.setValue(CmisConstants.OBJECT_TYPE_ID, new StringValue(typeDefinition.getId()));
policyEntry.setValue(CmisConstants.BASE_TYPE_ID, new StringValue(typeDefinition.getBaseId().value()));
String policyId = StorageImpl.generateId();
policyEntry.setValue(CmisConstants.OBJECT_ID, new StringValue(policyId));
String userId = getCurrentUser();
policyEntry.setValue(CmisConstants.CREATED_BY, new StringValue(userId));
policyEntry.setValue(CmisConstants.LAST_MODIFIED_BY, new StringValue(userId));
Calendar cal = Calendar.getInstance();
policyEntry.setValue(CmisConstants.CREATION_DATE, new DateValue(cal));
policyEntry.setValue(CmisConstants.LAST_MODIFICATION_DATE, new DateValue(cal));
for (Property<?> property : properties.values())
{
PropertyDefinition<?> definition = typeDefinition.getPropertyDefinition(property.getId());
Updatability updatability = definition.getUpdatability();
if (updatability == Updatability.READWRITE || updatability == Updatability.ONCREATE)
{
policyEntry.setProperty(property);
}
}
if (policies != null && policies.size() > 0)
{
for (PolicyData policy : policies)
{
policyEntry.addPolicy(policy);
}
}
if (acl != null && acl.size() > 0)
{
CmisUtils.addAclToPermissionMap(policyEntry.getPermissions(), acl);
}
parents.put(policyId, EMPTY_PARENTS);
entries.put(policyId, policyEntry);
PolicyDataImpl policy = new PolicyDataImpl(policyEntry, typeDefinition, this);
indexListener.created(policy);
changes.add(new ChangeEvent(generateId(), policyId, ChangeType.CREATED, (Calendar)cal.clone()));
return policy;
}
/**
* {@inheritDoc}
*/
public RelationshipData createRelationship(ObjectData source, ObjectData target, TypeDefinition typeDefinition,
Map<String, Property<?>> properties, List<AccessControlEntry> acl, Collection<PolicyData> policies)
throws NameConstraintViolationException, StorageException
{
String name = null;
Property<?> nameProperty = properties.get(CmisConstants.NAME);
if (nameProperty != null && nameProperty.getValues().size() > 0)
{
name = (String)nameProperty.getValues().get(0);
}
if (name == null || name.length() == 0)
{
throw new NameConstraintViolationException("Name for new relationship must be provided.");
}
for (Entry next : entries.values())
{
String typeId = next.getTypeId();
try
{
TypeDefinition type = getTypeDefinition(typeId, false);
if (type.getBaseId() == BaseType.RELATIONSHIP)
{
String name1 = next.getValue(CmisConstants.NAME).getStrings()[0];
if (name.equals(name1))
{
throw new NameConstraintViolationException("Relationship with name " + name + " already exists.");
}
}
}
catch (TypeNotFoundException e)
{
// Should never thrown thins we have objects of this type.
}
}
Entry relationshipEntry = new Entry();
relationshipEntry.setValue(CmisConstants.OBJECT_TYPE_ID, new StringValue(typeDefinition.getId()));
relationshipEntry.setValue(CmisConstants.BASE_TYPE_ID, new StringValue(typeDefinition.getBaseId().value()));
String relationshipId = StorageImpl.generateId();
relationshipEntry.setValue(CmisConstants.OBJECT_ID, new StringValue(relationshipId));
String userId = getCurrentUser();
relationshipEntry.setValue(CmisConstants.CREATED_BY, new StringValue(userId));
relationshipEntry.setValue(CmisConstants.LAST_MODIFIED_BY, new StringValue(userId));
Calendar cal = Calendar.getInstance();
relationshipEntry.setValue(CmisConstants.CREATION_DATE, new DateValue(cal));
relationshipEntry.setValue(CmisConstants.LAST_MODIFICATION_DATE, new DateValue(cal));
relationshipEntry.setValue(CmisConstants.SOURCE_ID, new StringValue(source.getObjectId()));
relationshipEntry.setValue(CmisConstants.TARGET_ID, new StringValue(target.getObjectId()));
for (Property<?> property : properties.values())
{
PropertyDefinition<?> definition = typeDefinition.getPropertyDefinition(property.getId());
Updatability updatability = definition.getUpdatability();
if (updatability == Updatability.READWRITE || updatability == Updatability.ONCREATE)
{
relationshipEntry.setProperty(property);
}
}
if (policies != null && policies.size() > 0)
{
for (PolicyData policy : policies)
{
relationshipEntry.addPolicy(policy);
}
}
if (acl != null && acl.size() > 0)
{
CmisUtils.addAclToPermissionMap(relationshipEntry.getPermissions(), acl);
}
parents.put(relationshipId, EMPTY_PARENTS);
entries.put(relationshipId, relationshipEntry);
Set<String> sourceRels = relationships.get(source.getObjectId());
if (sourceRels == null)
{
sourceRels = new CopyOnWriteArraySet<String>();
relationships.put(source.getObjectId(), sourceRels);
}
sourceRels.add(relationshipId);
Set<String> targetRels = relationships.get(target.getObjectId());
if (targetRels == null)
{
targetRels = new CopyOnWriteArraySet<String>();
relationships.put(target.getObjectId(), targetRels);
}
targetRels.add(relationshipId);
RelationshipDataImpl relationship = new RelationshipDataImpl(relationshipEntry, typeDefinition, this);
indexListener.created(relationship);
changes.add(new ChangeEvent(generateId(), relationshipId, ChangeType.CREATED, (Calendar)cal.clone()));
return relationship;
}
/**
* {@inheritDoc}
*/
public void deleteObject(ObjectData object, boolean deleteAllVersions) throws VersioningException,
UpdateConflictException, StorageException
{
// TODO support for 'deleteAllVersions'
String objectId = object.getObjectId();
((BaseObjectData)object).delete();
Set<String> removed = new HashSet<String>();
removed.add(objectId);
indexListener.removed(removed);
changes.add(new ChangeEvent(generateId(), objectId, ChangeType.DELETED, Calendar.getInstance()));
}
/**
* {@inheritDoc}
*/
public Collection<String> deleteTree(FolderData folder, boolean deleteAllVersions, UnfileObject unfileObject,
boolean continueOnFailure) throws UpdateConflictException
{
if (!deleteAllVersions)
{
// Throw exception to avoid unexpected removing data. Any way at the
// moment we are not able remove 'base version' of versionable node,
// so have not common behavior for removing just one version of document.
throw new CmisRuntimeException("Unable delete only specified version.");
}
String folderId = folder.getObjectId();
TreeVisitor visitor = new TreeVisitor();
folder.accept(visitor);
Set<String> removed = new HashSet<String>();
for (BaseObjectData o : visitor.items)
{
try
{
String id = o.getObjectId();
o.delete();
removed.add(id);
}
catch (Exception e)
{
if (LOG.isDebugEnabled())
{
LOG.warn("Unable delete object " + o.getObjectId());
}
if (!continueOnFailure)
{
break;
}
}
}
indexListener.removed(removed);
for (String id : removed)
{
changes.add(new ChangeEvent(generateId(), id, ChangeType.DELETED, Calendar.getInstance()));
}
try
{
folder = (FolderData)getObjectById(folderId);
// If not deleted then traversing one more time.
visitor = new TreeVisitor();
folder.accept(visitor);
List<String> failedToDelete = new ArrayList<String>(visitor.items.size());
for (BaseObjectData o : visitor.items)
{
failedToDelete.add(o.getObjectId());
}
return failedToDelete;
}
catch (ObjectNotFoundException e)
{
// Tree removed.
}
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
public Collection<DocumentData> getAllVersions(String versionSeriesId) throws ObjectNotFoundException
{
List<DocumentData> v = new ArrayList<DocumentData>();
if (!workingCopies.containsKey(versionSeriesId) && !versions.containsKey(versionSeriesId))
{
throw new ObjectNotFoundException("Version series '" + versionSeriesId + "' does not exist.");
}
String pwc = workingCopies.get(versionSeriesId);
if (pwc != null)
{
v.add((DocumentData)getObjectById(pwc));
}
for (String vId : versions.get(versionSeriesId))
{
v.add((DocumentData)getObjectById(vId));
}
Collections.reverse(v);
return v;
}
/**
* {@inheritDoc}
*/
public ItemsIterator<ChangeEvent> getChangeLog(String changeLogToken) throws ConstraintException
{
if (changeLogToken != null)
{
int offset = 0;
int size = changes.size();
for (Iterator<ChangeEvent> iter = changes.iterator(); iter.hasNext()
&& !changeLogToken.equals(iter.next().getLogToken());)
{
offset++;
}
if (offset == size)
{
throw new ConstraintException("No event corresponded to change log token " + changeLogToken);
}
return new BaseItemsIterator<ChangeEvent>(changes.subList(offset, size));
}
return new BaseItemsIterator<ChangeEvent>(changes);
}
/**
* {@inheritDoc}
*/
public ItemsIterator<DocumentData> getCheckedOutDocuments(FolderData folder, String orderBy)
{
List<DocumentData> checkedOut = new ArrayList<DocumentData>();
for (String pwcId : workingCopies.values())
{
DocumentData pwc = null;
try
{
pwc = (DocumentData)getObjectById(pwcId);
}
catch (ObjectNotFoundException e)
{
LOG.warn("Object " + pwcId + " not found.");
continue;
}
if (folder != null)
{
for (FolderData parent : pwc.getParents())
{
if (parent.getObjectId().equals(folder.getObjectId()))
{
checkedOut.add(pwc);
}
}
}
else
{
checkedOut.add(pwc);
}
}
return new BaseItemsIterator<DocumentData>(checkedOut);
}
/**
* {@inheritDoc}
*/
public String getId()
{
return configuration.getId();
}
/**
* {@inheritDoc}
*/
public ObjectData getObjectById(String objectId) throws ObjectNotFoundException
{
Entry entry = entries.get(objectId);
if (entry == null)
{
throw new ObjectNotFoundException("Object '" + objectId + "' does not exist.");
}
BaseType baseType = entry.getBaseTypeId();
String typeId = entry.getTypeId();
TypeDefinition typeDefinition = null;
try
{
typeDefinition = getTypeDefinition(typeId, true);
}
catch (TypeNotFoundException e)
{
throw new CmisRuntimeException(e.getMessage(), e);
}
switch (baseType)
{
case DOCUMENT :
return new DocumentDataImpl(entry, typeDefinition, this);
case FOLDER :
return new FolderDataImpl(entry, typeDefinition, this);
case POLICY :
return new PolicyDataImpl(entry, typeDefinition, this);
case RELATIONSHIP :
return new RelationshipDataImpl(entry, typeDefinition, this);
}
// Must never happen.
throw new CmisRuntimeException("Unknown base type. ");
}
/**
* {@inheritDoc}
*/
public ObjectData getObjectByPath(String path) throws ObjectNotFoundException
{
if (!path.startsWith("/"))
{
path = "/" + path;
}
StringTokenizer tokenizer = new StringTokenizer(path, "/");
String point = StorageImpl.ROOT_FOLDER_ID;
while (tokenizer.hasMoreTokens())
{
if (point == null)
{
break;
}
String segName = tokenizer.nextToken();
Set<String> childrenIds = children.get(point);
if (childrenIds == null || childrenIds.isEmpty())
{
point = null;
}
else
{
for (String id : childrenIds)
{
ObjectData seg = getObjectById(id);
String name = seg.getName();
if ((BaseType.FOLDER == seg.getBaseType() || !tokenizer.hasMoreElements()) && segName.equals(name))
{
point = id;
break;
}
point = null;
}
}
}
if (point == null)
{
throw new ObjectNotFoundException("Path '" + path + "' not found.");
}
return getObjectById(point);
}
/**
* {@inheritDoc}
*/
public ItemsIterator<Rendition> getRenditions(ObjectData object)
{
if (renditionManager != null)
{
ItemsIterator<Rendition> renditions = renditionManager.getRenditions(object);
return renditions;
}
return CmisUtils.emptyItemsIterator();
}
/**
* {@inheritDoc}
*/
public RepositoryInfo getRepositoryInfo()
{
int size = changes.size();
if (size > 0)
{
ChangeEvent event = changes.get(size - 1);
repositoryInfo.setLatestChangeLogToken(event.getLogToken());
}
// TODO clone repositoryInfo
return repositoryInfo;
}
/**
* {@inheritDoc}
*/
public ObjectData moveObject(ObjectData object, FolderData target, FolderData source)
throws UpdateConflictException, VersioningException, NameConstraintViolationException, StorageException
{
String name = object.getName();
for (ItemsIterator<ObjectData> iterator = target.getChildren(null); iterator.hasNext();)
{
if (name.equals(iterator.next().getName()))
{
throw new NameConstraintViolationException("Object with name " + name
+ " already exists in destination folder.");
}
}
String objectid = object.getObjectId();
String sourceId = source.getObjectId();
String targetId = target.getObjectId();
children.get(sourceId).remove(objectid);
children.get(targetId).add(objectid);
parents.get(object.getObjectId()).remove(sourceId);
parents.get(object.getObjectId()).add(targetId);
try
{
object = getObjectById(objectid);
}
catch (ObjectNotFoundException e)
{
throw new CmisRuntimeException("Unable get object after moving.");
}
indexListener.updated(object);
return object;
}
/**
* {@inheritDoc}
*/
public ItemsIterator<Result> query(Query query) throws InvalidArgumentException
{
try
{
org.xcmis.search.model.Query qom = cmisQueryParser.parseQuery(query.getStatement());
List<ScoredRow> rows = searchService.execute(qom);
//check if needed default sorting
if (qom.getOrderings().size() == 0)
{
Set<SelectorName> selectorsReferencedBy = Visitors.getSelectorsReferencedBy(qom);
Collections.sort(rows, new DocumentOrderResultSorter(selectorsReferencedBy.iterator().next().getName(),
this));
}
return new QueryResultIterator(rows, qom);
}
catch (InvalidQueryException e)
{
throw new InvalidArgumentException(e.getLocalizedMessage(), e);
}
catch (QueryExecutionException e)
{
throw new CmisRuntimeException(e.getLocalizedMessage(), e);
}
}
/**
* {@inheritDoc}
*/
public void unfileObject(ObjectData object)
{
String objectId = object.getObjectId();
Set<String> parentIds = parents.get(object.getObjectId());
for (String id : parentIds)
{
children.get(id).remove(objectId);
}
parentIds.clear();
unfiled.add(objectId);
indexListener.updated(object);
}
/**
* {@inheritDoc}
*/
public Iterator<String> getUnfiledObjectsId() throws StorageException
{
return unfiled.iterator();
}
/**
* {@inheritDoc}
*/
public String addType(TypeDefinition type) throws ConstraintException, StorageException
{
if (types.get(type.getId()) != null)
{
throw new ConstraintException("Type " + type.getId() + " already exists.");
}
// check duplicate for queryName object type attribute.
for (TypeDefinition t : types.values())
{
if(type.getQueryName() == null) break;
if(t.getQueryName() == null) continue;
if (t.getQueryName().equals(type.getQueryName()))
{
throw new ConstraintException("Duplicate queryName for types " + t.getId() + " and " + type.getId());
}
}
if (type.getBaseId() == null)
{
throw new ConstraintException("Base type id must be specified.");
}
if (type.getParentId() == null)
{
throw new ConstraintException("Unable add root type. Parent type id must be specified");
}
TypeDefinition superType;
try
{
superType = getTypeDefinition(type.getParentId(), true);
}
catch (TypeNotFoundException e)
{
throw new ConstraintException("Specified parent type '" + type.getParentId() + "' does not exist.");
}
// Check new type does not use known property IDs.
if (type.getPropertyDefinitions() != null)
{
for (PropertyDefinition<?> newDefinition : type.getPropertyDefinitions())
{
PropertyDefinition<?> definition = superType.getPropertyDefinition(newDefinition.getId());
if (definition != null)
{
throw new ConstraintException("Property " + newDefinition.getId() + " already defined");
}
}
}
Map<String, PropertyDefinition<?>> m = new HashMap<String, PropertyDefinition<?>>();
for (PropertyDefinition<?> next : superType.getPropertyDefinitions())
{
m.put(next.getId(), next);
}
if (type.getPropertyDefinitions() != null)
{
for (PropertyDefinition<?> next : type.getPropertyDefinitions())
{
m.put(next.getId(), next);
}
}
types.put(type.getId(), type);
typeChildren.get(superType.getId()).add(type.getId());
typeChildren.put(type.getId(), new HashSet<String>());
PropertyDefinitions.putAll(type.getId(), m);
return type.getId();
}
/**
* {@inheritDoc}
*/
public ItemsIterator<TypeDefinition> getTypeChildren(String typeId, boolean includePropertyDefinitions)
throws TypeNotFoundException, CmisRuntimeException
{
List<TypeDefinition> types = new ArrayList<TypeDefinition>();
if (typeId == null)
{
for (String t : new String[]{"cmis:document", "cmis:folder", "cmis:policy", "cmis:relationship"})
{
types.add(getTypeDefinition(t, includePropertyDefinitions));
}
}
else
{
if (this.types.get(typeId) == null)
{
throw new TypeNotFoundException("Type '" + typeId + "' does not exist.");
}
for (String t : typeChildren.get(typeId))
{
types.add(getTypeDefinition(t, includePropertyDefinitions));
}
}
return new BaseItemsIterator<TypeDefinition>(types);
}
/**
* {@inheritDoc}
*/
public TypeDefinition getTypeDefinition(String typeId, boolean includePropertyDefinition)
throws TypeNotFoundException, CmisRuntimeException
{
TypeDefinition type = types.get(typeId);
if (type == null)
{
throw new TypeNotFoundException("Type '" + typeId + "' does not exist.");
}
TypeDefinition copy =
new TypeDefinition(type.getId(), type.getBaseId(), type.getQueryName(), type.getLocalName(), type
.getLocalNamespace(), type.getParentId(), type.getDisplayName(), type.getDescription(), type.isCreatable(),
type.isFileable(), type.isQueryable(), type.isFulltextIndexed(), type.isIncludedInSupertypeQuery(), type
.isControllablePolicy(), type.isControllableACL(), type.isVersionable(), type.getAllowedSourceTypes(),
type.getAllowedTargetTypes(), type.getContentStreamAllowed(), includePropertyDefinition
? PropertyDefinitions.getAll(typeId) : null);
return copy;
}
public Collection<TypeDefinition> getSubTypes(String typeId, boolean includePropertyDefinitions)
throws TypeNotFoundException
{
List<TypeDefinition> subTypes = new ArrayList<TypeDefinition>();
for (ItemsIterator<TypeDefinition> children = getTypeChildren(typeId, includePropertyDefinitions); children
.hasNext();)
{
TypeDefinition type = children.next();
subTypes.add(type);
Collection<TypeDefinition> cchildren = getSubTypes(type.getId(), includePropertyDefinitions);
if (cchildren.size() > 0)
{
subTypes.addAll(cchildren);
}
}
return subTypes;
}
/**
* {@inheritDoc}
*/
public void removeType(String typeId) throws TypeNotFoundException, StorageException, ConstraintException
{
TypeDefinition type = types.get(typeId);
if (type == null)
{
throw new TypeNotFoundException("Type '" + typeId + "' does not exist.");
}
if (type.getParentId() == null)
{
throw new ConstraintException("Unable remove root type " + typeId);
}
if (typeChildren.get(typeId).size() > 0)
{
throw new ConstraintException("Unable remove type " + typeId + ". Type has descendant types.");
}
for (Entry entry : entries.values())
{
if (typeId.equals(entry.getTypeId()))
{
throw new ConstraintException("Unable remove type definition if at least one object of this type exists.");
}
}
types.remove(typeId);
typeChildren.get(type.getParentId()).remove(typeId);
PropertyDefinitions.removeAll(typeId);
}
protected String getCurrentUser()
{
UserContext ctx = UserContext.getCurrent();
return ctx != null ? ctx.getUserId() : getRepositoryInfo().getPrincipalAnonymous();
}
void validateMaxItemsNumber(ObjectData object) throws StorageException
{
long maxItemsNum = configuration.getMaxItemsNum();
if (maxItemsNum > -1 && entries.size() > maxItemsNum)
{
throw new StorageException("Unable add new object in storage. Max number '" + maxItemsNum
+ "' of items is reached."
+ " Increase or set storage configuration property 'org.xcmis.inmemory.maxitems'.");
}
}
void validateMemSize(byte[] content) throws StorageException
{
if (content == null || content.length == 0)
{
return;
}
long size = 0;
for (Entry c : entries.values())
{
ByteArrayValue contentValue = (ByteArrayValue)c.getValue(PropertyDefinitions.CONTENT);
if (contentValue != null)
{
size += contentValue.getBytes().length;
}
}
long maxMem = configuration.getMaxMem();
if (maxMem > -1 && size + content.length > maxMem)
{
throw new StorageException("Unable add new object in storage. Max allowed memory size '" + maxMem
+ "' bytes is reached." + " Increase or set storage configuration property 'org.xcmis.inmemory.maxmem'.");
}
}
private SearchService getInitializedSearchService() throws TikaException
{
try
{
IndexConfiguration indexConfiguration = new IndexConfiguration(getRepositoryInfo().getRootFolderId());
QueryNameTypeManager typeManager = new QueryNameTypeManager(this);
CmisSchema schema = new CmisSchema(typeManager);
CmisSchemaTableResolver tableResolver = new CmisSchemaTableResolver(
new ToStringNameConverter(), schema, typeManager);
SearchServiceConfiguration searchConfiguration =
new SearchServiceConfiguration(schema, tableResolver, new CmisContentReader(this), indexConfiguration);
SearchService searchService = new SearchService(searchConfiguration);
searchService.start();
return searchService;
}
catch (SearchServiceException e)
{
LOG.error("Unable to initialize storage. ", e);
}
catch (MimeTypeException e)
{
LOG.error("Unable to initialize storage. ", e);
}
catch (IOException e)
{
LOG.error("Unable to initialize storage. ", e);
}
return null;
}
// -------------------------------------------------------
private class DocumentOrderResultSorter implements Comparator<ScoredRow>
{
/** The selector name. */
private final String selectorName;
private final Map<String, ObjectData> itemCache;
private final Storage storage;
DocumentOrderResultSorter(final String selectorName, Storage storage)
{
this.selectorName = selectorName;
this.storage = storage;
this.itemCache = new HashMap<String, ObjectData>();
}
/**
* {@inheritDoc}
*/
public int compare(ScoredRow o1, ScoredRow o2)
{
if (o1.equals(o2))
{
return 0;
}
final String path1 = getPath(o1.getNodeIdentifer(selectorName));
final String path2 = getPath(o2.getNodeIdentifer(selectorName));
// TODO should be checked
if (path1 == null || path2 == null)
{
return 0;
}
return path1.compareTo(path2);
}
/**
* Return comparable location of the object
*
* @param identifer
* @return
*/
public String getPath(String identifer)
{
ObjectData obj = itemCache.get(identifer);
if (obj == null)
{
try
{
obj = storage.getObjectById(identifer);
}
catch (ObjectNotFoundException e)
{
// XXX : correct ?
return null;
}
itemCache.put(identifer, obj);
}
if (obj.getBaseType() == BaseType.FOLDER)
{
if (((FolderData)obj).isRoot())
{
return obj.getName();
}
}
Collection<FolderData> parents = obj.getParents();
if (parents.size() == 0)
{
return obj.getName();
}
return parents.iterator().next().getPath() + "/" + obj.getName();
}
}
/**
* Single row from query result.
*/
private class ResultImpl implements Result
{
private final String id;
private final String[] properties;
private final Score score;
ResultImpl(String id, String[] properties, Score score)
{
this.id = id;
this.properties = properties;
this.score = score;
}
public String[] getPropertyNames()
{
return properties;
}
public String getObjectId()
{
return id;
}
public Score getScore()
{
return score;
}
}
/**
* Iterator over query result's.
*/
private class QueryResultIterator extends LazyIterator<Result>
{
private final Iterator<ScoredRow> rows;
private final Set<SelectorName> selectors;
private final int size;
private final org.xcmis.search.model.Query qom;
QueryResultIterator(List<ScoredRow> rows, org.xcmis.search.model.Query qom)
{
this.size = rows.size();
this.rows = rows.iterator();
this.selectors = Visitors.getSelectorsReferencedBy(qom);
this.qom = qom;
fetchNext();
}
/**
* {@inheritDoc}
*/
public int size()
{
return size;
}
/**
* To fetch next <code>Result</code>.
*/
protected void fetchNext()
{
next = null;
while (next == null && rows.hasNext())
{
ScoredRow row = rows.next();
for (SelectorName selectorName : selectors)
{
String objectId = row.getNodeIdentifer(selectorName.getName());
List<String> properties = null;
Score score = null;
for (Column column : qom.getColumns())
{
//TODO check
if (column.isFunction())
{
score = new Score(column.getColumnName(), BigDecimal.valueOf(row.getScore()));
}
else
{
if (selectorName.getName().equals(column.getSelectorName()))
{
if (column.getPropertyName() != null)
{
if (properties == null)
{
properties = new ArrayList<String>();
}
properties.add(column.getPropertyName());
}
}
}
}
next =
new ResultImpl(objectId, properties == null ? null : properties
.toArray(new String[properties.size()]), score);
}
}
}
}
private class TreeVisitor implements ObjectDataVisitor
{
private final Collection<BaseObjectData> items = new LinkedHashSet<BaseObjectData>();
public void visit(ObjectData object)
{
TypeDefinition type = object.getTypeDefinition();
if (type.getBaseId() == BaseType.FOLDER)
{
for (ItemsIterator<ObjectData> children = ((FolderDataImpl)object).getChildren(null); children.hasNext();)
{
children.next().accept(this);
}
items.add((BaseObjectData)object);
}
else
{
items.add((BaseObjectData)object);
}
}
}
}