Creating a type

Java command-line example using the CMIS 1.1 type mutability feature.

package typeMutability.enabled;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.chemistry.opencmis.client.api.ObjectType;
import org.apache.chemistry.opencmis.client.api.Repository;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.SessionFactory;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
import org.apache.chemistry.opencmis.commons.definitions.Choice;
import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyBooleanDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyIdDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyIntegerDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyStringDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeMutability;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.Cardinality;
import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed;
import org.apache.chemistry.opencmis.commons.enums.PropertyType;
import org.apache.chemistry.opencmis.commons.enums.Updatability;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;

import typeMutability.util.TestStringChoice;

/* 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * This code is based on the Apache Chemistry OpenCMIS FileShare project
 * <http://chemistry.apache.org/java/developing/repositories/dev-repositories-fileshare.html>.
 *
 * This code is part of a training exercise and not intended for production use!
 *
 */

/**
 * Running this test requires an input file containing the connection 
 * properties for your CMIS server. 
 *
 * The default/static location is /temp/cmis.properties
 * and is hard coded in the main() method. 
 * See the loadConnectionProperties() call if you 
 * need to change this location or make it dynamic. 
 * 
 * Example format for a typical cmis.properties file:
 * You can also refer to the properties section in the 
 * Workbench page here:
 * http://chemistry.apache.org/java/developing/tools/dev-tools-workbench.html
 * 
 * ******************************************
 * SAMPLE cmis.properties *******************
 * ******************************************
 * 
 *     org.apache.chemistry.opencmis.binding.spi.type=browser
 *     org.apache.chemistry.opencmis
 *     org.apache.chemistry.opencmis.binding.browser.url=http://localhost:8080/openfncmis/browser
 *     org.apache.chemistry.opencmis.user=username_here
 *     org.apache.chemistry.opencmis.password=password_here
 *     org.apache.chemistry.opencmis.binding.compression=true
 *     org.apache.chemistry.opencmis.binding.cookies=true
 * 
 */

/*
 * This class serves as a simple example of how to create a new TypeDefinintion
 * in CMIS using the OpenCMIS client. (all in one self contained file) This code
 * was tested with the FileNet CMIS 1.1 tech preview but can be modified to work
 * with any CMIS client that supports type mutability.
 * 
 * Code will create a new subclass of cmis:document with 4 properties 
 * String, Int, Boolean and Id, and two choice list values on the String prop
 */
public class CreateTestCommandLine {

    // Modify these values to conform to your local repository requirements
    //
    // property id's and names here 
    private static final String property_stringPropertyId = "StringPropertyDefinition";
    private static final String property_intPropertyId = "IntPropertyDefinition";
    private static final String property_boolPropertyId = "BoolPropertyDefinition";
    private static final String property_idPropertyId = "IdPropertyDefinition";

    //
    // type related
    // the id of the new type's parent
    private static final String type_idOfParentType = "cmis:document";
    // Id of the new subclass we will be creating
    private static final String type_idOfNewClass = "cmis_newDocSubclass1";
    // other information about type
    private static final String type_description = "Test document type definition";
    private static final String type_displayName = "TestTypeDefinition";
    private static final String type_localName = "some test local name";
    private static final String type_localNamespace = "some test local name space";

    // globals
    private static Session session = null;

