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.CmsRequestUtil;
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.cmis.I_CmsCmisRepository#getTypeChildren(org.opencms.cmis.CmsCmisCallContext, java.lang.String, boolean, java.math.BigInteger, java.math.BigInteger)
1115     */
1116    public synchronized TypeDefinitionList getTypeChildren(
1117        CmsCmisCallContext context,
1118        String typeId,
1119        boolean includePropertyDefinitions,
1120        BigInteger maxItems,
1121        BigInteger skipCount) {
1122
1123        return m_typeManager.getTypeChildren(typeId, includePropertyDefinitions, maxItems, skipCount);
1124    }
1125
1126    /**
1127     * @see org.opencms.cmis.I_CmsCmisRepository#getTypeDefinition(org.opencms.cmis.CmsCmisCallContext, java.lang.String)
1128     */
1129    public synchronized TypeDefinition getTypeDefinition(CmsCmisCallContext context, String typeId) {
1130
1131        return m_typeManager.getTypeDefinition(typeId);
1132    }
1133
1134    /**
1135     * @see org.opencms.cmis.I_CmsCmisRepository#getTypeDescendants(org.opencms.cmis.CmsCmisCallContext, java.lang.String, java.math.BigInteger, boolean)
1136     */
1137    public synchronized List<TypeDefinitionContainer> getTypeDescendants(
1138        CmsCmisCallContext context,
1139        String typeId,
1140        BigInteger depth,
1141        boolean includePropertyDefinitions) {
1142
1143        return m_typeManager.getTypeDescendants(typeId, depth, includePropertyDefinitions);
1144    }
1145
1146    /**
1147     * @see org.opencms.cmis.I_CmsCmisRepository#initConfiguration()
1148     */
1149    public void initConfiguration() throws CmsConfigurationException {
1150
1151        if (m_filter != null) {
1152            m_filter.initConfiguration();
1153        }
1154        m_description = m_parameterConfiguration.getString(PARAM_DESCRIPTION, null);
1155        List<String> renditionProviderClasses = m_parameterConfiguration.getList(
1156            PARAM_RENDITION,
1157            Collections.<String> emptyList());
1158        for (String className : renditionProviderClasses) {
1159            try {
1160                I_CmsCmisRenditionProvider provider = (I_CmsCmisRenditionProvider)(Class.forName(
1161                    className).newInstance());
1162                String id = provider.getId();
1163                m_renditionProviders.put(id, provider);
1164            } catch (Throwable e) {
1165                LOG.error(e.getLocalizedMessage(), e);
1166            }
1167        }
1168        List<String> propertyProviderClasses = m_parameterConfiguration.getList(
1169            PARAM_PROPERTY,
1170            Collections.<String> emptyList());
1171        for (String className : propertyProviderClasses) {
1172            try {
1173                I_CmsPropertyProvider provider = (I_CmsPropertyProvider)(Class.forName(className).newInstance());
1174                m_propertyProviders.add(provider);
1175            } catch (Throwable e) {
1176                LOG.error(e.getLocalizedMessage(), e);
1177            }
1178        }
1179        m_indexName = m_parameterConfiguration.getString(PARAM_INDEX, null);
1180    }
1181
1182    /**
1183     * @see org.opencms.cmis.I_CmsCmisRepository#initializeCms(org.opencms.file.CmsObject)
1184     */
1185    public void initializeCms(CmsObject cms) throws CmsException {
1186
1187        m_adminCms = cms;
1188        m_typeManager = new CmsCmisTypeManager(cms, m_propertyProviders);
1189        String projectName = m_parameterConfiguration.getString(PARAM_PROJECT, CmsProject.ONLINE_PROJECT_NAME);
1190        CmsResource root = m_adminCms.readResource("/");
1191        CmsObject offlineCms = OpenCms.initCmsObject(m_adminCms);
1192        CmsProject project = m_adminCms.readProject(projectName);
1193        m_project = project;
1194        offlineCms.getRequestContext().setCurrentProject(project);
1195        m_adminCms = offlineCms;
1196        m_root = root;
1197        m_isReadOnly = project.isOnlineProject();
1198    }
1199
1200    /**
1201     * @see org.opencms.cmis.I_CmsCmisRepository#moveObject(org.opencms.cmis.CmsCmisCallContext, org.apache.chemistry.opencmis.commons.spi.Holder, java.lang.String, java.lang.String)
1202     */
1203    public synchronized void moveObject(
1204        CmsCmisCallContext context,
1205        Holder<String> objectId,
1206        String targetFolderId,
1207        String sourceFolderId) {
1208
1209        checkWriteAccess();
1210
1211        try {
1212            CmsObject cms = getCmsObject(context);
1213            CmsUUID structureId = new CmsUUID(objectId.getValue());
1214            CmsUUID targetStructureId = new CmsUUID(targetFolderId);
1215            CmsResource targetFolder = cms.readResource(targetStructureId);
1216            CmsResource resourceToMove = cms.readResource(structureId);
1217            String name = CmsResource.getName(resourceToMove.getRootPath());
1218            String newPath = CmsStringUtil.joinPaths(targetFolder.getRootPath(), name);
1219            boolean wasLocked = ensureLock(cms, resourceToMove);
1220            try {
1221                cms.moveResource(resourceToMove.getRootPath(), newPath);
1222            } finally {
1223                if (wasLocked) {
1224                    CmsResource movedResource = cms.readResource(resourceToMove.getStructureId());
1225                    cms.unlockResource(movedResource);
1226                }
1227            }
1228        } catch (CmsException e) {
1229            handleCmsException(e);
1230        }
1231    }
1232
1233    /**
1234     * @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)
1235     */
1236    @Override
1237    public synchronized ObjectList query(
1238        CmsCmisCallContext context,
1239        String statement,
1240        boolean searchAllVersions,
1241        boolean includeAllowableActions,
1242        IncludeRelationships includeRelationships,
1243        String renditionFilter,
1244        BigInteger maxItems,
1245        BigInteger skipCount) {
1246
1247        try {
1248            CmsObject cms = getCmsObject(context);
1249            CmsSolrIndex index = getIndex();
1250            CmsCmisResourceHelper helper = getResourceHelper();
1251
1252            // split filter
1253            Set<String> filterCollection = null;
1254            // skip and max
1255            int skip = (skipCount == null ? 0 : skipCount.intValue());
1256            if (skip < 0) {
1257                skip = 0;
1258            }
1259
1260            int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
1261            if (max < 0) {
1262                max = Integer.MAX_VALUE;
1263            }
1264            CmsSolrResultList results = solrSearch(cms, index, statement, skip, max);
1265            ObjectListImpl resultObjectList = new ObjectListImpl();
1266            List<ObjectData> objectDataList = new ArrayList<ObjectData>();
1267            resultObjectList.setObjects(objectDataList);
1268            for (CmsResource resource : results) {
1269                // build and add child object
1270                objectDataList.add(
1271                    helper.collectObjectData(
1272                        context,
1273                        cms,
1274                        resource,
1275                        filterCollection,
1276                        renditionFilter,
1277                        includeAllowableActions,
1278                        false,
1279                        includeRelationships));
1280            }
1281            resultObjectList.setHasMoreItems(Boolean.valueOf(!results.isEmpty()));
1282            resultObjectList.setNumItems(BigInteger.valueOf(results.getVisibleHitCount()));
1283            return resultObjectList;
1284        } catch (CmsException e) {
1285            handleCmsException(e);
1286            return null;
1287        }
1288
1289    }
1290
1291    /**
1292     * @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)
1293     */
1294    public synchronized void setContentStream(
1295        CmsCmisCallContext context,
1296        Holder<String> objectId,
1297        boolean overwriteFlag,
1298        Holder<String> changeToken,
1299        ContentStream contentStream) {
1300
1301        checkWriteAccess();
1302
1303        try {
1304            CmsObject cms = getCmsObject(context);
1305            CmsUUID structureId = new CmsUUID(objectId.getValue());
1306            if (!overwriteFlag) {
1307                throw new CmisContentAlreadyExistsException();
1308            }
1309            CmsResource resource = cms.readResource(structureId);
1310            if (resource.isFolder()) {
1311                throw new CmisStreamNotSupportedException("Folders may not have content streams.");
1312            }
1313            CmsFile file = cms.readFile(resource);
1314            InputStream contentInput = contentStream.getStream();
1315            byte[] newContent = CmsFileUtil.readFully(contentInput);
1316            file.setContents(newContent);
1317            boolean wasLocked = ensureLock(cms, resource);
1318            CmsFile newFile = cms.writeFile(file);
1319            if (wasLocked) {
1320                cms.unlockResource(newFile);
1321            }
1322        } catch (CmsException e) {
1323            handleCmsException(e);
1324        } catch (IOException e) {
1325            throw new CmisRuntimeException(e.getLocalizedMessage(), e);
1326        }
1327    }
1328
1329    /**
1330     * @see org.opencms.cmis.I_CmsCmisRepository#setFilter(org.opencms.repository.CmsRepositoryFilter)
1331     */
1332    public void setFilter(CmsRepositoryFilter filter) {
1333
1334        m_filter = filter;
1335        LOG.warn("Filters not supported by CMIS repositories, ignoring configuration...");
1336    }
1337
1338    /**
1339     * @see org.opencms.cmis.I_CmsCmisRepository#setName(java.lang.String)
1340     */
1341    public void setName(String name) {
1342
1343        m_id = name;
1344    }
1345
1346    /**
1347     * @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)
1348     */
1349    public synchronized void updateProperties(
1350        CmsCmisCallContext context,
1351        Holder<String> objectId,
1352        Holder<String> changeToken,
1353        Properties properties) {
1354
1355        checkWriteAccess();
1356
1357        try {
1358
1359            CmsObject cms = getCmsObject(context);
1360            CmsUUID structureId = new CmsUUID(objectId.getValue());
1361            CmsResource resource = cms.readResource(structureId);
1362            Map<String, PropertyData<?>> propertyMap = properties.getProperties();
1363            List<CmsProperty> cmsProperties = getOpenCmsProperties(propertyMap);
1364            boolean wasLocked = ensureLock(cms, resource);
1365            try {
1366                cms.writePropertyObjects(resource, cmsProperties);
1367                @SuppressWarnings("unchecked")
1368                PropertyData<String> nameProperty = (PropertyData<String>)propertyMap.get(PropertyIds.NAME);
1369                if (nameProperty != null) {
1370                    String newName = nameProperty.getFirstValue();
1371                    checkResourceName(newName);
1372                    String parentFolder = CmsResource.getParentFolder(resource.getRootPath());
1373                    String newPath = CmsStringUtil.joinPaths(parentFolder, newName);
1374                    cms.moveResource(resource.getRootPath(), newPath);
1375                    resource = cms.readResource(resource.getStructureId());
1376                }
1377
1378                for (String key : properties.getProperties().keySet()) {
1379                    if (key.startsWith(CmsCmisTypeManager.PROPERTY_PREFIX_DYNAMIC)) {
1380                        I_CmsPropertyProvider provider = getTypeManager().getPropertyProvider(key);
1381                        try {
1382                            String value = (String)(properties.getProperties().get(key).getFirstValue());
1383                            provider.setPropertyValue(cms, resource, value);
1384                        } catch (CmsException e) {
1385                            LOG.error(e.getLocalizedMessage(), e);
1386                        }
1387                    }
1388                }
1389            } finally {
1390                if (wasLocked) {
1391                    cms.unlockResource(resource);
1392                }
1393            }
1394        } catch (CmsException e) {
1395            handleCmsException(e);
1396        }
1397    }
1398
1399    /**
1400     * Checks whether we have write access to this repository and throws an exception otherwise.<p>
1401     */
1402    protected void checkWriteAccess() {
1403
1404        if (m_isReadOnly) {
1405            throw new CmisNotSupportedException("Readonly repository '" + m_id + "' does not allow write operations.");
1406        }
1407    }
1408
1409    /**
1410     * Initializes a CMS context for the authentication data contained in a call context.<p>
1411     *
1412     * @param context the call context
1413     * @return the initialized CMS context
1414     */
1415    protected CmsObject getCmsObject(CmsCmisCallContext context) {
1416
1417        try {
1418            if (context.getUsername() == null) {
1419                // user name can be null
1420                CmsObject cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserGuest());
1421                cms.getRequestContext().setCurrentProject(m_adminCms.getRequestContext().getCurrentProject());
1422                return cms;
1423            } else {
1424                CmsObject cms = OpenCms.initCmsObject(m_adminCms);
1425                CmsProject projectBeforeLogin = cms.getRequestContext().getCurrentProject();
1426                cms.loginUser(context.getUsername(), context.getPassword());
1427                cms.getRequestContext().setCurrentProject(projectBeforeLogin);
1428                return cms;
1429            }
1430        } catch (CmsException e) {
1431            throw new CmisPermissionDeniedException(e.getLocalizedMessage(), e);
1432
1433        }
1434    }
1435
1436    /**
1437     *  Gets the relationship data for a given resource.<p>
1438     *
1439     * @param context the call context
1440     * @param cms the CMS context
1441     * @param resource the resource
1442     * @param relationshipDirection the relationship direction
1443     * @param filterSet the property filter
1444     * @param includeAllowableActions true if allowable actions should be included
1445     * @return the list of relationship data
1446     *
1447     * @throws CmsException if something goes wrong
1448     */
1449    protected List<ObjectData> getRelationshipObjectData(
1450        CmsCmisCallContext context,
1451        CmsObject cms,
1452        CmsResource resource,
1453        RelationshipDirection relationshipDirection,
1454        Set<String> filterSet,
1455        boolean includeAllowableActions)
1456    throws CmsException {
1457
1458        List<ObjectData> resultObjects = new ArrayList<ObjectData>();
1459        CmsRelationFilter relationFilter;
1460        if (relationshipDirection == RelationshipDirection.SOURCE) {
1461            relationFilter = CmsRelationFilter.TARGETS;
1462        } else if (relationshipDirection == RelationshipDirection.TARGET) {
1463            relationFilter = CmsRelationFilter.SOURCES;
1464        } else {
1465            relationFilter = CmsRelationFilter.ALL;
1466        }
1467
1468        List<CmsRelation> unfilteredRelations = cms.getRelationsForResource(resource.getRootPath(), relationFilter);
1469        List<CmsRelation> relations = new ArrayList<CmsRelation>();
1470        for (CmsRelation relation : unfilteredRelations) {
1471            if (relation.getTargetId().isNullUUID() || relation.getSourceId().isNullUUID()) {
1472                continue;
1473            }
1474            relations.add(relation);
1475        }
1476        CmsCmisRelationHelper helper = getRelationHelper();
1477        for (CmsRelation relation : relations) {
1478            ObjectData objData = helper.collectObjectData(
1479                context,
1480                cms,
1481                resource,
1482                relation,
1483                filterSet,
1484                includeAllowableActions,
1485                false);
1486            resultObjects.add(objData);
1487        }
1488        return resultObjects;
1489    }
1490
1491    /**
1492     * Gets the rendition providers matching the given filter.<p>
1493     *
1494     * @param filter the rendition filter
1495     *
1496     * @return the rendition providers matching the filter
1497     */
1498    protected List<I_CmsCmisRenditionProvider> getRenditionProviders(CmsCmisRenditionFilter filter) {
1499
1500        List<I_CmsCmisRenditionProvider> result = new ArrayList<I_CmsCmisRenditionProvider>();
1501        for (I_CmsCmisRenditionProvider provider : m_renditionProviders.values()) {
1502            String mimetype = provider.getMimeType();
1503            String kind = provider.getKind();
1504            if (filter.accept(kind, mimetype)) {
1505                result.add(provider);
1506            }
1507        }
1508        return result;
1509    }
1510
1511    /**
1512     * Extracts the resource type from a set of CMIS properties.<p>
1513     *
1514     * @param properties the CMIS properties
1515     * @param defaultValue the default value
1516     *
1517     * @return the resource type property, or the default value if the property was not found
1518     */
1519    protected String getResourceTypeFromProperties(Map<String, PropertyData<?>> properties, String defaultValue) {
1520
1521        PropertyData<?> typeProp = properties.get(CmsCmisTypeManager.PROPERTY_RESOURCE_TYPE);
1522        String resTypeName = defaultValue;
1523        if (typeProp != null) {
1524            resTypeName = (String)typeProp.getFirstValue();
1525        }
1526        return resTypeName;
1527    }
1528
1529    /**
1530     * Gets the type manager instance.<p>
1531     *
1532     * @return the type manager instance
1533     */
1534    protected CmsCmisTypeManager getTypeManager() {
1535
1536        return m_typeManager;
1537    }
1538
1539    /**
1540     * Gets the correct helper object for a given object id to perform operations on the corresponding object.<p>
1541     *
1542     * @param objectId the object id
1543     *
1544     * @return the helper object to use for the given object id
1545     */
1546    I_CmsCmisObjectHelper getHelper(String objectId) {
1547
1548        if (CmsUUID.isValidUUID(objectId)) {
1549            return getResourceHelper();
1550        } else if (CmsCmisRelationHelper.RELATION_PATTERN.matcher(objectId).matches()) {
1551            return getRelationHelper();
1552        } else {
1553            return null;
1554        }
1555    }
1556
1557    /**
1558     * Helper method for executing a query.<p>
1559     *
1560     * @param cms the CMS context to use
1561     * @param index the index to use for the query
1562     * @param query the query to perform
1563     * @param start the start offset
1564     * @param rows the number of results to return
1565     *
1566     * @return the list of search results
1567     * @throws CmsSearchException if something goes wrong
1568     */
1569    CmsSolrResultList solrSearch(CmsObject cms, CmsSolrIndex index, String query, int start, int rows)
1570    throws CmsSearchException {
1571
1572        CmsSolrQuery q = new CmsSolrQuery(null, CmsRequestUtil.createParameterMap(query));
1573        q.setStart(new Integer(start));
1574        q.setRows(new Integer(rows));
1575        CmsSolrResultList resultPage = index.search(cms, q, true);
1576        return resultPage;
1577    }
1578
1579    /**
1580     * Helper method to collect the descendants of a given folder.<p>
1581     *
1582     * @param context the call context
1583     * @param cms the CMS context
1584     * @param folder the parent folder
1585     * @param list the list to which the descendants should be added
1586     * @param foldersOnly flag to exclude files from the result
1587     * @param depth the maximum depth
1588     * @param filter the property filter
1589     * @param includeAllowableActions flag to include allowable actions
1590     * @param includePathSegments flag to include path segments
1591     */
1592    private void gatherDescendants(
1593        CmsCmisCallContext context,
1594        CmsObject cms,
1595        CmsResource folder,
1596        List<ObjectInFolderContainer> list,
1597        boolean foldersOnly,
1598        int depth,
1599        Set<String> filter,
1600        boolean includeAllowableActions,
1601        boolean includePathSegments) {
1602
1603        try {
1604            CmsCmisResourceHelper helper = getResourceHelper();
1605            List<CmsResource> children = cms.getResourcesInFolder(cms.getSitePath(folder), CmsResourceFilter.DEFAULT);
1606            Collections.sort(children, new Comparator<CmsResource>() {
1607
1608                public int compare(CmsResource a, CmsResource b) {
1609
1610                    return a.getName().compareTo(b.getName());
1611                }
1612            });
1613            // iterate through children
1614            for (CmsResource child : children) {
1615
1616                // folders only?
1617                if (foldersOnly && !child.isFolder()) {
1618                    continue;
1619                }
1620
1621                // add to list
1622                ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
1623                objectInFolder.setObject(
1624                    helper.collectObjectData(
1625                        context,
1626                        cms,
1627                        child,
1628                        filter,
1629                        "cmis:none",
1630                        includeAllowableActions,
1631                        false,
1632                        IncludeRelationships.NONE));
1633                if (includePathSegments) {
1634                    objectInFolder.setPathSegment(child.getName());
1635                }
1636
1637                ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl();
1638                container.setObject(objectInFolder);
1639
1640                list.add(container);
1641
1642                // move to next level
1643                if ((depth != 1) && child.isFolder()) {
1644                    container.setChildren(new ArrayList<ObjectInFolderContainer>());
1645                    gatherDescendants(
1646                        context,
1647                        cms,
1648                        child,
1649                        container.getChildren(),
1650                        foldersOnly,
1651                        depth - 1,
1652                        filter,
1653                        includeAllowableActions,
1654                        includePathSegments);
1655                }
1656            }
1657        } catch (CmsException e) {
1658            handleCmsException(e);
1659        }
1660    }
1661
1662    /**
1663     * Gets the index to use for queries.<p>
1664     *
1665     * @return the index to use for queries
1666     */
1667    private CmsSolrIndex getIndex() {
1668
1669        String indexName = m_indexName;
1670        if (indexName == null) {
1671            return null;
1672        }
1673        return OpenCms.getSearchManager().getIndexSolr(indexName);
1674    }
1675
1676    /**
1677     * Gets the relation object helper.<p>
1678     *
1679     * @return the relation object helper
1680     */
1681    private CmsCmisRelationHelper getRelationHelper() {
1682
1683        return m_relationHelper;
1684    }
1685
1686    /**
1687     * Gets the resource object helper.<p>
1688     *
1689     * @return the resource object helper
1690     */
1691    private CmsCmisResourceHelper getResourceHelper() {
1692
1693        return m_resourceHelper;
1694    }
1695
1696}