001/*
002 * Copyright (c) 2006-2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Florent Guillaume
011 */
012package org.nuxeo.ecm.core.opencmis.impl.server;
013
014import static org.apache.chemistry.opencmis.commons.BasicPermissions.ALL;
015import static org.apache.chemistry.opencmis.commons.BasicPermissions.READ;
016import static org.apache.chemistry.opencmis.commons.BasicPermissions.WRITE;
017import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_POLICY_OBJECT;
018import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_POLICY_POLICY;
019import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_TO_FOLDER_FOLDER;
020import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_TO_FOLDER_OBJECT;
021import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_APPLY_ACL_OBJECT;
022import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT;
023import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CHECKIN_DOCUMENT;
024import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CHECKOUT_DOCUMENT;
025import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER;
026import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_FOLDER_FOLDER;
027import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_RELATIONSHIP_SOURCE;
028import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_RELATIONSHIP_TARGET;
029import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT;
030import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_DELETE_OBJECT;
031import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_DELETE_TREE_FOLDER;
032import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_ACL_OBJECT;
033import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES;
034import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_APPLIED_POLICIES_OBJECT;
035import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_CHILDREN_FOLDER;
036import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_DESCENDENTS_FOLDER;
037import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT;
038import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT;
039import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_PARENTS_FOLDER;
040import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_PROPERTIES_OBJECT;
041import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_MOVE_OBJECT;
042import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_MOVE_SOURCE;
043import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_MOVE_TARGET;
044import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_FROM_FOLDER_FOLDER;
045import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_FROM_FOLDER_OBJECT;
046import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_POLICY_OBJECT;
047import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_POLICY_POLICY;
048import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_SET_CONTENT_DOCUMENT;
049import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT;
050import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_VIEW_CONTENT_OBJECT;
051
052import java.lang.reflect.Field;
053import java.math.BigInteger;
054import java.util.ArrayList;
055import java.util.Arrays;
056import java.util.Collections;
057import java.util.HashMap;
058import java.util.HashSet;
059import java.util.LinkedList;
060import java.util.List;
061import java.util.Map;
062import java.util.Map.Entry;
063import java.util.Set;
064
065import javax.servlet.http.HttpServletRequest;
066
067import org.apache.chemistry.opencmis.commons.data.ExtensionFeature;
068import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
069import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
070import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
071import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
072import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
073import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
074import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
075import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
076import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
077import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
078import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
079import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
080import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
081import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
082import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
083import org.apache.chemistry.opencmis.commons.enums.SupportedPermissions;
084import org.apache.chemistry.opencmis.commons.impl.dataobjects.AclCapabilitiesDataImpl;
085import org.apache.chemistry.opencmis.commons.impl.dataobjects.CreatablePropertyTypesImpl;
086import org.apache.chemistry.opencmis.commons.impl.dataobjects.NewTypeSettableAttributesImpl;
087import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionDefinitionDataImpl;
088import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionMappingDataImpl;
089import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl;
090import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl;
091import org.apache.chemistry.opencmis.commons.server.CallContext;
092
093import org.nuxeo.common.Environment;
094import org.nuxeo.ecm.core.api.security.PermissionProvider;
095import org.nuxeo.ecm.core.api.security.SecurityConstants;
096import org.nuxeo.ecm.core.opencmis.impl.util.TypeManagerImpl;
097import org.nuxeo.ecm.core.schema.DocumentType;
098import org.nuxeo.ecm.core.schema.SchemaManager;
099import org.nuxeo.ecm.core.schema.types.CompositeType;
100import org.nuxeo.ecm.core.security.DefaultPermissionProvider;
101import org.nuxeo.ecm.core.security.PermissionVisibilityDescriptor;
102import org.nuxeo.runtime.api.Framework;
103
104/**
105 * Information about a Nuxeo repository.
106 */
107public class NuxeoRepository {
108
109    /**
110     * @deprecated Since 7.10. Use {@link Environment#DISTRIBUTION_VERSION}
111     */
112    @Deprecated
113    public static final String NUXEO_VERSION_PROP = Environment.DISTRIBUTION_VERSION;
114
115    public static final String NUXEO_URL_PROP = "nuxeo.url";
116
117    public static final String SUPPORTS_JOINS_PROP = "org.nuxeo.cmis.joins";
118
119    public static final String ELASTICSEARCH_PROP = "org.nuxeo.cmis.elasticsearch";
120
121    private static final String NUXEO_CONTEXT_PATH_PROP = "org.nuxeo.ecm.contextPath";
122
123    private static final String NUXEO_CONTEXT_PATH_DEFAULT = "/nuxeo";
124
125    private static final String X_FORWARDED_HOST = "x-forwarded-host";
126
127    private static final String NUXEO_VH_HEADER = "nuxeo-virtual-host";
128
129    private static final String VH_PARAM = "nuxeo.virtual.host";
130
131    public static final String NUXEO_READ_REMOVE = "ReadRemove";
132
133    protected final String repositoryId;
134
135    protected final String rootFolderId;
136
137    protected boolean supportsJoins;
138
139    protected boolean useElasticsearch;
140
141    protected TypeManagerImpl typeManager;
142
143    protected CmisVersion cmisVersion;
144
145    public NuxeoRepository(String repositoryId, String rootFolderId) {
146        this.repositoryId = repositoryId;
147        this.rootFolderId = rootFolderId;
148        if (Framework.isBooleanPropertyTrue(SUPPORTS_JOINS_PROP)) {
149            setSupportsJoins(true);
150        }
151        if (Framework.isBooleanPropertyTrue(ELASTICSEARCH_PROP)) {
152            setUseElasticsearch(true);
153        }
154    }
155
156    public void setSupportsJoins(boolean supportsJoins) {
157        this.supportsJoins = supportsJoins;
158    }
159
160    public boolean supportsJoins() {
161        return supportsJoins;
162    }
163
164    public void setUseElasticsearch(boolean useElasticsearch) {
165        this.useElasticsearch = useElasticsearch;
166    }
167
168    public boolean useElasticsearch() {
169        return useElasticsearch;
170    }
171
172    public String getId() {
173        return repositoryId;
174    }
175
176    // no need to have it synchronized
177    public TypeManagerImpl getTypeManager() {
178        if (typeManager == null) {
179            typeManager = initializeTypes();
180        }
181        return typeManager;
182    }
183
184    protected TypeManagerImpl initializeTypes() {
185        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
186        // scan the types to find super/inherited relationships
187        Map<String, List<String>> typesChildren = new HashMap<>();
188        for (DocumentType dt : schemaManager.getDocumentTypes()) {
189            org.nuxeo.ecm.core.schema.types.Type st = dt.getSuperType();
190            if (st == null) {
191                continue;
192            }
193            String name = st.getName();
194            List<String> siblings = typesChildren.get(name);
195            if (siblings == null) {
196                siblings = new LinkedList<>();
197                typesChildren.put(name, siblings);
198            }
199            siblings.add(dt.getName());
200        }
201        // convert the transitive closure for Folder and Document subtypes
202        Set<String> done = new HashSet<>();
203        TypeManagerImpl typeManager = new TypeManagerImpl();
204        typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_DOCUMENT, schemaManager, cmisVersion));
205        typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_FOLDER, schemaManager, cmisVersion));
206        typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_RELATIONSHIP, schemaManager, cmisVersion));
207        if (cmisVersion != CmisVersion.CMIS_1_0) {
208            typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_SECONDARY, schemaManager, cmisVersion));
209        }
210        addTypesRecursively(typeManager, NuxeoTypeHelper.NUXEO_DOCUMENT, typesChildren, done, schemaManager);
211        addTypesRecursively(typeManager, NuxeoTypeHelper.NUXEO_FOLDER, typesChildren, done, schemaManager);
212        addTypesRecursively(typeManager, NuxeoTypeHelper.NUXEO_RELATION, typesChildren, done, schemaManager);
213        if (cmisVersion != CmisVersion.CMIS_1_0) {
214            addSecondaryTypes(typeManager, schemaManager);
215        }
216        return typeManager;
217    }
218
219    protected void addTypesRecursively(TypeManagerImpl typeManager, String name,
220            Map<String, List<String>> typesChildren, Set<String> done, SchemaManager schemaManager) {
221        if (done.contains(name)) {
222            return;
223        }
224        done.add(name);
225        DocumentType dt = schemaManager.getDocumentType(name);
226        String parentTypeId = NuxeoTypeHelper.getParentTypeId(dt);
227        if (parentTypeId != null) {
228            TypeDefinitionContainer parentType = typeManager.getTypeById(parentTypeId);
229            if (parentType == null) {
230                // if parent was ignored, reparent under cmis:document
231                parentTypeId = BaseTypeId.CMIS_DOCUMENT.value();
232            } else {
233                if (parentType.getTypeDefinition().getBaseTypeId() != BaseTypeId.CMIS_FOLDER && dt.isFolder()) {
234                    // reparent Folderish but child of Document under
235                    // cmis:folder
236                    parentTypeId = BaseTypeId.CMIS_FOLDER.value();
237                }
238            }
239            typeManager.addTypeDefinition(NuxeoTypeHelper.constructDocumentType(dt, parentTypeId, cmisVersion));
240        }
241        // recurse in children
242        List<String> children = typesChildren.get(name);
243        if (children == null) {
244            return;
245        }
246        for (String sub : children) {
247            addTypesRecursively(typeManager, sub, typesChildren, done, schemaManager);
248        }
249    }
250
251    protected void addSecondaryTypes(TypeManagerImpl typeManager, SchemaManager schemaManager) {
252        for (CompositeType type : schemaManager.getFacets()) {
253            typeManager.addTypeDefinition(NuxeoTypeHelper.constructSecondaryType(type, cmisVersion), false);
254        }
255    }
256
257    public String getRootFolderId() {
258        return rootFolderId;
259    }
260
261    public RepositoryInfo getRepositoryInfo(String latestChangeLogToken, CallContext callContext) {
262        cmisVersion = callContext.getCmisVersion();
263
264        RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl();
265        repositoryInfo.setId(repositoryId);
266        repositoryInfo.setName("Nuxeo Repository " + repositoryId);
267        repositoryInfo.setDescription("Nuxeo Repository " + repositoryId);
268        repositoryInfo.setCmisVersionSupported(cmisVersion.value());
269        repositoryInfo.setPrincipalAnonymous("Guest"); // TODO
270        repositoryInfo.setPrincipalAnyone(SecurityConstants.EVERYONE);
271        repositoryInfo.setThinClientUri(getBaseURL(callContext));
272        repositoryInfo.setChangesIncomplete(Boolean.FALSE);
273        repositoryInfo.setChangesOnType(Arrays.asList(BaseTypeId.CMIS_DOCUMENT, BaseTypeId.CMIS_FOLDER));
274        repositoryInfo.setLatestChangeLogToken(latestChangeLogToken);
275        repositoryInfo.setVendorName("Nuxeo");
276        repositoryInfo.setProductName("Nuxeo OpenCMIS Connector");
277        String version = Framework.getProperty(Environment.DISTRIBUTION_VERSION, "5.5 dev");
278        repositoryInfo.setProductVersion(version);
279        repositoryInfo.setRootFolder(rootFolderId);
280        repositoryInfo.setExtensionFeature(Collections.<ExtensionFeature> emptyList());
281
282        // capabilities
283
284        RepositoryCapabilitiesImpl caps = new RepositoryCapabilitiesImpl();
285        caps.setAllVersionsSearchable(Boolean.TRUE);
286        caps.setCapabilityAcl(CapabilityAcl.MANAGE);
287        caps.setCapabilityChanges(CapabilityChanges.OBJECTIDSONLY);
288        caps.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.PWCONLY);
289        caps.setCapabilityJoin(supportsJoins ? CapabilityJoin.INNERANDOUTER : CapabilityJoin.NONE);
290        caps.setCapabilityQuery(CapabilityQuery.BOTHCOMBINED);
291        caps.setCapabilityRendition(CapabilityRenditions.READ);
292        caps.setIsPwcSearchable(Boolean.TRUE);
293        caps.setIsPwcUpdatable(Boolean.TRUE);
294        caps.setSupportsGetDescendants(Boolean.TRUE);
295        caps.setSupportsGetFolderTree(Boolean.TRUE);
296        caps.setSupportsMultifiling(Boolean.FALSE);
297        caps.setSupportsUnfiling(Boolean.FALSE);
298        caps.setSupportsVersionSpecificFiling(Boolean.FALSE);
299        caps.setNewTypeSettableAttributes(new NewTypeSettableAttributesImpl());
300        caps.setCreatablePropertyTypes(new CreatablePropertyTypesImpl());
301        repositoryInfo.setCapabilities(caps);
302
303        // ACL capabilities
304
305        AclCapabilitiesDataImpl aclCaps = new AclCapabilitiesDataImpl();
306        aclCaps.setAclPropagation(AclPropagation.PROPAGATE);
307        aclCaps.setSupportedPermissions(SupportedPermissions.REPOSITORY);
308
309        List<PermissionDefinition> permDefs = new ArrayList<>();
310        addPermissionDefinitions(permDefs);
311        aclCaps.setPermissionDefinitionData(permDefs);
312
313        Map<String, PermissionMapping> permMap = new HashMap<>();
314        addPermissionMapping(permMap, CAN_GET_DESCENDENTS_FOLDER, READ);
315        addPermissionMapping(permMap, CAN_GET_CHILDREN_FOLDER, READ);
316        addPermissionMapping(permMap, CAN_GET_PARENTS_FOLDER, READ);
317        addPermissionMapping(permMap, CAN_GET_FOLDER_PARENT_OBJECT, READ);
318        addPermissionMapping(permMap, CAN_CREATE_DOCUMENT_FOLDER, WRITE);
319        addPermissionMapping(permMap, CAN_CREATE_FOLDER_FOLDER, WRITE);
320        // no CAN_CREATE_POLICY_FOLDER due to spec bug
321        addPermissionMapping(permMap, CAN_CREATE_RELATIONSHIP_SOURCE, READ);
322        addPermissionMapping(permMap, CAN_CREATE_RELATIONSHIP_TARGET, READ);
323        addPermissionMapping(permMap, CAN_GET_PROPERTIES_OBJECT, READ);
324        addPermissionMapping(permMap, CAN_VIEW_CONTENT_OBJECT, READ);
325        addPermissionMapping(permMap, CAN_UPDATE_PROPERTIES_OBJECT, WRITE);
326        addPermissionMapping(permMap, CAN_MOVE_OBJECT, WRITE);
327        addPermissionMapping(permMap, CAN_MOVE_TARGET, WRITE);
328        addPermissionMapping(permMap, CAN_MOVE_SOURCE, WRITE);
329        addPermissionMapping(permMap, CAN_DELETE_OBJECT, WRITE);
330        addPermissionMapping(permMap, CAN_DELETE_TREE_FOLDER, WRITE);
331        addPermissionMapping(permMap, CAN_SET_CONTENT_DOCUMENT, WRITE);
332        addPermissionMapping(permMap, CAN_DELETE_CONTENT_DOCUMENT, WRITE);
333        addPermissionMapping(permMap, CAN_ADD_TO_FOLDER_OBJECT, WRITE);
334        addPermissionMapping(permMap, CAN_ADD_TO_FOLDER_FOLDER, WRITE);
335        addPermissionMapping(permMap, CAN_REMOVE_FROM_FOLDER_OBJECT, WRITE);
336        addPermissionMapping(permMap, CAN_REMOVE_FROM_FOLDER_FOLDER, WRITE);
337        addPermissionMapping(permMap, CAN_CHECKOUT_DOCUMENT, WRITE);
338        addPermissionMapping(permMap, CAN_CANCEL_CHECKOUT_DOCUMENT, WRITE);
339        addPermissionMapping(permMap, CAN_CHECKIN_DOCUMENT, WRITE);
340        addPermissionMapping(permMap, CAN_GET_ALL_VERSIONS_VERSION_SERIES, READ);
341        addPermissionMapping(permMap, CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, READ);
342        addPermissionMapping(permMap, CAN_ADD_POLICY_OBJECT, WRITE);
343        addPermissionMapping(permMap, CAN_ADD_POLICY_POLICY, WRITE);
344        addPermissionMapping(permMap, CAN_REMOVE_POLICY_OBJECT, WRITE);
345        addPermissionMapping(permMap, CAN_REMOVE_POLICY_POLICY, WRITE);
346        addPermissionMapping(permMap, CAN_GET_APPLIED_POLICIES_OBJECT, READ);
347        addPermissionMapping(permMap, CAN_GET_ACL_OBJECT, READ);
348        addPermissionMapping(permMap, CAN_APPLY_ACL_OBJECT, ALL);
349        aclCaps.setPermissionMappingData(permMap);
350
351        repositoryInfo.setAclCapabilities(aclCaps);
352
353        return repositoryInfo;
354    }
355
356    @SuppressWarnings("unchecked")
357    protected static void addPermissionDefinitions(List<PermissionDefinition> permDefs) {
358        addPermissionDefinition(permDefs, READ, "Read"); // = Nuxeo Read
359        addPermissionDefinition(permDefs, WRITE, "Write"); // = Nuxeo ReadWrite
360        addPermissionDefinition(permDefs, ALL, "All"); // = Nuxeo Everything
361        addPermissionDefinition(permDefs, NUXEO_READ_REMOVE, "Remove");
362
363        Set<String> done = new HashSet<>();
364        done.add(SecurityConstants.READ);
365        done.add(SecurityConstants.READ_WRITE);
366        done.add(SecurityConstants.EVERYTHING);
367        done.add(NUXEO_READ_REMOVE);
368
369        /*
370         * Add Nuxeo-specific permissions registered through the permissionsVisibility extension point.
371         */
372
373        DefaultPermissionProvider permissionProvider = (DefaultPermissionProvider) Framework.getService(PermissionProvider.class);
374        permissionProvider.getUserVisiblePermissionDescriptors(); // init var
375        Map<String, PermissionVisibilityDescriptor> map;
376        try {
377            Field f;
378            f = DefaultPermissionProvider.class.getDeclaredField("mergedPermissionsVisibility");
379            f.setAccessible(true);
380            map = (Map<String, PermissionVisibilityDescriptor>) f.get(permissionProvider);
381        } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) {
382            throw new RuntimeException(e);
383        }
384        // iterate for all types regisited, not just the default ""
385        for (Entry<String, PermissionVisibilityDescriptor> en : map.entrySet()) {
386            for (String permission : en.getValue().getSortedItems()) {
387                if (!done.add(permission)) {
388                    continue;
389                }
390                addPermissionDefinition(permDefs, permission, permission);
391            }
392        }
393    }
394
395    protected static void addPermissionDefinition(List<PermissionDefinition> permDefs, String permission,
396            String description) {
397        PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
398        pd.setId(permission);
399        pd.setDescription(description);
400        permDefs.add(pd);
401    }
402
403    protected static void addPermissionMapping(Map<String, PermissionMapping> permMap, String key, String permission) {
404        PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
405        pm.setKey(key);
406        pm.setPermissions(Collections.singletonList(permission));
407        permMap.put(key, pm);
408    }
409
410    // Structures are not copied when returned
411    public TypeDefinition getTypeDefinition(String typeId) {
412        TypeDefinitionContainer typec = getTypeManager().getTypeById(typeId);
413        return typec == null ? null : typec.getTypeDefinition();
414    }
415
416    public boolean hasType(String typeId) {
417        return getTypeManager().hasType(typeId);
418    }
419
420    // Structures are not copied when returned
421    public TypeDefinitionList getTypeChildren(String typeId, Boolean includePropertyDefinitions, BigInteger maxItems,
422            BigInteger skipCount) {
423        return getTypeManager().getTypeChildren(typeId, includePropertyDefinitions, maxItems, skipCount);
424    }
425
426    public List<TypeDefinitionContainer> getTypeDescendants(String typeId, int depth, Boolean includePropertyDefinitions) {
427        return getTypeManager().getTypeDescendants(typeId, depth, includePropertyDefinitions);
428    }
429
430    /** Returns the server base URL (including context). */
431    private static String getBaseURL(CallContext callContext) {
432        HttpServletRequest request = (HttpServletRequest) callContext.get(CallContext.HTTP_SERVLET_REQUEST);
433        if (request != null) {
434            String baseURL = getServerURL(request);
435            String contextPath = request.getContextPath();
436            if (contextPath == null) {
437                contextPath = Framework.getProperty(NUXEO_CONTEXT_PATH_PROP, NUXEO_CONTEXT_PATH_DEFAULT);
438            }
439            // add context path
440            return baseURL + contextPath + '/';
441        } else {
442            return Framework.getProperty(NUXEO_URL_PROP);
443        }
444    }
445
446    /**
447     * Returns the server URL according to virtual hosting headers (without trailing slash).
448     */
449    private static String getServerURL(HttpServletRequest request) {
450        String url = null;
451        // Detect Nuxeo specific header for VH
452        String nuxeoVH = request.getHeader(NUXEO_VH_HEADER);
453        if (nuxeoVH == null) {
454            nuxeoVH = Framework.getProperty(VH_PARAM);
455        }
456        if (nuxeoVH != null && nuxeoVH.startsWith("http")) {
457            url = nuxeoVH;
458        } else {
459            // default values
460            String scheme = request.getScheme();
461            String serverName = request.getServerName();
462            int serverPort = request.getServerPort();
463            // Detect virtual hosting based in standard header
464            String forwardedHost = request.getHeader(X_FORWARDED_HOST);
465            if (forwardedHost != null) {
466                if (forwardedHost.contains(":")) {
467                    String[] split = forwardedHost.split(":");
468                    serverName = split[0];
469                    serverPort = Integer.parseInt(split[1]);
470                } else {
471                    serverName = forwardedHost;
472                    serverPort = 80; // fallback
473                }
474            }
475            url = buildURL(scheme, serverName, serverPort);
476        }
477        // strip trailing slash
478        if (url.endsWith("/")) {
479            url = url.substring(0, url.length() - 1);
480        }
481        return url;
482    }
483
484    /** Builds an URL (without trailing slash). */
485    private static String buildURL(String scheme, String serverName, int serverPort) {
486        StringBuilder sb = new StringBuilder();
487        sb.append(scheme);
488        sb.append("://");
489        sb.append(serverName);
490        if (serverPort != 0) {
491            if ("http".equals(scheme) && serverPort != 80 || "https".equals(scheme) && serverPort != 443) {
492                sb.append(':');
493                sb.append(serverPort);
494            }
495        }
496        return sb.toString();
497    }
498
499}