    public static void main(String[] args) {

        System.out.println("An example of type creation with CMIS TypeMutability from OpenCMIS.");

        // Assume /cmis.properties is the name of the input file containing all
        // of the session parameters (test only)
        Map<String, String> parameters = loadConnectionProperties("/temp/cmis.properties");

        session = getSession(parameters);

        // Look at repository info - demonstrates a valid connection
        RepositoryInfo repositoryInfo = session.getRepositoryInfo();
        System.out.println("Connected to repository.  Supports CMIS version:"
                + repositoryInfo.getCmisVersionSupported());


        // Check here to verify the types we will be creating are permissible 
        // for this repository.
        Boolean canCreateStringProperty = false;
        Boolean canCreateIdProperty = false;
        Boolean canCreateBoolProperty = false;
        Boolean canCreateIntProperty = false;
        for (PropertyType propCreatable : repositoryInfo.getCapabilities().getCreatablePropertyTypes().canCreate()) {

            if (propCreatable.equals(PropertyType.STRING)) {
                canCreateStringProperty = true;
            } else if (propCreatable.equals(PropertyType.INTEGER)) {
                canCreateIntProperty = true;
            } else if (propCreatable.equals(PropertyType.ID)) {
                canCreateIdProperty = true;
            } else if (propCreatable.equals(PropertyType.BOOLEAN)) {
                canCreateBoolProperty = true;
            }

            System.out.println("Repository can create property of : " + propCreatable.toString());
        }

        assert canCreateStringProperty: "String is not one of the createable properties.";
        assert canCreateIdProperty: "Id is not one of the createable properties.";
        assert canCreateBoolProperty: "Boolean is not one of the createable properties.";
        assert canCreateIntProperty: "Integer is not one of the createable properties.";

        // Create new type with string property
        // and verify it exists in type collection
        ObjectType newType = createNewType();

        // clean up the type if permitted
        // TODO - check to see if delete is permitted first...
        session.deleteType(newType.getId());

        // TODO - verify delete here for completeness.
        System.out.println("Cleanup completed.");
    }

    /**
     * Create the new type with with our 4 test property types.
     * 
     * @return
     */
    public static ObjectType createNewType() {
        // assuming the following default (change if your repository
        // does not support these settings
        Boolean isCreatable = true;
        Boolean includedInSupertypeQuery = true;
        Boolean queryable = true;
        ContentStreamAllowed contentStreamAllowed = ContentStreamAllowed.ALLOWED;
        Boolean versionable = false;

        // build property definitions - string, int, boolean and id
        Map<String, PropertyDefinition<?>> propertyDefinitions = new LinkedHashMap<String, PropertyDefinition<?>>();
        TestStringPropertyDefinition spd = createStringPropertyDefinition();
        TestIntegerPropertyDefinition ipd = createIntPropertyDefinition();
        TestBooleanPropertyDefinition bpd = createBooleanPropertyDefinition();
        TestIdPropertyDefinition idpd = createIDPropertyDefinition();

        propertyDefinitions.put(spd.getId(), spd);
        propertyDefinitions.put(ipd.getId(), ipd);
        propertyDefinitions.put(bpd.getId(), bpd);
        propertyDefinitions.put(idpd.getId(), idpd);

        TestDocumentTypeDefinition typeToCreate = new TestDocumentTypeDefinition(type_idOfNewClass, type_description,
                type_displayName, type_localName, type_localNamespace, type_idOfParentType, isCreatable,
                includedInSupertypeQuery, queryable, contentStreamAllowed, versionable, propertyDefinitions);

        TypeDefinition createdType = null;
        try {
            createdType = session.createType(typeToCreate);
            System.out.println("Type created: " + createdType.toString());
        } catch (Exception e) {
            assert false: "An exception was thrown when trying to create a new type definition. Message: " + e.getMessage();
        }

        ObjectType retrievedType = null;
        try {
            retrievedType = session.getTypeDefinition(createdType.getId());
            assert retrievedType != null: "Unable to retrieve new type. ";
        } catch (Exception e) {
            assert false: "Got exception. Cannot get the type definition from the repository. Message: " + e.getMessage();
        }

        return retrievedType;
    }

    public static Session getSession(Map<String, String> parameters) {
        Session session = null;

        SessionFactory factory = SessionFactoryImpl.newInstance();
        if (parameters.containsKey(SessionParameter.REPOSITORY_ID)) {
            session = factory.createSession(parameters);
        } else {
            // Create session for the first repository.
            List<Repository> repositories = factory.getRepositories(parameters);
            session = repositories.get(0).createSession();
        }

        // reset op context to default
        session.setDefaultContext(session.createOperationContext());

        return session;
    }

