001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.cmis;
029
030import static org.opencms.cmis.CmsCmisUtil.checkResourceName;
031import static org.opencms.cmis.CmsCmisUtil.ensureLock;
032import static org.opencms.cmis.CmsCmisUtil.handleCmsException;
033import static org.opencms.cmis.CmsCmisUtil.splitFilter;
034
035import org.opencms.configuration.CmsConfigurationException;
036import org.opencms.configuration.CmsParameterConfiguration;
037import org.opencms.file.CmsFile;
038import org.opencms.file.CmsObject;
039import org.opencms.file.CmsProject;
040import org.opencms.file.CmsProperty;
041import org.opencms.file.CmsResource;
042import org.opencms.file.CmsResourceFilter;
043import org.opencms.file.CmsVfsResourceAlreadyExistsException;
044import org.opencms.file.types.CmsResourceTypeFolder;
045import org.opencms.file.types.I_CmsResourceType;
046import org.opencms.main.CmsException;
047import org.opencms.main.CmsLog;
048import org.opencms.main.OpenCms;
049import org.opencms.relations.CmsRelation;
050import org.opencms.relations.CmsRelationFilter;
051import org.opencms.repository.CmsRepositoryFilter;
052import org.opencms.search.CmsSearchException;
053import org.opencms.search.solr.CmsSolrIndex;
054import org.opencms.search.solr.CmsSolrQuery;
055import org.opencms.search.solr.CmsSolrResultList;
056import org.opencms.util.CmsFileUtil;
057import org.opencms.util.CmsResourceTranslator;
058import org.opencms.util.CmsStringUtil;
059import org.opencms.util.CmsUUID;
060
061import java.io.ByteArrayInputStream;
062import java.io.IOException;
063import java.io.InputStream;
064import java.math.BigInteger;
065import java.util.ArrayList;
066import java.util.Collections;
067import java.util.Comparator;
068import java.util.HashMap;
069import java.util.List;
070import java.util.Map;
071import java.util.Set;
072
073import org.apache.chemistry.opencmis.commons.PropertyIds;
074import org.apache.chemistry.opencmis.commons.data.Acl;
075import org.apache.chemistry.opencmis.commons.data.AllowableActions;
076import org.apache.chemistry.opencmis.commons.data.ContentStream;
077import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
078import org.apache.chemistry.opencmis.commons.data.ObjectData;
079import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
080import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
081import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
082import org.apache.chemistry.opencmis.commons.data.ObjectList;
083import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
084import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
085import org.apache.chemistry.opencmis.commons.data.Properties;
086import org.apache.chemistry.opencmis.commons.data.PropertyData;
087import org.apache.chemistry.opencmis.commons.data.RenditionData;
088import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
089import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
090import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
091import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
092import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
093import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
094import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
095import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
096import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
097import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
098import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
099import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
100import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
101import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
102import org.apache.chemistry.opencmis.commons.enums.SupportedPermissions;
103import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
104import org.apache.chemistry.opencmis.commons.enums.VersioningState;
105import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
106import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
107import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
108import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
109import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
110import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
111import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
112import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
113import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
114import org.apache.chemistry.opencmis.commons.impl.dataobjects.AclCapabilitiesDataImpl;
115import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
116import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
117import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderContainerImpl;
118import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
119import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
120import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectListImpl;
121import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
122import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionDefinitionDataImpl;
123import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionMappingDataImpl;
124import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl;
125import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl;
126import org.apache.chemistry.opencmis.commons.spi.Holder;
127import org.apache.commons.logging.Log;
128
129/**
130 * Repository instance for CMIS repositories.<p>
131 */
132public class CmsCmisRepository extends A_CmsCmisRepository {
133
134    /**
135     * Simple helper class to simplify creating a permission mapping.<p>
136     */
137    @SuppressWarnings("serial")
138    private static class PermissionMappings extends HashMap<String, PermissionMapping> {
139
140        /** Default constructor.<p> */
141        public PermissionMappings() {
142
143        }
144
145        /**
146         * Creates a single mapping entry.<p>
147         *
148         * @param key the mapping key
149         * @param permission the permission
150         *
151         * @return the mapping entry
152         */
153        private static PermissionMapping createMapping(String key, String permission) {
154
155            PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
156            pm.setKey(key);
157            pm.setPermissions(Collections.singletonList(permission));
158
159            return pm;
160        }
161
162        /**
163         * Adds a permission mapping.<p>
164         *
165         * @param key the key
166         * @param permission the permissions
167         *
168         * @return the instance itself
169         */
170        public PermissionMappings add(String key, String permission) {
171
172            put(key, createMapping(key, permission));
173            return this;
174        }
175
176    }
177
178    /** The description parameter name. */
179    public static final String PARAM_DESCRIPTION = "description";
180
181    /** The project parameter name. */
182    public static final String PARAM_PROJECT = "project";
183
184    /** The property parameter name. */
185    public static final String PARAM_PROPERTY = "property";
186
187    /** The rendition parameter name. */
188    public static final String PARAM_RENDITION = "rendition";
189
190    /** The logger instance for this class. */
191    protected static final Log LOG = CmsLog.getLog(CmsCmisRepository.class);
192
193    /** The index parameter name. */
194    private static final String PARAM_INDEX = "index";
195
196    /** The internal admin CMS context. */
197    private CmsObject m_adminCms;
198
199    /** The repository description. */
200    private String m_description;
201
202    /** The repository filter. */
203    private CmsRepositoryFilter m_filter;
204
205    /** The repository id. */
206    private String m_id;
207
208    /** The name of the SOLR index to use for querying. */
209    private String m_indexName;
210
211    /**
212     * Readonly flag to prevent write operations on the repository.<p>
213     */
214    private boolean m_isReadOnly;
215
216    /** The parameter configuration map. */
217    private CmsParameterConfiguration m_parameterConfiguration = new CmsParameterConfiguration();
218
219    /** The project of the repository. */
220    private CmsProject m_project;
221
222    /** List of dynamic property providers. */
223    private List<I_CmsPropertyProvider> m_propertyProviders = new ArrayList<I_CmsPropertyProvider>();
224
225    /** The relation object helper. */
226    private CmsCmisRelationHelper m_relationHelper = new CmsCmisRelationHelper(this);
227
228    /** The map of rendition providers by stream ids. */
229    private Map<String, I_CmsCmisRenditionProvider> m_renditionProviders = new HashMap<String, I_CmsCmisRenditionProvider>();
230
231    /** The resource object helper. */
232    private CmsCmisResourceHelper m_resourceHelper = new CmsCmisResourceHelper(this);
233
234    /** The root folder. */
235    private CmsResource m_root;
236
237    /**
238     * Creates a permission definition.<p>
239     *
240     * @param permission the permission name
241     * @param description the permission description
242     *
243     * @return the new permission definition
244     */
245    private static PermissionDefinition createPermission(String permission, String description) {
246
247        PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
248        pd.setId(permission);
249        pd.setDescription(description);
250
251        return pd;
252    }
253
254    /**
255     * @see org.opencms.cmis.I_CmsCmisRepository#addConfigurationParameter(java.lang.String, java.lang.String)
256     */
257    public void addConfigurationParameter(String paramName, String paramValue) {
258
259        m_parameterConfiguration.add(paramName, paramValue);
260
261    }
262
263    /**
264     * @see org.opencms.cmis.I_CmsCmisRepository#createDocument(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.data.Properties, java.lang.String, org.apache.chemistry.opencmis.commons.data.ContentStream, org.apache.chemistry.opencmis.commons.enums.VersioningState, java.util.List, org.apache.chemistry.opencmis.commons.data.Acl, org.apache.chemistry.opencmis.commons.data.Acl)
265     */
266    public synchronized String createDocument(
267        CmsCmisCallContext context,
268        Properties propertiesObj,
269        String folderId,
270        ContentStream contentStream,
271        VersioningState versioningState,
272        List<String> policies,
273        Acl addAces,
274        Acl removeAces) {
275
276        checkWriteAccess();
277
278        if ((addAces != null) || (removeAces != null)) {
279            throw new CmisConstraintException("createDocument: ACEs not allowed");
280        }
281
282        if (contentStream == null) {
283            throw new CmisConstraintException("createDocument: no content stream given");
284        }
285
286        try {
287            CmsObject cms = getCmsObject(context);
288            Map<String, PropertyData<?>> properties = propertiesObj.getProperties();
289            String newDocName = (String)properties.get(PropertyIds.NAME).getFirstValue();
290            String defaultType = OpenCms.getResourceManager().getDefaultTypeForName(newDocName).getTypeName();
291            String resTypeName = getResourceTypeFromProperties(properties, defaultType);
292            I_CmsResourceType cmsResourceType = OpenCms.getResourceManager().getResourceType(resTypeName);
293            if (cmsResourceType.isFolder()) {
294                throw new CmisConstraintException("Not a document type: " + resTypeName);
295            }
296            List<CmsProperty> cmsProperties = getOpenCmsProperties(properties);
297            checkResourceName(newDocName);
298            InputStream stream = contentStream.getStream();
299            byte[] content = CmsFileUtil.readFully(stream);
300            CmsUUID parentFolderId = new CmsUUID(folderId);
301            CmsResource parentFolder = cms.readResource(parentFolderId);
302            String newFolderPath = CmsStringUtil.joinPaths(parentFolder.getRootPath(), newDocName);
303            try {
304                CmsResource newDocument = cms.createResource(
305                    newFolderPath,
306                    cmsResourceType.getTypeId(),
307                    content,
308                    cmsProperties);
309                cms.unlockResource(newDocument.getRootPath());
310                return newDocument.getStructureId().toString();
311            } catch (CmsVfsResourceAlreadyExistsException e) {
312                throw new CmisNameConstraintViolationException(e.getLocalizedMessage(), e);
313            }
314        } catch (CmsException e) {
315            handleCmsException(e);
316            return null;
317        } catch (IOException e) {
318            throw new CmisRuntimeException(e.getLocalizedMessage(), e);
319        }
320    }
321
322    /**
323     * @see org.opencms.cmis.I_CmsCmisRepository#createDocumentFromSource(org.opencms.cmis.CmsCmisCallContext, java.lang.String, org.apache.chemistry.opencmis.commons.data.Properties, java.lang.String, org.apache.chemistry.opencmis.commons.enums.VersioningState, java.util.List, org.apache.chemistry.opencmis.commons.data.Acl, org.apache.chemistry.opencmis.commons.data.Acl)
324     */
325    public synchronized String createDocumentFromSource(
326        CmsCmisCallContext context,
327        String sourceId,
328        Properties propertiesObj,
329        String folderId,
330        VersioningState versioningState,
331        List<String> policies,
332        Acl addAces,
333        Acl removeAces) {
334
335        checkWriteAccess();
336
337        if ((addAces != null) || (removeAces != null)) {
338            throw new CmisConstraintException("createDocument: ACEs not allowed");
339        }
340
341        try {
342            CmsObject cms = getCmsObject(context);
343            Map<String, PropertyData<?>> properties = new HashMap<String, PropertyData<?>>();
344            if (propertiesObj != null) {
345                properties = propertiesObj.getProperties();
346            }
347            List<CmsProperty> cmsProperties = getOpenCmsProperties(properties);
348            CmsUUID parentFolderId = new CmsUUID(folderId);
349            CmsResource parentFolder = cms.readResource(parentFolderId);
350            CmsUUID sourceUuid = new CmsUUID(sourceId);
351            CmsResource source = cms.readResource(sourceUuid);
352            String sourcePath = source.getRootPath();
353
354            PropertyData<?> nameProp = properties.get(PropertyIds.NAME);
355            String newDocName;
356            if (nameProp != null) {
357                newDocName = (String)nameProp.getFirstValue();
358                checkResourceName(newDocName);
359            } else {
360                newDocName = CmsResource.getName(source.getRootPath());
361            }
362            String targetPath = CmsStringUtil.joinPaths(parentFolder.getRootPath(), newDocName);
363
364            try {
365                cms.copyResource(sourcePath, targetPath);
366            } catch (CmsVfsResourceAlreadyExistsException e) {
367                throw new CmisNameConstraintViolationException(e.getLocalizedMessage(), e);
368            }
369
370            CmsResource targetResource = cms.readResource(targetPath);
371            cms.setDateLastModified(targetResource.getRootPath(), targetResource.getDateCreated(), false);
372            cms.unlockResource(targetResource);
373            boolean wasLocked = ensureLock(cms, targetResource);
374            cms.writePropertyObjects(targetResource, cmsProperties);
375            for (String key : properties.keySet()) {
376                if (key.startsWith(CmsCmisTypeManager.PROPERTY_PREFIX_DYNAMIC)) {
377                    I_CmsPropertyProvider provider = getTypeManager().getPropertyProvider(key);
378                    try {
379                        String value = (String)(properties.get(key).getFirstValue());
380                        provider.setPropertyValue(cms, targetResource, value);
381                    } catch (CmsException e) {
382                        LOG.error(e.getLocalizedMessage(), e);
383                    }
384                }
385            }
386
387            if (wasLocked) {
388                cms.unlockResource(targetResource);
389            }
390            return targetResource.getStructureId().toString();
391        } catch (CmsException e) {
392            handleCmsException(e);
393            return null;
394        }
395    }
396
397    /**
398     * @see org.opencms.cmis.I_CmsCmisRepository#createFolder(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.data.Properties, java.lang.String, java.util.List, org.apache.chemistry.opencmis.commons.data.Acl, org.apache.chemistry.opencmis.commons.data.Acl)
399     */
400    public synchronized String createFolder(
401        CmsCmisCallContext context,
402        Properties propertiesObj,
403        String folderId,
404        List<String> policies,
405        Acl addAces,
406        Acl removeAces) {
407
408        checkWriteAccess();
409
410        if ((addAces != null) || (removeAces != null)) {
411            throw new CmisConstraintException("createFolder: ACEs not allowed");
412        }
413
414        try {
415            CmsObject cms = getCmsObject(context);
416            Map<String, PropertyData<?>> properties = propertiesObj.getProperties();
417            String resTypeName = getResourceTypeFromProperties(properties, CmsResourceTypeFolder.getStaticTypeName());
418            I_CmsResourceType cmsResourceType = OpenCms.getResourceManager().getResourceType(resTypeName);
419            if (!cmsResourceType.isFolder()) {
420                throw new CmisConstraintException("Invalid folder type: " + resTypeName);
421            }
422            List<CmsProperty> cmsProperties = getOpenCmsProperties(properties);
423            String newFolderName = (String)properties.get(PropertyIds.NAME).getFirstValue();
424            checkResourceName(newFolderName);
425            CmsUUID parentFolderId = new CmsUUID(folderId);
426            CmsResource parentFolder = cms.readResource(parentFolderId);
427            String newFolderPath = CmsStringUtil.joinPaths(parentFolder.getRootPath(), newFolderName);
428            try {
429                CmsResource newFolder = cms.createResource(
430                    newFolderPath,
431                    cmsResourceType.getTypeId(),
432                    null,
433                    cmsProperties);
434                cms.unlockResource(newFolder);
435                return newFolder.getStructureId().toString();
436            } catch (CmsVfsResourceAlreadyExistsException e) {
437                throw new CmisNameConstraintViolationException(e.getLocalizedMessage(), e);
438            }
439        } catch (CmsException e) {
440            handleCmsException(e);
441            return null;
442        }
443    }
444
445    /**
446     * @see org.opencms.cmis.I_CmsCmisRepository#createRelationship(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.data.Properties, java.util.List, org.apache.chemistry.opencmis.commons.data.Acl, org.apache.chemistry.opencmis.commons.data.Acl)
447     */
448    public synchronized String createRelationship(
449        CmsCmisCallContext context,
450        Properties properties,
451        List<String> policies,
452        Acl addAces,
453        Acl removeAces) {
454
455        try {
456            CmsObject cms = getCmsObject(context);
457            Map<String, PropertyData<?>> propertyMap = properties.getProperties();
458            String sourceProp = (String)(propertyMap.get(PropertyIds.SOURCE_ID).getFirstValue());
459            String targetProp = (String)(propertyMap.get(PropertyIds.TARGET_ID).getFirstValue());
460            String typeId = (String)(propertyMap.get(PropertyIds.OBJECT_TYPE_ID).getFirstValue());
461            if (!typeId.startsWith("opencms:")) {
462                throw new CmisConstraintException("Can't create this relationship type.");
463            }
464            String cmsTypeName = typeId.substring("opencms:".length());
465            CmsUUID sourceId = new CmsUUID(sourceProp);
466            CmsUUID targetId = new CmsUUID(targetProp);
467            CmsResource sourceRes = cms.readResource(sourceId);
468            boolean wasLocked = ensureLock(cms, sourceRes);
469            try {
470                CmsResource targetRes = cms.readResource(targetId);
471                cms.addRelationToResource(sourceRes.getRootPath(), targetRes.getRootPath(), cmsTypeName);
472                return "REL_" + sourceRes.getStructureId() + "_" + targetRes.getStructureId() + "_" + cmsTypeName;
473            } finally {
474                if (wasLocked) {
475                    cms.unlockResource(sourceRes);
476                }
477            }
478        } catch (CmsException e) {
479            CmsCmisUtil.handleCmsException(e);
480            return null;
481        }
482    }
483
484    /**
485     * @see org.opencms.cmis.I_CmsCmisRepository#deleteContentStream(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.spi.Holder, org.apache.chemistry.opencmis.commons.spi.Holder)
486     */
487    public synchronized void deleteContentStream(
488        CmsCmisCallContext context,
489        Holder<String> objectId,
490        Holder<String> changeToken) {
491
492        throw new CmisConstraintException("Content streams may not be deleted.");
493
494    }
495
496    /**
497     * @see org.opencms.cmis.I_CmsCmisRepository#deleteObject(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean)
498     */
499    public synchronized void deleteObject(CmsCmisCallContext context, String objectId, boolean allVersions) {
500
501        checkWriteAccess();
502        getHelper(objectId).deleteObject(context, objectId, allVersions);
503    }
504
505    /**
506     * @see org.opencms.cmis.I_CmsCmisRepository#deleteTree(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean, org.apache.chemistry.opencmis.commons.enums.UnfileObject, boolean)
507     */
508    public synchronized FailedToDeleteData deleteTree(
509        CmsCmisCallContext context,
510        String folderId,
511        boolean allVersions,
512        UnfileObject unfileObjects,
513        boolean continueOnFailure) {
514
515        checkWriteAccess();
516
517        try {
518
519            FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
520            result.setIds(new ArrayList<String>());
521            CmsObject cms = getCmsObject(context);
522            CmsUUID structureId = new CmsUUID(folderId);
523            CmsResource folder = cms.readResource(structureId);
524            if (!folder.isFolder()) {
525                throw new CmisConstraintException("deleteTree can only be used on folders.");
526            }
527            ensureLock(cms, folder);
528            cms.deleteResource(folder.getRootPath(), CmsResource.DELETE_PRESERVE_SIBLINGS);
529            return result;
530        } catch (CmsException e) {
531            handleCmsException(e);
532            return null;
533        }
534    }
535
536    /**
537     * @see org.opencms.cmis.I_CmsCmisRepository#getAcl(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean)
538     */
539    public synchronized Acl getAcl(CmsCmisCallContext context, String objectId, boolean onlyBasicPermissions) {
540
541        return getHelper(objectId).getAcl(context, objectId, onlyBasicPermissions);
542    }
543
544    /**
545     * @see org.opencms.cmis.I_CmsCmisRepository#getAllowableActions(org.opencms.cmis.CmsCmisCallContext, java.lang.String)
546     */
547    public synchronized AllowableActions getAllowableActions(CmsCmisCallContext context, String objectId) {
548
549        return getHelper(objectId).getAllowableActions(context, objectId);
550    }
551
552    /**
553     * @see org.opencms.cmis.I_CmsCmisRepository#getCheckedOutDocs(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, java.lang.String, boolean, org.apache.chemistry.opencmis.commons.enums.IncludeRelationships, java.lang.String, java.math.BigInteger, java.math.BigInteger)
554     */
555    public synchronized ObjectList getCheckedOutDocs(
556        CmsCmisCallContext context,
557        String folderId,
558        String filter,
559        String orderBy,
560        boolean includeAllowableActions,
561        IncludeRelationships includeRelationships,
562        String renditionFilter,
563        BigInteger maxItems,
564        BigInteger skipCount) {
565
566        ObjectListImpl result = new ObjectListImpl();
567        result.setObjects(new ArrayList<ObjectData>());
568        return result;
569    }
570
571    /**
572     * @see org.opencms.cmis.I_CmsCmisRepository#getChildren(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, java.lang.String, boolean, org.apache.chemistry.opencmis.commons.enums.IncludeRelationships, java.lang.String, boolean, java.math.BigInteger, java.math.BigInteger)
573     */
574    public synchronized ObjectInFolderList getChildren(
575        CmsCmisCallContext context,
576        String folderId,
577        String filter,
578        String orderBy,
579        boolean includeAllowableActions,
580        IncludeRelationships includeRelationships,
581        String renditionFilter,
582        boolean includePathSegment,
583        BigInteger maxItems,
584        BigInteger skipCount) {
585
586        try {
587            CmsCmisResourceHelper helper = getResourceHelper();
588
589            // split filter
590            Set<String> filterCollection = splitFilter(filter);
591            // skip and max
592            int skip = (skipCount == null ? 0 : skipCount.intValue());
593            if (skip < 0) {
594                skip = 0;
595            }
596
597            int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
598            if (max < 0) {
599                max = Integer.MAX_VALUE;
600            }
601
602            CmsObject cms = getCmsObject(context);
603            CmsUUID structureId = new CmsUUID(folderId);
604            CmsResource folder = cms.readResource(structureId);
605            if (!folder.isFolder()) {
606                throw new CmisObjectNotFoundException("Not a folder!");
607            }
608
609            // set object info of the the folder
610            if (context.isObjectInfoRequired()) {
611                helper.collectObjectData(
612                    context,
613                    cms,
614                    folder,
615                    null,
616                    renditionFilter,
617                    false,
618                    false,
619                    includeRelationships);
620            }
621
622            // prepare result
623            ObjectInFolderListImpl result = new ObjectInFolderListImpl();
624            String folderSitePath = cms.getRequestContext().getSitePath(folder);
625            List<CmsResource> children = cms.getResourcesInFolder(folderSitePath, CmsResourceFilter.DEFAULT);
626            CmsObjectListLimiter<CmsResource> limiter = new CmsObjectListLimiter<CmsResource>(
627                children,
628                maxItems,
629                skipCount);
630            List<ObjectInFolderData> resultObjects = new ArrayList<ObjectInFolderData>();
631            for (CmsResource child : limiter) {
632                // build and add child object
633                ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
634                objectInFolder.setObject(
635                    helper.collectObjectData(
636                        context,
637                        cms,
638                        child,
639                        filterCollection,
640                        renditionFilter,
641                        includeAllowableActions,
642                        false,
643                        includeRelationships));
644                if (includePathSegment) {
645                    objectInFolder.setPathSegment(child.getName());
646                }
647                resultObjects.add(objectInFolder);
648            }
649            result.setObjects(resultObjects);
650            result.setNumItems(BigInteger.valueOf(children.size()));
651            result.setHasMoreItems(Boolean.valueOf(limiter.hasMore()));
652            return result;
653        } catch (CmsException e) {
654            handleCmsException(e);
655            return null;
656        }
657
658    }
659
660    /**
661     * @see org.opencms.cmis.I_CmsCmisRepository#getConfiguration()
662     */
663    public CmsParameterConfiguration getConfiguration() {
664
665        return m_parameterConfiguration;
666    }
667
668    /**
669     * @see org.opencms.cmis.I_CmsCmisRepository#getContentStream(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, java.math.BigInteger, java.math.BigInteger)
670     */
671    public synchronized ContentStream getContentStream(
672        CmsCmisCallContext context,
673        String objectId,
674        String streamId,
675        BigInteger offset,
676        BigInteger length) {
677
678        try {
679            CmsObject cms = getCmsObject(context);
680            CmsResource resource = cms.readResource(new CmsUUID(objectId));
681            byte[] contents = null;
682            if (streamId != null) {
683                I_CmsCmisRenditionProvider renditionProvider = m_renditionProviders.get(streamId);
684                if (renditionProvider == null) {
685                    throw new CmisRuntimeException("Invalid stream id " + streamId);
686                }
687                contents = renditionProvider.getContent(cms, resource);
688            } else if (resource.isFolder()) {
689                throw new CmisStreamNotSupportedException("Not a file!");
690            } else {
691                CmsFile file = cms.readFile(resource);
692                contents = file.getContents();
693            }
694            contents = extractRange(contents, offset, length);
695            InputStream stream = new ByteArrayInputStream(contents);
696            ContentStreamImpl result = new ContentStreamImpl();
697            result.setFileName(resource.getName());
698            result.setLength(BigInteger.valueOf(contents.length));
699            result.setMimeType(OpenCms.getResourceManager().getMimeType(resource.getRootPath(), null, "text/plain"));
700            result.setStream(stream);
701
702            return result;
703        } catch (CmsException e) {
704            handleCmsException(e);
705            return null;
706        }
707    }
708
709    /**
710     * @see org.opencms.cmis.I_CmsCmisRepository#getDescendants(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.math.BigInteger, java.lang.String, boolean, boolean, boolean)
711     */
712    public synchronized List<ObjectInFolderContainer> getDescendants(
713        CmsCmisCallContext context,
714        String folderId,
715        BigInteger depth,
716        String filter,
717        boolean includeAllowableActions,
718        boolean includePathSegment,
719        boolean foldersOnly) {
720
721        try {
722            CmsCmisResourceHelper helper = getResourceHelper();
723
724            // check depth
725            int d = (depth == null ? 2 : depth.intValue());
726            if (d == 0) {
727                throw new CmisInvalidArgumentException("Depth must not be 0!");
728            }
729            if (d < -1) {
730                d = -1;
731            }
732
733            // split filter
734            Set<String> filterCollection = splitFilter(filter);
735
736            CmsObject cms = getCmsObject(context);
737            CmsUUID folderStructureId = new CmsUUID(folderId);
738            CmsResource folder = cms.readResource(folderStructureId);
739            if (!folder.isFolder()) {
740                throw new CmisObjectNotFoundException("Not a folder!");
741            }
742
743            // set object info of the the folder
744            if (context.isObjectInfoRequired()) {
745                helper.collectObjectData(
746                    context,
747                    cms,
748                    folder,
749                    null,
750                    "cmis:none",
751                    false,
752                    false,
753                    IncludeRelationships.NONE);
754            }
755
756            // get the tree
757            List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
758            gatherDescendants(
759                context,
760                cms,
761                folder,
762                result,
763                foldersOnly,
764                d,
765                filterCollection,
766                includeAllowableActions,
767                includePathSegment);
768
769            return result;
770        } catch (CmsException e) {
771            handleCmsException(e);
772            return null;
773        }
774    }
775
776    /**
777     * @see org.opencms.cmis.I_CmsCmisRepository#getDescription()
778     */
779    public String getDescription() {
780
781        if (m_description != null) {
782            return m_description;
783        }
784        if (m_project != null) {
785            return m_project.getDescription();
786        }
787        return m_id;
788    }
789
790    /**
791     * @see org.opencms.cmis.I_CmsCmisRepository#getFilter()
792     */
793    public CmsRepositoryFilter getFilter() {
794
795        return m_filter;
796    }
797
798    /**
799     * @see org.opencms.cmis.I_CmsCmisRepository#getFolderParent(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String)
800     */
801    public synchronized ObjectData getFolderParent(CmsCmisCallContext context, String folderId, String filter) {
802
803        List<ObjectParentData> parents = getObjectParents(context, folderId, filter, false, false);
804        if (parents.size() == 0) {
805            throw new CmisInvalidArgumentException("The root folder has no parent!");
806        }
807        return parents.get(0).getObject();
808    }
809
810    /**
811     * @see org.opencms.cmis.I_CmsCmisRepository#getId()
812     */
813    public String getId() {
814
815        return m_id;
816    }
817
818    /**
819     * @see org.opencms.cmis.I_CmsCmisRepository#getName()
820     */
821    public String getName() {
822
823        return m_id;
824    }
825
826    /**
827     * @see org.opencms.cmis.I_CmsCmisRepository#getObject(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, boolean, org.apache.chemistry.opencmis.commons.enums.IncludeRelationships, java.lang.String, boolean, boolean)
828     */
829    public synchronized ObjectData getObject(
830        CmsCmisCallContext context,
831        String objectId,
832        String filter,
833        boolean includeAllowableActions,
834        IncludeRelationships includeRelationships,
835        String renditionFilter,
836        boolean includePolicyIds,
837        boolean includeAcl) {
838
839        return getHelper(objectId).getObject(
840            context,
841            objectId,
842            filter,
843            includeAllowableActions,
844            includeRelationships,
845            renditionFilter,
846            includePolicyIds,
847            includeAcl);
848    }
849
850    /**
851     * @see org.opencms.cmis.I_CmsCmisRepository#getObjectByPath(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, boolean, org.apache.chemistry.opencmis.commons.enums.IncludeRelationships, java.lang.String, boolean, boolean)
852     */
853    public synchronized ObjectData getObjectByPath(
854        CmsCmisCallContext context,
855        String path,
856        String filter,
857        boolean includeAllowableActions,
858        IncludeRelationships includeRelationships,
859        String renditionFilter,
860        boolean includePolicyIds,
861        boolean includeAcl
862
863    ) {
864
865        try {
866            CmsCmisResourceHelper helper = getResourceHelper();
867
868            // split filter
869            Set<String> filterCollection = splitFilter(filter);
870
871            // check path
872            if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
873                throw new CmisInvalidArgumentException("Invalid folder path!");
874            }
875            CmsObject cms = getCmsObject(context);
876            CmsResource file = cms.readResource(path);
877
878            return helper.collectObjectData(
879                context,
880                cms,
881                file,
882                filterCollection,
883                renditionFilter,
884                includeAllowableActions,
885                includeAcl,
886                IncludeRelationships.NONE);
887
888        } catch (CmsException e) {
889            handleCmsException(e);
890            return null;
891        }
892    }
893
894    /**
895     * @see org.opencms.cmis.I_CmsCmisRepository#getObjectParents(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, boolean, boolean)
896     */
897    public synchronized List<ObjectParentData> getObjectParents(
898        CmsCmisCallContext context,
899        String objectId,
900        String filter,
901        boolean includeAllowableActions,
902        boolean includeRelativePathSegment) {
903
904        try {
905            CmsCmisResourceHelper helper = getResourceHelper();
906
907            // split filter
908            Set<String> filterCollection = splitFilter(filter);
909            CmsObject cms = getCmsObject(context);
910            CmsUUID structureId = new CmsUUID(objectId);
911            CmsResource file = cms.readResource(structureId);
912            // don't climb above the root folder
913
914            if (m_root.equals(file)) {
915                return Collections.emptyList();
916            }
917
918            // set object info of the the object
919            if (context.isObjectInfoRequired()) {
920                helper.collectObjectData(
921                    context,
922                    cms,
923                    file,
924                    null,
925                    "cmis:none",
926                    false,
927                    false,
928                    IncludeRelationships.NONE);
929            }
930
931            // get parent folder
932            CmsResource parent = cms.readParentFolder(file.getStructureId());
933            ObjectData object = helper.collectObjectData(
934                context,
935                cms,
936                parent,
937                filterCollection,
938                "cmis:none",
939                includeAllowableActions,
940                false,
941                IncludeRelationships.NONE);
942
943            ObjectParentDataImpl result = new ObjectParentDataImpl();
944            result.setObject(object);
945            if (includeRelativePathSegment) {
946                result.setRelativePathSegment(file.getName());
947            }
948
949            return Collections.singletonList((ObjectParentData)result);
950        } catch (CmsException e) {
951            handleCmsException(e);
952            return null;
953        }
954
955    }
956
957    /**
958     * @see org.opencms.cmis.I_CmsCmisRepository#getObjectRelationships(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean, org.apache.chemistry.opencmis.commons.enums.RelationshipDirection, java.lang.String, java.lang.String, boolean, java.math.BigInteger, java.math.BigInteger)
959     */
960    public synchronized ObjectList getObjectRelationships(
961        CmsCmisCallContext context,
962        String objectId,
963        boolean includeSubRelationshipTypes,
964        RelationshipDirection relationshipDirection,
965        String typeId,
966        String filter,
967        boolean includeAllowableActions,
968        BigInteger maxItems,
969        BigInteger skipCount) {
970
971        try {
972            CmsObject cms = getCmsObject(context);
973            ObjectListImpl result = new ObjectListImpl();
974            CmsUUID structureId = new CmsUUID(objectId);
975            CmsResource resource = cms.readResource(structureId);
976
977            List<ObjectData> resultObjects = getRelationshipObjectData(
978                context,
979                cms,
980                resource,
981                relationshipDirection,
982                CmsCmisUtil.splitFilter(filter),
983                includeAllowableActions);
984            CmsObjectListLimiter<ObjectData> limiter = new CmsObjectListLimiter<ObjectData>(
985                resultObjects,
986                maxItems,
987                skipCount);
988            List<ObjectData> limitedResults = new ArrayList<ObjectData>();
989            for (ObjectData objectData : limiter) {
990                limitedResults.add(objectData);
991            }
992            result.setNumItems(BigInteger.valueOf(resultObjects.size()));
993            result.setHasMoreItems(Boolean.valueOf(limiter.hasMore()));
994            result.setObjects(limitedResults);
995            return result;
996        } catch (CmsException e) {
997            CmsCmisUtil.handleCmsException(e);
998            return null;
999        }
1000    }
1001
1002    /**
1003     * @see org.opencms.cmis.I_CmsCmisRepository#getProperties(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String)
1004     */
1005    public synchronized Properties getProperties(CmsCmisCallContext context, String objectId, String filter) {
1006
1007        ObjectData object = getObject(context, objectId, null, false, null, null, false, false);
1008        return object.getProperties();
1009    }
1010
1011    /**
1012     * @see org.opencms.cmis.I_CmsCmisRepository#getRenditions(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.lang.String, java.math.BigInteger, java.math.BigInteger)
1013     */
1014    public synchronized List<RenditionData> getRenditions(
1015        CmsCmisCallContext context,
1016        String objectId,
1017        String renditionFilter,
1018        BigInteger maxItems,
1019        BigInteger skipCount) {
1020
1021        try {
1022            CmsObject cms = getCmsObject(context);
1023            CmsResource resource = cms.readResource(new CmsUUID(objectId));
1024            return getResourceHelper().collectObjectData(
1025                context,
1026                cms,
1027                resource,
1028                null,
1029                renditionFilter,
1030                false,
1031                false,
1032                IncludeRelationships.NONE).getRenditions();
1033        } catch (CmsException e) {
1034            handleCmsException(e);
1035            return null;
1036        }
1037    }
1038
1039    /**
1040     * @see org.opencms.cmis.I_CmsCmisRepository#getRepositoryInfo()
1041     */
1042    public synchronized RepositoryInfo getRepositoryInfo() {
1043
1044        // compile repository info
1045        RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl();
1046
1047        repositoryInfo.setId(m_id);
1048        repositoryInfo.setName(getName());
1049        repositoryInfo.setDescription(getDescription());
1050
1051        repositoryInfo.setCmisVersionSupported("1.0");
1052
1053        repositoryInfo.setProductName("OpenCms");
1054        repositoryInfo.setProductVersion(OpenCms.getSystemInfo().getVersion());
1055        repositoryInfo.setVendorName("Alkacon Software GmbH & Co. KG");
1056        repositoryInfo.setRootFolder(m_root.getStructureId().toString());
1057        repositoryInfo.setThinClientUri("");
1058        repositoryInfo.setPrincipalAnonymous(OpenCms.getDefaultUsers().getUserGuest());
1059        repositoryInfo.setChangesIncomplete(Boolean.TRUE);
1060        RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
1061        capabilities.setCapabilityAcl(CapabilityAcl.DISCOVER);
1062        capabilities.setAllVersionsSearchable(Boolean.FALSE);
1063        capabilities.setCapabilityJoin(CapabilityJoin.NONE);
1064        capabilities.setSupportsMultifiling(Boolean.FALSE);
1065        capabilities.setSupportsUnfiling(Boolean.FALSE);
1066        capabilities.setSupportsVersionSpecificFiling(Boolean.FALSE);
1067        capabilities.setIsPwcSearchable(Boolean.FALSE);
1068        capabilities.setIsPwcUpdatable(Boolean.FALSE);
1069        capabilities.setCapabilityQuery(getIndex() != null ? CapabilityQuery.FULLTEXTONLY : CapabilityQuery.NONE);
1070        capabilities.setCapabilityChanges(CapabilityChanges.NONE);
1071        capabilities.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.ANYTIME);
1072        capabilities.setSupportsGetDescendants(Boolean.TRUE);
1073        capabilities.setSupportsGetFolderTree(Boolean.TRUE);
1074        capabilities.setCapabilityRendition(CapabilityRenditions.READ);
1075        repositoryInfo.setCapabilities(capabilities);
1076
1077        AclCapabilitiesDataImpl aclCapability = new AclCapabilitiesDataImpl();
1078        aclCapability.setSupportedPermissions(SupportedPermissions.BOTH);
1079        aclCapability.setAclPropagation(AclPropagation.REPOSITORYDETERMINED);
1080
1081        // permissions
1082        List<PermissionDefinition> permissions = new ArrayList<PermissionDefinition>();
1083        permissions.add(createPermission(CMIS_READ, "Read"));
1084        permissions.add(createPermission(CMIS_WRITE, "Write"));
1085        permissions.add(createPermission(CMIS_ALL, "All"));
1086        aclCapability.setPermissionDefinitionData(permissions);
1087
1088        // mappings
1089        PermissionMappings m = new PermissionMappings();
1090        m.add(PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER, CMIS_WRITE);
1091        m.add(PermissionMapping.CAN_CREATE_FOLDER_FOLDER, CMIS_WRITE);
1092        m.add(PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT, CMIS_WRITE);
1093        m.add(PermissionMapping.CAN_DELETE_OBJECT, CMIS_WRITE);
1094        m.add(PermissionMapping.CAN_DELETE_TREE_FOLDER, CMIS_WRITE);
1095        m.add(PermissionMapping.CAN_GET_ACL_OBJECT, CMIS_READ);
1096        m.add(PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES, CMIS_READ);
1097        m.add(PermissionMapping.CAN_GET_CHILDREN_FOLDER, CMIS_READ);
1098        m.add(PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, CMIS_READ);
1099        m.add(PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, CMIS_READ);
1100        m.add(PermissionMapping.CAN_GET_PARENTS_FOLDER, CMIS_READ);
1101        m.add(PermissionMapping.CAN_GET_PROPERTIES_OBJECT, CMIS_READ);
1102        m.add(PermissionMapping.CAN_MOVE_OBJECT, CMIS_WRITE);
1103        m.add(PermissionMapping.CAN_MOVE_SOURCE, CMIS_WRITE);
1104        m.add(PermissionMapping.CAN_MOVE_TARGET, CMIS_WRITE);
1105        m.add(PermissionMapping.CAN_SET_CONTENT_DOCUMENT, CMIS_WRITE);
1106        m.add(PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, CMIS_WRITE);
1107        m.add(PermissionMapping.CAN_VIEW_CONTENT_OBJECT, CMIS_READ);
1108        aclCapability.setPermissionMappingData(m);
1109        repositoryInfo.setAclCapabilities(aclCapability);
1110        return repositoryInfo;
1111    }
1112
1113    /**
1114     * @see org.opencms.repository.I_CmsRepository#getTranslation()
1115     */
1116    public CmsResourceTranslator getTranslation() {
1117
1118        // TODO Auto-generated method stub
1119        return null;
1120    }
1121
1122    /**
1123     * @see org.opencms.cmis.I_CmsCmisRepository#getTypeChildren(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean, java.math.BigInteger, java.math.BigInteger)
1124     */
1125    public synchronized TypeDefinitionList getTypeChildren(
1126        CmsCmisCallContext context,
1127        String typeId,
1128        boolean includePropertyDefinitions,
1129        BigInteger maxItems,
1130        BigInteger skipCount) {
1131
1132        return m_typeManager.getTypeChildren(typeId, includePropertyDefinitions, maxItems, skipCount);
1133    }
1134
1135    /**
1136     * @see org.opencms.cmis.I_CmsCmisRepository#getTypeDefinition(org.opencms.cmis.CmsCmisCallContext, java.lang.String)
1137     */
1138    public synchronized TypeDefinition getTypeDefinition(CmsCmisCallContext context, String typeId) {
1139
1140        return m_typeManager.getTypeDefinition(typeId);
1141    }
1142
1143    /**
1144     * @see org.opencms.cmis.I_CmsCmisRepository#getTypeDescendants(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.math.BigInteger, boolean)
1145     */
1146    public synchronized List<TypeDefinitionContainer> getTypeDescendants(
1147        CmsCmisCallContext context,
1148        String typeId,
1149        BigInteger depth,
1150        boolean includePropertyDefinitions) {
1151
1152        return m_typeManager.getTypeDescendants(typeId, depth, includePropertyDefinitions);
1153    }
1154
1155    /**
1156     * @see org.opencms.cmis.I_CmsCmisRepository#initConfiguration()
1157     */
1158    public void initConfiguration() throws CmsConfigurationException {
1159
1160        if (m_filter != null) {
1161            m_filter.initConfiguration();
1162        }
1163        m_description = m_parameterConfiguration.getString(PARAM_DESCRIPTION, null);
1164        List<String> renditionProviderClasses = m_parameterConfiguration.getList(
1165            PARAM_RENDITION,
1166            Collections.<String> emptyList());
1167        for (String className : renditionProviderClasses) {
1168            try {
1169                I_CmsCmisRenditionProvider provider = (I_CmsCmisRenditionProvider)(Class.forName(
1170                    className).newInstance());
1171                String id = provider.getId();
1172                m_renditionProviders.put(id, provider);
1173            } catch (Throwable e) {
1174                LOG.error(e.getLocalizedMessage(), e);
1175            }
1176        }
1177        List<String> propertyProviderClasses = m_parameterConfiguration.getList(
1178            PARAM_PROPERTY,
1179            Collections.<String> emptyList());
1180        for (String className : propertyProviderClasses) {
1181            try {
1182                I_CmsPropertyProvider provider = (I_CmsPropertyProvider)(Class.forName(className).newInstance());
1183                m_propertyProviders.add(provider);
1184            } catch (Throwable e) {
1185                LOG.error(e.getLocalizedMessage(), e);
1186            }
1187        }
1188        m_indexName = m_parameterConfiguration.getString(PARAM_INDEX, null);
1189    }
1190
1191    /**
1192     * @see org.opencms.cmis.I_CmsCmisRepository#initializeCms(org.opencms.file.CmsObject)
1193     */
1194    public void initializeCms(CmsObject cms) throws CmsException {
1195
1196        m_adminCms = cms;
1197        m_typeManager = new CmsCmisTypeManager(cms, m_propertyProviders);
1198        String projectName = m_parameterConfiguration.getString(PARAM_PROJECT, CmsProject.ONLINE_PROJECT_NAME);
1199        CmsResource root = m_adminCms.readResource("/");
1200        CmsObject offlineCms = OpenCms.initCmsObject(m_adminCms);
1201        CmsProject project = m_adminCms.readProject(projectName);
1202        m_project = project;
1203        offlineCms.getRequestContext().setCurrentProject(project);
1204        m_adminCms = offlineCms;
1205        m_root = root;
1206        m_isReadOnly = project.isOnlineProject();
1207    }
1208
1209    /**
1210     * @see org.opencms.repository.I_CmsRepository#isTranslationEnabled()
1211     */
1212    public boolean isTranslationEnabled() {
1213
1214        // TODO Auto-generated method stub
1215        return false;
1216    }
1217
1218    /**
1219     * @see org.opencms.cmis.I_CmsCmisRepository#moveObject(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.spi.Holder, java.lang.String, java.lang.String)
1220     */
1221    public synchronized void moveObject(
1222        CmsCmisCallContext context,
1223        Holder<String> objectId,
1224        String targetFolderId,
1225        String sourceFolderId) {
1226
1227        checkWriteAccess();
1228
1229        try {
1230            CmsObject cms = getCmsObject(context);
1231            CmsUUID structureId = new CmsUUID(objectId.getValue());
1232            CmsUUID targetStructureId = new CmsUUID(targetFolderId);
1233            CmsResource targetFolder = cms.readResource(targetStructureId);
1234            CmsResource resourceToMove = cms.readResource(structureId);
1235            String name = CmsResource.getName(resourceToMove.getRootPath());
1236            String newPath = CmsStringUtil.joinPaths(targetFolder.getRootPath(), name);
1237            boolean wasLocked = ensureLock(cms, resourceToMove);
1238            try {
1239                cms.moveResource(resourceToMove.getRootPath(), newPath);
1240            } finally {
1241                if (wasLocked) {
1242                    CmsResource movedResource = cms.readResource(resourceToMove.getStructureId());
1243                    cms.unlockResource(movedResource);
1244                }
1245            }
1246        } catch (CmsException e) {
1247            handleCmsException(e);
1248        }
1249    }
1250
1251    /**
1252     * @see org.opencms.cmis.I_CmsCmisRepository#query(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean, boolean, org.apache.chemistry.opencmis.commons.enums.IncludeRelationships, java.lang.String, java.math.BigInteger, java.math.BigInteger)
1253     */
1254    @Override
1255    public synchronized ObjectList query(
1256        CmsCmisCallContext context,
1257        String statement,
1258        boolean searchAllVersions,
1259        boolean includeAllowableActions,
1260        IncludeRelationships includeRelationships,
1261        String renditionFilter,
1262        BigInteger maxItems,
1263        BigInteger skipCount) {
1264
1265        try {
1266            CmsObject cms = getCmsObject(context);
1267            CmsSolrIndex index = getIndex();
1268            CmsCmisResourceHelper helper = getResourceHelper();
1269
1270            // split filter
1271            Set<String> filterCollection = null;
1272            // skip and max
1273            int skip = (skipCount == null ? 0 : skipCount.intValue());
1274            if (skip < 0) {
1275                skip = 0;
1276            }
1277
1278            int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
1279            if (max < 0) {
1280                max = Integer.MAX_VALUE;
1281            }
1282            CmsSolrResultList results = solrSearch(cms, index, statement, skip, max);
1283            ObjectListImpl resultObjectList = new ObjectListImpl();
1284            List<ObjectData> objectDataList = new ArrayList<ObjectData>();
1285            resultObjectList.setObjects(objectDataList);
1286            for (CmsResource resource : results) {
1287                // build and add child object
1288                objectDataList.add(
1289                    helper.collectObjectData(
1290                        context,
1291                        cms,
1292                        resource,
1293                        filterCollection,
1294                        renditionFilter,
1295                        includeAllowableActions,
1296                        false,
1297                        includeRelationships));
1298            }
1299            resultObjectList.setHasMoreItems(Boolean.valueOf(!results.isEmpty()));
1300            resultObjectList.setNumItems(BigInteger.valueOf(results.getVisibleHitCount()));
1301            return resultObjectList;
1302        } catch (CmsException e) {
1303            handleCmsException(e);
1304            return null;
1305        }
1306
1307    }
1308
1309    /**
1310     * @see org.opencms.cmis.I_CmsCmisRepository#setContentStream(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.spi.Holder, boolean, org.apache.chemistry.opencmis.commons.spi.Holder, org.apache.chemistry.opencmis.commons.data.ContentStream)
1311     */
1312    public synchronized void setContentStream(
1313        CmsCmisCallContext context,
1314        Holder<String> objectId,
1315        boolean overwriteFlag,
1316        Holder<String> changeToken,
1317        ContentStream contentStream) {
1318
1319        checkWriteAccess();
1320
1321        try {
1322            CmsObject cms = getCmsObject(context);
1323            CmsUUID structureId = new CmsUUID(objectId.getValue());
1324            if (!overwriteFlag) {
1325                throw new CmisContentAlreadyExistsException();
1326            }
1327            CmsResource resource = cms.readResource(structureId);
1328            if (resource.isFolder()) {
1329                throw new CmisStreamNotSupportedException("Folders may not have content streams.");
1330            }
1331            CmsFile file = cms.readFile(resource);
1332            InputStream contentInput = contentStream.getStream();
1333            byte[] newContent = CmsFileUtil.readFully(contentInput);
1334            file.setContents(newContent);
1335            boolean wasLocked = ensureLock(cms, resource);
1336            CmsFile newFile = cms.writeFile(file);
1337            if (wasLocked) {
1338                cms.unlockResource(newFile);
1339            }
1340        } catch (CmsException e) {
1341            handleCmsException(e);
1342        } catch (IOException e) {
1343            throw new CmisRuntimeException(e.getLocalizedMessage(), e);
1344        }
1345    }
1346
1347    /**
1348     * @see org.opencms.cmis.I_CmsCmisRepository#setFilter(org.opencms.repository.CmsRepositoryFilter)
1349     */
1350    public void setFilter(CmsRepositoryFilter filter) {
1351
1352        m_filter = filter;
1353        LOG.warn("Filters not supported by CMIS repositories, ignoring configuration...");
1354    }
1355
1356    /**
1357     * @see org.opencms.cmis.I_CmsCmisRepository#setName(java.lang.String)
1358     */
1359    public void setName(String name) {
1360
1361        m_id = name;
1362    }
1363
1364    /**
1365     * @see org.opencms.repository.I_CmsRepository#setTranslation(org.opencms.util.CmsResourceTranslator, boolean)
1366     */
1367    public void setTranslation(CmsResourceTranslator translator, boolean enabled) {
1368
1369        throw new UnsupportedOperationException("File translations not supported by CMIS repository.");
1370    }
1371
1372    /**
1373     * @see org.opencms.cmis.I_CmsCmisRepository#updateProperties(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.spi.Holder, org.apache.chemistry.opencmis.commons.spi.Holder, org.apache.chemistry.opencmis.commons.data.Properties)
1374     */
1375    public synchronized void updateProperties(
1376        CmsCmisCallContext context,
1377        Holder<String> objectId,
1378        Holder<String> changeToken,
1379        Properties properties) {
1380
1381        checkWriteAccess();
1382
1383        try {
1384
1385            CmsObject cms = getCmsObject(context);
1386            CmsUUID structureId = new CmsUUID(objectId.getValue());
1387            CmsResource resource = cms.readResource(structureId);
1388            Map<String, PropertyData<?>> propertyMap = properties.getProperties();
1389            List<CmsProperty> cmsProperties = getOpenCmsProperties(propertyMap);
1390            boolean wasLocked = ensureLock(cms, resource);
1391            try {
1392                cms.writePropertyObjects(resource, cmsProperties);
1393                @SuppressWarnings("unchecked")
1394                PropertyData<String> nameProperty = (PropertyData<String>)propertyMap.get(PropertyIds.NAME);
1395                if (nameProperty != null) {
1396                    String newName = nameProperty.getFirstValue();
1397                    checkResourceName(newName);
1398                    String parentFolder = CmsResource.getParentFolder(resource.getRootPath());
1399                    String newPath = CmsStringUtil.joinPaths(parentFolder, newName);
1400                    cms.moveResource(resource.getRootPath(), newPath);
1401                    resource = cms.readResource(resource.getStructureId());
1402                }
1403
1404                for (String key : properties.getProperties().keySet()) {
1405                    if (key.startsWith(CmsCmisTypeManager.PROPERTY_PREFIX_DYNAMIC)) {
1406                        I_CmsPropertyProvider provider = getTypeManager().getPropertyProvider(key);
1407                        try {
1408                            String value = (String)(properties.getProperties().get(key).getFirstValue());
1409                            provider.setPropertyValue(cms, resource, value);
1410                        } catch (CmsException e) {
1411                            LOG.error(e.getLocalizedMessage(), e);
1412                        }
1413                    }
1414                }
1415            } finally {
1416                if (wasLocked) {
1417                    cms.unlockResource(resource);
1418                }
1419            }
1420        } catch (CmsException e) {
1421            handleCmsException(e);
1422        }
1423    }
1424
1425    /**
1426     * Checks whether we have write access to this repository and throws an exception otherwise.<p>
1427     */
1428    protected void checkWriteAccess() {
1429
1430        if (m_isReadOnly) {
1431            throw new CmisNotSupportedException("Readonly repository '" + m_id + "' does not allow write operations.");
1432        }
1433    }
1434
1435    /**
1436     * Initializes a CMS context for the authentication data contained in a call context.<p>
1437     *
1438     * @param context the call context
1439     * @return the initialized CMS context
1440     */
1441    protected CmsObject getCmsObject(CmsCmisCallContext context) {
1442
1443        try {
1444            if (context.getUsername() == null) {
1445                // user name can be null
1446                CmsObject cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserGuest());
1447                cms.getRequestContext().setCurrentProject(m_adminCms.getRequestContext().getCurrentProject());
1448                return cms;
1449            } else {
1450                CmsObject cms = OpenCms.initCmsObject(m_adminCms);
1451                CmsProject projectBeforeLogin = cms.getRequestContext().getCurrentProject();
1452                cms.loginUser(context.getUsername(), context.getPassword());
1453                cms.getRequestContext().setCurrentProject(projectBeforeLogin);
1454                return cms;
1455            }
1456        } catch (CmsException e) {
1457            throw new CmisPermissionDeniedException(e.getLocalizedMessage(), e);
1458
1459        }
1460    }
1461
1462    /**
1463     *  Gets the relationship data for a given resource.<p>
1464     *
1465     * @param context the call context
1466     * @param cms the CMS context
1467     * @param resource the resource
1468     * @param relationshipDirection the relationship direction
1469     * @param filterSet the property filter
1470     * @param includeAllowableActions true if allowable actions should be included
1471     * @return the list of relationship data
1472     *
1473     * @throws CmsException if something goes wrong
1474     */
1475    protected List<ObjectData> getRelationshipObjectData(
1476        CmsCmisCallContext context,
1477        CmsObject cms,
1478        CmsResource resource,
1479        RelationshipDirection relationshipDirection,
1480        Set<String> filterSet,
1481        boolean includeAllowableActions)
1482    throws CmsException {
1483
1484        List<ObjectData> resultObjects = new ArrayList<ObjectData>();
1485        CmsRelationFilter relationFilter;
1486        if (relationshipDirection == RelationshipDirection.SOURCE) {
1487            relationFilter = CmsRelationFilter.TARGETS;
1488        } else if (relationshipDirection == RelationshipDirection.TARGET) {
1489            relationFilter = CmsRelationFilter.SOURCES;
1490        } else {
1491            relationFilter = CmsRelationFilter.ALL;
1492        }
1493
1494        List<CmsRelation> unfilteredRelations = cms.getRelationsForResource(resource.getRootPath(), relationFilter);
1495        List<CmsRelation> relations = new ArrayList<CmsRelation>();
1496        for (CmsRelation relation : unfilteredRelations) {
1497            if (relation.getTargetId().isNullUUID() || relation.getSourceId().isNullUUID()) {
1498                continue;
1499            }
1500            relations.add(relation);
1501        }
1502        CmsCmisRelationHelper helper = getRelationHelper();
1503        for (CmsRelation relation : relations) {
1504            ObjectData objData = helper.collectObjectData(
1505                context,
1506                cms,
1507                resource,
1508                relation,
1509                filterSet,
1510                includeAllowableActions,
1511                false);
1512            resultObjects.add(objData);
1513        }
1514        return resultObjects;
1515    }
1516
1517    /**
1518     * Gets the rendition providers matching the given filter.<p>
1519     *
1520     * @param filter the rendition filter
1521     *
1522     * @return the rendition providers matching the filter
1523     */
1524    protected List<I_CmsCmisRenditionProvider> getRenditionProviders(CmsCmisRenditionFilter filter) {
1525
1526        List<I_CmsCmisRenditionProvider> result = new ArrayList<I_CmsCmisRenditionProvider>();
1527        for (I_CmsCmisRenditionProvider provider : m_renditionProviders.values()) {
1528            String mimetype = provider.getMimeType();
1529            String kind = provider.getKind();
1530            if (filter.accept(kind, mimetype)) {
1531                result.add(provider);
1532            }
1533        }
1534        return result;
1535    }
1536
1537    /**
1538     * Extracts the resource type from a set of CMIS properties.<p>
1539     *
1540     * @param properties the CMIS properties
1541     * @param defaultValue the default value
1542     *
1543     * @return the resource type property, or the default value if the property was not found
1544     */
1545    protected String getResourceTypeFromProperties(Map<String, PropertyData<?>> properties, String defaultValue) {
1546
1547        PropertyData<?> typeProp = properties.get(CmsCmisTypeManager.PROPERTY_RESOURCE_TYPE);
1548        String resTypeName = defaultValue;
1549        if (typeProp != null) {
1550            resTypeName = (String)typeProp.getFirstValue();
1551        }
1552        return resTypeName;
1553    }
1554
1555    /**
1556     * Gets the type manager instance.<p>
1557     *
1558     * @return the type manager instance
1559     */
1560    protected CmsCmisTypeManager getTypeManager() {
1561
1562        return m_typeManager;
1563    }
1564
1565    /**
1566     * Gets the correct helper object for a given object id to perform operations on the corresponding object.<p>
1567     *
1568     * @param objectId the object id
1569     *
1570     * @return the helper object to use for the given object id
1571     */
1572    I_CmsCmisObjectHelper getHelper(String objectId) {
1573
1574        if (CmsUUID.isValidUUID(objectId)) {
1575            return getResourceHelper();
1576        } else if (CmsCmisRelationHelper.RELATION_PATTERN.matcher(objectId).matches()) {
1577            return getRelationHelper();
1578        } else {
1579            return null;
1580        }
1581    }
1582
1583    /**
1584     * Helper method for executing a query.<p>
1585     *
1586     * @param cms the CMS context to use
1587     * @param index the index to use for the query
1588     * @param query the query to perform
1589     * @param start the start offset
1590     * @param rows the number of results to return
1591     *
1592     * @return the list of search results
1593     * @throws CmsSearchException if something goes wrong
1594     */
1595    CmsSolrResultList solrSearch(CmsObject cms, CmsSolrIndex index, String query, int start, int rows)
1596    throws CmsSearchException {
1597
1598        Map<String, String[]> params = new HashMap<>();
1599        CmsSolrQuery q = new CmsSolrQuery(null, params);
1600        q.setText(query);
1601        q.setStart(new Integer(start));
1602        q.setRows(new Integer(rows));
1603        CmsSolrResultList resultPage = index.search(cms, q, true);
1604        return resultPage;
1605    }
1606
1607    /**
1608     * Helper method to collect the descendants of a given folder.<p>
1609     *
1610     * @param context the call context
1611     * @param cms the CMS context
1612     * @param folder the parent folder
1613     * @param list the list to which the descendants should be added
1614     * @param foldersOnly flag to exclude files from the result
1615     * @param depth the maximum depth
1616     * @param filter the property filter
1617     * @param includeAllowableActions flag to include allowable actions
1618     * @param includePathSegments flag to include path segments
1619     */
1620    private void gatherDescendants(
1621        CmsCmisCallContext context,
1622        CmsObject cms,
1623        CmsResource folder,
1624        List<ObjectInFolderContainer> list,
1625        boolean foldersOnly,
1626        int depth,
1627        Set<String> filter,
1628        boolean includeAllowableActions,
1629        boolean includePathSegments) {
1630
1631        try {
1632            CmsCmisResourceHelper helper = getResourceHelper();
1633            List<CmsResource> children = cms.getResourcesInFolder(cms.getSitePath(folder), CmsResourceFilter.DEFAULT);
1634            Collections.sort(children, new Comparator<CmsResource>() {
1635
1636                public int compare(CmsResource a, CmsResource b) {
1637
1638                    return a.getName().compareTo(b.getName());
1639                }
1640            });
1641            // iterate through children
1642            for (CmsResource child : children) {
1643
1644                // folders only?
1645                if (foldersOnly && !child.isFolder()) {
1646                    continue;
1647                }
1648
1649                // add to list
1650                ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
1651                objectInFolder.setObject(
1652                    helper.collectObjectData(
1653                        context,
1654                        cms,
1655                        child,
1656                        filter,
1657                        "cmis:none",
1658                        includeAllowableActions,
1659                        false,
1660                        IncludeRelationships.NONE));
1661                if (includePathSegments) {
1662                    objectInFolder.setPathSegment(child.getName());
1663                }
1664
1665                ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl();
1666                container.setObject(objectInFolder);
1667
1668                list.add(container);
1669
1670                // move to next level
1671                if ((depth != 1) && child.isFolder()) {
1672                    container.setChildren(new ArrayList<ObjectInFolderContainer>());
1673                    gatherDescendants(
1674                        context,
1675                        cms,
1676                        child,
1677                        container.getChildren(),
1678                        foldersOnly,
1679                        depth - 1,
1680                        filter,
1681                        includeAllowableActions,
1682                        includePathSegments);
1683                }
1684            }
1685        } catch (CmsException e) {
1686            handleCmsException(e);
1687        }
1688    }
1689
1690    /**
1691     * Gets the index to use for queries.<p>
1692     *
1693     * @return the index to use for queries
1694     */
1695    private CmsSolrIndex getIndex() {
1696
1697        String indexName = m_indexName;
1698        if (indexName == null) {
1699            return null;
1700        }
1701        return OpenCms.getSearchManager().getIndexSolr(indexName);
1702    }
1703
1704    /**
1705     * Gets the relation object helper.<p>
1706     *
1707     * @return the relation object helper
1708     */
1709    private CmsCmisRelationHelper getRelationHelper() {
1710
1711        return m_relationHelper;
1712    }
1713
1714    /**
1715     * Gets the resource object helper.<p>
1716     *
1717     * @return the resource object helper
1718     */
1719    private CmsCmisResourceHelper getResourceHelper() {
1720
1721        return m_resourceHelper;
1722    }
1723
1724}