class SharingController {

    @RequestMapping( method = { RequestMethod.POST, RequestMethod.PUT }, consumes = MediaType.APPLICATION_JSON_VALUE )
    public void setSharing( @RequestParam String type, @RequestParam String id, HttpServletResponse response, HttpServletRequest request ) throws IOException, WebMessageException
    {
        type = getSharingType( type );

        Class<? extends IdentifiableObject> sharingClass = aclService.classForType( type );

        if ( sharingClass == null || !aclService.isShareable( sharingClass ) )
        {
            throw new WebMessageException( WebMessageUtils.conflict( "Type " + type + " is not supported." ) );
        }

        BaseIdentifiableObject object = (BaseIdentifiableObject) manager.get( sharingClass, id );

        if ( object == null )
        {
            throw new WebMessageException( WebMessageUtils.notFound( "Object of type " + type + " with ID " + id + " was not found." ) );
        }

        if ( ( object instanceof SystemDefaultMetadataObject ) && ( (SystemDefaultMetadataObject) object ).isDefault() )
        {
            throw new WebMessageException( WebMessageUtils.conflict( "Sharing settings of system default metadata object of type " + type + " cannot be modified." ) );
        }

        User user = currentUserService.getCurrentUser();

        if ( !aclService.canManage( user, object ) )
        {
            throw new AccessDeniedException( "You do not have manage access to this object." );
        }

        Sharing sharing = renderService.fromJson( request.getInputStream(), Sharing.class );

        if ( !AccessStringHelper.isValid( sharing.getObject().getPublicAccess() ) )
        {
            throw new WebMessageException( WebMessageUtils.conflict( "Invalid public access string: " + sharing.getObject().getPublicAccess() ) );
        }

        // ---------------------------------------------------------------------
        // Ignore externalAccess if user is not allowed to make objects external
        // ---------------------------------------------------------------------

        if ( aclService.canMakeExternal( user, object.getClass() ) )
        {
            object.setExternalAccess( sharing.getObject().hasExternalAccess() );
        }

        // ---------------------------------------------------------------------
        // Ignore publicAccess if user is not allowed to make objects public
        // ---------------------------------------------------------------------

        Schema schema = schemaService.getDynamicSchema( sharingClass );

        if ( aclService.canMakePublic( user, object.getClass() ) )
        {
            object.setPublicAccess( sharing.getObject().getPublicAccess() );
        }

        if ( !schema.isDataShareable() )
        {
            if ( AccessStringHelper.hasDataSharing( object.getPublicAccess() ) )
            {
                throw new WebMessageException( WebMessageUtils.conflict( "Data sharing is not enabled for this object." ) );
            }
        }

        if ( object.getUser() == null )
        {
            object.setUser( user );
        }

        Iterator<UserGroupAccess> userGroupAccessIterator = object.getUserGroupAccesses().iterator();

        while ( userGroupAccessIterator.hasNext() )
        {
            UserGroupAccess userGroupAccess = userGroupAccessIterator.next();
            userGroupAccessIterator.remove();

            userGroupAccessService.deleteUserGroupAccess( userGroupAccess );
        }

        for ( SharingUserGroupAccess sharingUserGroupAccess : sharing.getObject().getUserGroupAccesses() )
        {
            UserGroupAccess userGroupAccess = new UserGroupAccess();

            if ( !AccessStringHelper.isValid( sharingUserGroupAccess.getAccess() ) )
            {
                throw new WebMessageException( WebMessageUtils.conflict( "Invalid user group access string: " + sharingUserGroupAccess.getAccess() ) );
            }

            if ( !schema.isDataShareable() )
            {
                if ( AccessStringHelper.hasDataSharing( sharingUserGroupAccess.getAccess() ) )
                {
                    throw new WebMessageException( WebMessageUtils.conflict( "Data sharing is not enabled for this object." ) );
                }
            }

            userGroupAccess.setAccess( sharingUserGroupAccess.getAccess() );

            UserGroup userGroup = manager.get( UserGroup.class, sharingUserGroupAccess.getId() );

            if ( userGroup != null )
            {
                userGroupAccess.setUserGroup( userGroup );
                userGroupAccessService.addUserGroupAccess( userGroupAccess );

                object.getUserGroupAccesses().add( userGroupAccess );
            }
        }

        Iterator<UserAccess> userAccessIterator = object.getUserAccesses().iterator();

        while ( userAccessIterator.hasNext() )
        {
            UserAccess userAccess = userAccessIterator.next();
            userAccessIterator.remove();

            userAccessService.deleteUserAccess( userAccess );
        }

        for ( SharingUserAccess sharingUserAccess : sharing.getObject().getUserAccesses() )
        {
            UserAccess userAccess = new UserAccess();

            if ( !AccessStringHelper.isValid( sharingUserAccess.getAccess() ) )
            {
                throw new WebMessageException( WebMessageUtils.conflict( "Invalid user access string: " + sharingUserAccess.getAccess() ) );
            }

            if ( !schema.isDataShareable() )
            {
                if ( AccessStringHelper.hasDataSharing( sharingUserAccess.getAccess() ) )
                {
                    throw new WebMessageException( WebMessageUtils.conflict( "Data sharing is not enabled for this object." ) );
                }
            }

            userAccess.setAccess( sharingUserAccess.getAccess() );

            User sharingUser = manager.get( User.class, sharingUserAccess.getId() );

            if ( sharingUser != null )
            {
                userAccess.setUser( sharingUser );
                userAccessService.addUserAccess( userAccess );

                object.getUserAccesses().add( userAccess );
            }
        }

        manager.updateNoAcl( object );

        if ( Program.class.isInstance( object ) )
        {
            syncSharingForEventProgram( (Program) object );
        }

        log.info( sharingToString( object ) );

        webMessageService.send( WebMessageUtils.ok( "Access control set" ), response, request );
    }

}