    /*
     * Load our connection properties from a local file
     * 
     * Note file should use the same format as the expert settings for Workbench
     */
    public static final Map<String, String> loadConnectionProperties(String configResource) {
        Properties testConfig = new Properties();
        if (configResource == null) {
            throw new CmisRuntimeException("Filename with connection parameters was not supplied.");
        }

        FileInputStream inStream;
        try {
            inStream = new FileInputStream(configResource);
            testConfig.load(inStream);
            inStream.close();
        } catch (FileNotFoundException e1) {
            throw new CmisRuntimeException("Test properties file '" + configResource + "' was not found at:"
                    + configResource);
        } catch (IOException e) {
            throw new CmisRuntimeException("Exception loading test properties file " + configResource, e);
        }

        Map<String, String> map = new HashMap<String, String>();
        for (Entry<?, ?> entry : testConfig.entrySet()) {
            System.out.println("Found key: " + entry.getKey() + " Value:" + entry.getValue());
            map.put((String) entry.getKey(), ((String) entry.getValue()).trim());
        }
        return map;
    }

    /**
     * Create a single string property definition with a choice list
     */
    private static TestStringPropertyDefinition createStringPropertyDefinition() {

        Cardinality cardinality = Cardinality.SINGLE;
        String description = "String property definition";
        String displayName = "StringPropertyDefinition";
        String localName = "StringPropertyDefinition";
        String localNameSpace = "StringPropertyDefinition";    
        Updatability updatability = Updatability.READWRITE;
        Boolean orderable = false;
        Boolean queryable = false;
        ArrayList<String> defaults = new ArrayList<String>();
        defaults.add("test");

        List<String> vals1 = new LinkedList<String>();
        vals1.add("val1");

        List<String> vals2 = new LinkedList<String>();
        vals2.add("val2");

        TestStringChoice strChoice1 = new TestStringChoice("choice1", vals1, null);
        TestStringChoice strChoice2 = new TestStringChoice("choice2", vals2, null);
        List<Choice<String>> choiceList = new LinkedList<Choice<String>>();
        choiceList.add (strChoice1);
        choiceList.add (strChoice2);

        TestStringPropertyDefinition spd = new TestStringPropertyDefinition(property_stringPropertyId, cardinality, 
                description, displayName, localName, localNameSpace, updatability, orderable, queryable, 
                defaults, choiceList, null);

        return spd;      
    }

    private static TestIntegerPropertyDefinition createIntPropertyDefinition() {
        Cardinality cardinality = Cardinality.MULTI;
        String description = "Int property definition";
        String displayName = "IntPropertyDefinition";
        String localName = "IntPropertyDefinition";
        String localNameSpace = "IntPropertyDefinition";
        Updatability updatability = Updatability.READWRITE;
        Boolean orderable = false;
        Boolean queryable = false;
        ArrayList<BigInteger> defaults = new ArrayList<BigInteger>();
        // defaults.add(new BigInteger("101"));
        BigInteger minVal = new BigInteger("100");
        BigInteger maxVal = new BigInteger("1000");

        TestIntegerPropertyDefinition ipd = new TestIntegerPropertyDefinition(property_intPropertyId, cardinality,
                description, displayName, localName, localNameSpace, updatability, orderable, queryable, defaults,
                minVal, maxVal, null);
        return ipd;
    }

    private static TestBooleanPropertyDefinition createBooleanPropertyDefinition() {

        Cardinality cardinality = Cardinality.SINGLE;
        String description = "Boolean property definition";
        String displayName = "BooleanPropertyDefinition";
        String localName = "BooleanPropertyDefinition";
        String localNameSpace = "BooleanPropertyDefinition";
        Updatability updatability = Updatability.ONCREATE;
        Boolean orderable = false;
        Boolean queryable = false;
        ArrayList<Boolean> defaults = new ArrayList<Boolean>();
        defaults.add(false);

        TestBooleanPropertyDefinition spd = new TestBooleanPropertyDefinition(property_boolPropertyId, cardinality,
                description, displayName, localName, localNameSpace, updatability, orderable, queryable, defaults);
        return spd;
    }

    private static TestIdPropertyDefinition createIDPropertyDefinition() {
        Cardinality cardinality = Cardinality.SINGLE;
        String description = "ID property definition";
        String displayName = "IDPropertyDefinition";
        String localName = "IDPropertyDefinition";
        String localNameSpace = "IDPropertyDefinition";
        Updatability updatability = Updatability.READWRITE;
        Boolean orderable = false;
        Boolean queryable = false;

        TestIdPropertyDefinition idpd = new TestIdPropertyDefinition(property_idPropertyId, cardinality, description,
                displayName, localName, localNameSpace, updatability, orderable, queryable, null);
        return idpd;
    }

    /**
     * **************************************************************************
     * Inner classes follow
     * **************************************************************************
     * 
     * All of the abstract base classes (for properties and types) 
     * that are used in this example are defined here
     * along with their subclasses for each type that we support in this example.
     * 
     * These classes can be further extended and reused for additional type 
     * mutability operations. 
     * 
     * These were made inner classes so the entire example would be contained
     * in a single Java file. (no design reason) 
     * 
     */

    private static class TestStringPropertyDefinition extends TestPropertyDefinition<String> implements
            PropertyStringDefinition {

        BigInteger maxLength = null;

        public TestStringPropertyDefinition(String idAndQueryName, Cardinality cardinality, String description,
                String displayName, String localName, String localNameSpace, Updatability updatability,
                Boolean orderable, Boolean queryable, List<String> defaultValue, List<Choice<String>> choiceList,
                BigInteger maxLength) {
            super(idAndQueryName, cardinality, description, displayName, localName, localNameSpace, updatability,
                    orderable, queryable, defaultValue, choiceList);

            this.maxLength = maxLength;
        }

        @Override
        public PropertyType getPropertyType() {
            return PropertyType.STRING;
        }

        @Override
        public BigInteger getMaxLength() {
            return maxLength;
        }

    }

    private static class TestIntegerPropertyDefinition extends TestPropertyDefinition<BigInteger> implements
            PropertyIntegerDefinition {

        private BigInteger minVal = null;
        private BigInteger maxVal = null;

        public TestIntegerPropertyDefinition(String idAndQueryName, Cardinality cardinality, String description,
                String displayName, String localName, String localNameSpace, Updatability updatability,
                Boolean orderable, Boolean queryable, List<BigInteger> defaultValue, BigInteger minVal,
                BigInteger maxVal, List<Choice<BigInteger>> choiceList) {
            super(idAndQueryName, cardinality, description, displayName, localName, localNameSpace, updatability,
                    orderable, queryable, defaultValue, choiceList);

            this.minVal = minVal;
            this.maxVal = maxVal;
        }

        @Override
        public PropertyType getPropertyType() {
            return PropertyType.INTEGER;
        }

        @Override
        public BigInteger getMaxValue() {
            return this.maxVal;
        }

        @Override
        public BigInteger getMinValue() {
            return this.minVal;
        }
    }

    private static class TestBooleanPropertyDefinition extends TestPropertyDefinition<Boolean> implements
            PropertyBooleanDefinition {

        public TestBooleanPropertyDefinition(String idAndQueryName, Cardinality cardinality, String description,
                String displayName, String localName, String localNameSpace, Updatability updatability,
                Boolean orderable, Boolean queryable, List<Boolean> defaultValue) {

            super(idAndQueryName, cardinality, description, displayName, localName, localNameSpace, updatability,
                    orderable, queryable, defaultValue, null);
        }

        @Override
        public PropertyType getPropertyType() {
            return PropertyType.BOOLEAN;
        }
    }

    private static class TestIdPropertyDefinition extends TestPropertyDefinition<String> implements
            PropertyIdDefinition {

        public TestIdPropertyDefinition(String idAndQueryName, Cardinality cardinality, String description,
                String displayName, String localName, String localNameSpace, Updatability updatability,
                Boolean orderable, Boolean queryable, List<String> defaultValue) {
            super(idAndQueryName, cardinality, description, displayName, localName, localNameSpace, updatability,
                    orderable, queryable, defaultValue, null);
        }

        @Override
        public PropertyType getPropertyType() {
            return PropertyType.ID;
        }

    }

    /** 
     * Base class for all property definition types
     * 
     * See TestStringPropertyDefinition for example of how to subclass this.
     *
     * @param <T>
     */
    abstract private static class TestPropertyDefinition<T> implements PropertyDefinition<T> {

        private String idAndQueryName = null;
        private Cardinality cardinality = null;
        private String description = null;
        private String displayName = null;
        private String localName = null;
        private String localNameSpace = null;
        private Updatability updatability = null;
        private Boolean orderable = null;
        private Boolean queryable = null;

        private List<T> defaultValue = null;
        private List<Choice<T>> choiceList = null;

        public TestPropertyDefinition(String idAndQueryName, Cardinality cardinality, String description,
                String displayName, String localName, String localNameSpace, Updatability updatability,
                Boolean orderable, Boolean queryable, List<T> defaultValue, List<Choice<T>> choiceList) {
            super();
            this.idAndQueryName = idAndQueryName;
            this.cardinality = cardinality;
            this.description = description;
            this.displayName = displayName;
            this.localName = localName;
            this.localNameSpace = localNameSpace;
            this.updatability = updatability;
            this.orderable = orderable;
            this.queryable = queryable;
            this.defaultValue = defaultValue;
            this.choiceList = choiceList;
        }

        @Override
        public String getId() {
            return idAndQueryName;
        }

        @Override
        public Cardinality getCardinality() {
            return cardinality;
        }

        @Override
        public String getDescription() {
            return description;
        }

        @Override
        public String getDisplayName() {
            return displayName;
        }

        @Override
        public String getLocalName() {
            return localName;
        }

        @Override
        public String getLocalNamespace() {
            return localNameSpace;
        }

        @Override
        abstract public PropertyType getPropertyType();

        @Override
        public String getQueryName() {
            return idAndQueryName;
        }

        @Override
        public Updatability getUpdatability() {
            return updatability;
        }

        @Override
        public Boolean isOrderable() {
            return orderable;
        }

        @Override
        public Boolean isQueryable() {
            return queryable;
        }

        // methods with static content

        @Override
        public List<Choice<T>> getChoices() {
            return this.choiceList;
        }

        @Override
        public List<T> getDefaultValue() {
            return this.defaultValue;
        }


        /**
         * TODO For these remaining attributes you will want to set them 
         * accordingly.  They are all set to static values only 
         * because this is sample code. 
         */
        @Override
        public Boolean isInherited() {
            return false;
        }

        @Override
        public Boolean isOpenChoice() {
            return false;
        }

        @Override
        public Boolean isRequired() {
            return false;
        }

        @Override
        public List<CmisExtensionElement> getExtensions() {
            return null;
        }

        @Override
        public void setExtensions(List<CmisExtensionElement> arg0) {
        }

    }

    /**
     * Base class for all typeDefinitions.  
     * See TestDocumentTypeDefinition for an example of how to subclass this for document.
     *
     */
    private static abstract class TestTypeDefinition implements TypeDefinition {

        private String description = null;
        private String displayName = null;
        private String idAndQueryName = null;
        private String localName = null;
        private String localNamespace = null;
        private String parentTypeId = null;
        private Boolean isCreatable = null;
        private Boolean includedInSupertypeQuery = null;
        private Boolean queryable = null;
        private Map<String, PropertyDefinition<?>> propertyDefinitions = new HashMap<String, PropertyDefinition<?>>();

        public TestTypeDefinition(String idAndQueryName, String description, String displayName, String localName,
                String localNamespace, String parentTypeId, Boolean isCreatable, Boolean includedInSupertypeQuery,
                Boolean queryable, Map<String, PropertyDefinition<?>> propertyDefinitions) {

            this.description = description;
            this.displayName = displayName;
            this.idAndQueryName = idAndQueryName;
            this.localName = localName;
            this.localNamespace = localNamespace;
            this.parentTypeId = parentTypeId;
            this.isCreatable = isCreatable;
            this.includedInSupertypeQuery = includedInSupertypeQuery;
            this.queryable = queryable;

            if (propertyDefinitions != null) {
                this.propertyDefinitions = propertyDefinitions;
            }
        }

        @Override
        abstract public BaseTypeId getBaseTypeId();

        @Override
        public String getDescription() {
            return description;
        }

        @Override
        public String getDisplayName() {
            return displayName;
        }

        @Override
        public String getId() {
            return idAndQueryName;
        }

        @Override
        public String getLocalName() {
            return localName;
        }

        @Override
        public String getLocalNamespace() {
            return localNamespace;
        }

        @Override
        public String getParentTypeId() {
            return parentTypeId;
        }

        @Override
        public Map<String, PropertyDefinition<?>> getPropertyDefinitions() {
            return propertyDefinitions;
        }

        @Override
        public String getQueryName() {
            return idAndQueryName;
        }

        @Override
        public Boolean isCreatable() {
            return isCreatable;
        }

        @Override
        public Boolean isIncludedInSupertypeQuery() {
            return includedInSupertypeQuery;
        }

        @Override
        public Boolean isQueryable() {
            return queryable;
        }


        /**
         * TODO For these remaining attributes you will want to set them 
         * accordingly.  They are all set to static values only 
         * because this is sample code. 
         */

        @Override
        public TypeMutability getTypeMutability() {
            return new TestTypeMutability();
        }

        @Override
        public Boolean isControllableAcl() {
            return true;
        }

        @Override
        public Boolean isControllablePolicy() {
            return false;
        }

        @Override
        public Boolean isFileable() {
            return true;
        }

        @Override
        public Boolean isFulltextIndexed() {
            return false;
        }

        @Override
        public List<CmisExtensionElement> getExtensions() {
            return null;
        }

        @Override
        public void setExtensions(List<CmisExtensionElement> extension) {
        }

    }

    private static class TestDocumentTypeDefinition extends TestTypeDefinition implements DocumentTypeDefinition {

        private ContentStreamAllowed contentStreamAllowed = null;
        private Boolean versionable = null;

        public TestDocumentTypeDefinition(String idAndQueryName, String description, String displayName,
                String localName, String localNamespace, String parentTypeId, Boolean isCreatable,
                Boolean includedInSupertypeQuery, Boolean queryable, ContentStreamAllowed contentStreamAllowed,
                Boolean versionable, Map<String, PropertyDefinition<?>> propertyDefinitions) {

            super(idAndQueryName, description, displayName, localName, localNamespace, parentTypeId, isCreatable,
                    includedInSupertypeQuery, queryable, propertyDefinitions);

            this.contentStreamAllowed = contentStreamAllowed;
            this.versionable = versionable;
        }

        @Override
        public BaseTypeId getBaseTypeId() {
            return BaseTypeId.CMIS_DOCUMENT;
        }

        @Override
        public ContentStreamAllowed getContentStreamAllowed() {
            return contentStreamAllowed;
        }

        @Override
        public Boolean isVersionable() {
            return versionable;
        }

    }

    public static class TestTypeMutability implements TypeMutability {

        /**
         * TODO:
         * Change these values based on your repository and requirements
         */

        @Override
        public List<CmisExtensionElement> getExtensions() {
            return null;
        }

        @Override
        public void setExtensions(List<CmisExtensionElement> arg0) {
        }

        @Override
        public Boolean canCreate() {
            return true;
        }

        @Override
        public Boolean canDelete() {
            return true;
        }

        @Override
        public Boolean canUpdate() {
            return true;
        }
    }
}