org.sakaiproject.rubrics.logic.RubricsServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.rubrics.logic.RubricsServiceImpl.java

Source

/**********************************************************************************
 *
 * Copyright (c) 2017 The Sakai Foundation
 *
 * Original developers:
 *
 *   Unicon
 *
 * Licensed under the Educational Community 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.osedu.org/licenses/ECL-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.
 *
 **********************************************************************************/

package org.sakaiproject.rubrics.logic;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import javax.annotation.PostConstruct;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.authz.api.FunctionManager;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.entity.api.EntityProducer;
import org.sakaiproject.entity.api.EntityTransferrer;
import org.sakaiproject.entity.api.HttpAccess;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.event.api.EventTrackingService;
import org.sakaiproject.memory.api.Cache;
import org.sakaiproject.memory.api.MemoryService;
import org.sakaiproject.rubrics.logic.model.Criterion;
import org.sakaiproject.rubrics.logic.model.Evaluation;
import org.sakaiproject.rubrics.logic.model.Rating;
import org.sakaiproject.rubrics.logic.model.Rubric;
import org.sakaiproject.rubrics.logic.model.ToolItemRubricAssociation;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.tool.api.SessionManager;
import org.sakaiproject.tool.api.ToolManager;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.util.ResourceLoader;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.client.Traverson;
import org.springframework.hateoas.mvc.TypeReferences;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Implementation of {@link RubricsService}
 */
@Slf4j
public class RubricsServiceImpl implements RubricsService, EntityProducer, EntityTransferrer {

    protected static ResourceLoader rb = new ResourceLoader("org.sakaiproject.rubrics.bundle.Messages");

    private static final String RBCS_PERMISSIONS_EVALUATOR = "rubrics.evaluator";
    private static final String RBCS_PERMISSIONS_EDITOR = "rubrics.editor";
    private static final String RBCS_PERMISSIONS_EVALUEE = "rubrics.evaluee";
    private static final String RBCS_PERMISSIONS_ASSOCIATOR = "rubrics.associator";
    private static final String RBCS_PERMISSIONS_SUPERUSER = "rubrics.superuser";

    private static final String RBCS_SERVICE_URL_PREFIX = "/rubrics-service/rest/";

    private static final String RUBRICS_TOKEN_SIGNING_SHARED_SECRET_PROPERTY = "rubrics.integration.token-secret";
    private static final String SITE_CONTEXT_TYPE = "site";

    private static final String SERVER_ID_PROPERTY = "sakai.serverId";

    private static final String JWT_ISSUER = "sakai";
    private static final String JWT_AUDIENCE = "rubrics";
    private static final String JWT_CUSTOM_CLAIM_TOOL_ID = "toolId";
    private static final String JWT_CUSTOM_CLAIM_SESSION_ID = "sessionId";
    private static final String JWT_CUSTOM_CLAIM_ROLES = "roles";
    private static final String JWT_CUSTOM_CLAIM_CONTEXT_ID = "contextId";
    private static final String JWT_CUSTOM_CLAIM_CONTEXT_TYPE = "contextType";

    @Getter
    @Setter
    private ToolManager toolManager;

    @Getter
    @Setter
    private SessionManager sessionManager;

    @Getter
    @Setter
    private UserDirectoryService userDirectoryService;

    @Getter
    @Setter
    private SecurityService securityService;

    @Getter
    @Setter
    private EventTrackingService eventTrackingService;

    @Getter
    @Setter
    private ServerConfigurationService serverConfigurationService;

    @Getter
    @Setter
    private SiteService siteService;

    @Getter
    @Setter
    private FunctionManager functionManager;

    @Getter
    @Setter
    private AuthzGroupService authzGroupService;

    @Getter
    @Setter
    private EntityManager entityManager;

    @Getter
    @Setter
    private MemoryService memoryService;

    private Cache<String, Boolean> hasAssociatedRubricCache;

    public void init() {
        if (StringUtils
                .isBlank(serverConfigurationService.getString(RUBRICS_TOKEN_SIGNING_SHARED_SECRET_PROPERTY))) {
            throw new IllegalStateException(String.format(
                    "Required deployment property %s was not found. Please " + "configure it in sakai.properties.",
                    RUBRICS_TOKEN_SIGNING_SHARED_SECRET_PROPERTY));
        }

        // register as an entity producer
        entityManager.registerEntityProducer(this, REFERENCE_ROOT);

        setFunction(RBCS_PERMISSIONS_EVALUATOR);
        setFunction(RBCS_PERMISSIONS_EDITOR);
        setFunction(RBCS_PERMISSIONS_EVALUEE);
        setFunction(RBCS_PERMISSIONS_ASSOCIATOR);

        hasAssociatedRubricCache = memoryService
                .<String, Boolean>getCache("org.sakaiproject.rubrics.logic.hasAssociatedRubricCache");
    }

    /**
     * {@inheritDoc}
     */
    private void setFunction(String function) {
        functionManager.registerFunction(function);
    }

    @PostConstruct
    private void postConstruct() {

    }

    private String getCurrentSiteId(String method) {
        if (toolManager.getCurrentPlacement() == null) {
            log.error("{}: current placement is null, Rubrics token won't be generated.", method);
            return null;
        }
        return toolManager.getCurrentPlacement().getContext();
    }

    public String generateJsonWebToken(String tool) {
        return generateJsonWebToken(tool, getCurrentSiteId("generateJsonWebToken"));
    }

    public String generateJsonWebToken(String tool, String siteId) {

        String token = null;
        String userId = sessionManager.getCurrentSessionUserId();

        try {
            DateTime now = DateTime.now();

            JWTCreator.Builder jwtBuilder = JWT.create();
            jwtBuilder.withIssuer(JWT_ISSUER).withAudience(JWT_AUDIENCE).withSubject(userId)
                    .withClaim(JWT_CUSTOM_CLAIM_TOOL_ID, tool)
                    .withClaim(JWT_CUSTOM_CLAIM_SESSION_ID, sessionManager.getCurrentSession().getId())
                    .withIssuedAt(now.toDate());
            int sessionTimeoutInSeconds = sessionManager.getCurrentSession().getMaxInactiveInterval();
            if (sessionTimeoutInSeconds > 0) {
                jwtBuilder.withExpiresAt(now.plusSeconds(sessionTimeoutInSeconds).toDate());
            } else {
                // if Sakai is configured for sessions to never timeout (negative value), we will set 30 minutes for
                // tokens - the rubrics service will check Sakai session validity if it receives an expired token.
                jwtBuilder.withExpiresAt(now.plusMinutes(30).toDate());
            }

            if (securityService.isSuperUser()) {
                jwtBuilder.withArrayClaim(JWT_CUSTOM_CLAIM_ROLES,
                        new String[] { RBCS_PERMISSIONS_EDITOR, RBCS_PERMISSIONS_ASSOCIATOR,
                                RBCS_PERMISSIONS_EVALUATOR, RBCS_PERMISSIONS_EVALUEE, RBCS_PERMISSIONS_SUPERUSER });
            } else {
                List<String> roles = new ArrayList<>();
                if (authzGroupService.isAllowed(userId, RBCS_PERMISSIONS_EDITOR, "/site/" + siteId)) {
                    roles.add(RBCS_PERMISSIONS_EDITOR);
                }
                if (authzGroupService.isAllowed(userId, RBCS_PERMISSIONS_ASSOCIATOR, "/site/" + siteId)) {
                    roles.add(RBCS_PERMISSIONS_ASSOCIATOR);
                }
                if (authzGroupService.isAllowed(userId, RBCS_PERMISSIONS_EVALUATOR, "/site/" + siteId)) {
                    roles.add(RBCS_PERMISSIONS_EVALUATOR);
                }
                if (authzGroupService.isAllowed(userId, RBCS_PERMISSIONS_EVALUEE, "/site/" + siteId)) {
                    roles.add(RBCS_PERMISSIONS_EVALUEE);
                }
                jwtBuilder.withArrayClaim(JWT_CUSTOM_CLAIM_ROLES, roles.toArray(new String[] {}));
            }
            jwtBuilder.withClaim(JWT_CUSTOM_CLAIM_CONTEXT_ID, siteId);
            jwtBuilder.withClaim(JWT_CUSTOM_CLAIM_CONTEXT_TYPE, SITE_CONTEXT_TYPE);
            token = jwtBuilder.sign(Algorithm
                    .HMAC256(serverConfigurationService.getString(RUBRICS_TOKEN_SIGNING_SHARED_SECRET_PROPERTY)));

        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(String.format(
                    "An error occurred while generating a JSON Web Token to "
                            + "authorize communication with the Rubrics service. Please verify the %s property is "
                            + "defined in the sakai.properties file.",
                    RUBRICS_TOKEN_SIGNING_SHARED_SECRET_PROPERTY), e);
        }

        return token;
    }

    public boolean hasAssociatedRubric(String tool, String id) {

        String cacheKey = tool + "#" + id;
        Boolean isAssociated = hasAssociatedRubricCache.get(cacheKey);

        if (isAssociated != null) {
            return isAssociated;
        } else {
            boolean exists = false;
            try {
                Optional<ToolItemRubricAssociation> association = getRubricAssociation(tool, id);
                exists = association.isPresent();
                hasAssociatedRubricCache.put(cacheKey, exists);
            } catch (Exception e) {
                log.debug("No previous association or rubrics not answering", e);
            }
            return exists;
        }
    }

    /**
     * call the rubrics-service to save the binding between assignment and rubric
     * @param params A hashmap with all the rbcs params comming from the component. The tool should generate it.
     * @param tool the tool id, something like "sakai.assignment"
     * @param id the id of the element to
     */
    public void saveRubricAssociation(String tool, String id, Map<String, String> params) {

        String associationHref = null;
        String created = "";
        String owner = "";
        String ownerType = "";
        String creatorId = "";
        Long oldRubricId = null;
        Map<String, Boolean> oldParams = new HashMap<>();

        try {
            Optional<Resource<ToolItemRubricAssociation>> associationResource = getRubricAssociationResource(tool,
                    id, null);
            ToolItemRubricAssociation association = null;
            if (associationResource.isPresent()) {
                associationHref = associationResource.get().getLink(Link.REL_SELF).getHref();
                association = associationResource.get().getContent();
                created = association.getMetadata().getCreated().toString();
                owner = association.getMetadata().getOwnerId();
                ownerType = association.getMetadata().getOwnerType();
                creatorId = association.getMetadata().getCreatorId();
                oldParams = association.getParameters();
                oldRubricId = association.getRubricId();
            }

            //we will create a new one or update if the parameter rbcs-associate is true
            String nowTime = LocalDateTime.now().toString();
            if (params.get(RubricsConstants.RBCS_ASSOCIATE).equals("1")) {

                if (associationHref == null) { // create a new one.
                    String input = "{\"toolId\" : \"" + tool + "\",\"itemId\" : \"" + id + "\",\"rubricId\" : "
                            + params.get(RubricsConstants.RBCS_LIST) + ",\"metadata\" : {\"created\" : \"" + nowTime
                            + /*"\",\"modified\" : \"" + nowTime +*/ "\",\"ownerId\" : \""
                            + userDirectoryService.getCurrentUser().getId() + "\"},\"parameters\" : {"
                            + setConfigurationParameters(params, oldParams) + "}}";
                    log.debug("New association " + input);
                    String query = serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX
                            + "rubric-associations/";
                    String resultPost = postRubricResource(query, input, tool, null);
                    log.debug("resultPost: " + resultPost);
                } else {
                    String input = "{\"toolId\" : \"" + tool + "\",\"itemId\" : \"" + id + "\",\"rubricId\" : "
                            + params.get(RubricsConstants.RBCS_LIST) + ",\"metadata\" : {\"created\" : \"" + created
                            + /*"\",\"modified\" : \"" + nowTime +*/ "\",\"ownerId\" : \"" + owner
                            + "\",\"ownerType\" : \"" + ownerType + "\",\"creatorId\" : \"" + creatorId
                            + "\"},\"parameters\" : {" + setConfigurationParameters(params, oldParams) + "}}";
                    log.debug("Existing association update " + input);
                    if (Long.valueOf(params.get(RubricsConstants.RBCS_LIST)) != oldRubricId) {
                        deleteRubricEvaluationsForAssociation(associationHref, tool);
                    }
                    String resultPut = putRubricResource(associationHref, input, tool);
                    //update the actual one.
                    log.debug("resultPUT: " + resultPut);
                }
            } else {
                // We delete the association
                if (associationHref != null) {
                    deleteRubricEvaluationsForAssociation(associationHref, tool);
                    deleteRubricResource(associationHref, tool, null);
                    if (association != null) {
                        hasAssociatedRubricCache.remove(association.getToolId() + "#" + association.getItemId());
                    }
                }
            }

        } catch (Exception e) {
            //TODO If we have an error here, maybe we should return say something to the user
        }
    }

    public void saveRubricEvaluation(String toolId, String associatedItemId, String evaluatedItemId,
            String evaluatedItemOwnerId, String evaluatorId, Map<String, String> params) {

        String evaluationUri = null;
        String created = "";
        String owner = "";

        try {
            // Check for an existing evaluation
            Evaluation existingEvaluation = null;
            String rubricEvaluationId = null;

            try {
                TypeReferences.ResourcesType<Resource<Evaluation>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<Evaluation>>() {
                };

                URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
                Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);

                Traverson.TraversalBuilder builder = traverson.follow("evaluations", "search",
                        "by-tool-item-and-associated-item-and-evaluated-item-ids");

                HttpHeaders headers = new HttpHeaders();
                headers.add("Authorization", String.format("Bearer %s", generateJsonWebToken(toolId)));
                builder.withHeaders(headers);

                Map<String, Object> parameters = new HashMap<>();
                parameters.put("toolId", toolId);
                parameters.put("itemId", associatedItemId);
                parameters.put("evaluatedItemId", evaluatedItemId);
                parameters.put("evaluatorId", evaluatorId);

                Resources<Resource<Evaluation>> evaluationResources = builder.withTemplateParameters(parameters)
                        .toObject(resourceParameterizedTypeReference);

                // Should only be one matching this search criterion
                if (evaluationResources.getContent().size() > 1) {
                    throw new IllegalStateException(
                            String.format("Number of evaluation resources greater than one for request: %s",
                                    evaluationResources.getLink(Link.REL_SELF).toString()));
                }

                for (Resource<Evaluation> evaluationResource : evaluationResources) {
                    existingEvaluation = evaluationResource.getContent();
                    evaluationUri = evaluationResource.getLink(Link.REL_SELF).getHref();
                }

            } catch (Exception ex) {
                log.info("Exception on saveRubricEvaluation: " + ex.getMessage());
                //no previous evaluation
            }

            // Get the actual association (necessary to get the rubrics association resource for persisting the evaluation)
            Resource<ToolItemRubricAssociation> rubricToolItemAssociationResource = getRubricAssociationResource(
                    toolId, associatedItemId, null).get();

            String criterionJsonData = createCriterionJsonPayload(associatedItemId, evaluatedItemId, params,
                    rubricToolItemAssociationResource);

            if (existingEvaluation == null) { // Create a new one

                String input = String.format("{ \"evaluatorId\" : \"%s\",\"evaluatedItemId\" : \"%s\", "
                        + "\"evaluatedItemOwnerId\" : \"%s\"," + "\"overallComment\" : \"%s\", "
                        + "\"toolItemRubricAssociation\" : \"%s\", " + "\"criterionOutcomes\" : [ %s ] " + "}",
                        evaluatorId, evaluatedItemId, evaluatedItemOwnerId, "",
                        rubricToolItemAssociationResource.getLink(Link.REL_SELF).getHref(), criterionJsonData);

                String requestUri = serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX
                        + "evaluations/";
                String resultPost = postRubricResource(requestUri, input, toolId, null);
                log.debug("resultPost: " + resultPost);

            } else { // Update existing evaluation

                // Resource IDs return as null when using Spring HATEOAS due to https://github.com/spring-projects/spring-hateoas/issues/67
                // so ID is not added and the resource URI is where it is derived from.
                String input = String.format("{ \"evaluatorId\" : \"%s\",\"evaluatedItemId\" : \"%s\", "
                        + "\"evaluatedItemOwnerId\" : \"%s\", \"overallComment\" : \"%s\", \"toolItemRubricAssociation\" : \"%s\", \"criterionOutcomes\" : [ %s ], "
                        + "\"metadata\" : {\"created\" : \"%s\", \"ownerId\" : \"%s\", \"ownerType\" : \"%s\", \"creatorId\" : \"%s\"} }",
                        evaluatorId, evaluatedItemId, evaluatedItemOwnerId, existingEvaluation.getOverallComment(),
                        rubricToolItemAssociationResource.getLink(Link.REL_SELF).getHref(), criterionJsonData,
                        existingEvaluation.getMetadata().getCreated(),
                        existingEvaluation.getMetadata().getOwnerId(),
                        existingEvaluation.getMetadata().getOwnerType(),
                        existingEvaluation.getMetadata().getCreatorId());

                String resultPut = putRubricResource(evaluationUri, input, toolId);
                //lets update the actual one.
                log.debug("resultPUT: " + resultPut);
            }

        } catch (Exception e) {
            //TODO If we have an error here, maybe we should return say something to the user
            log.error("Error in SaveRubricEvaluation " + e.getMessage());
        }

    }

    private String createCriterionJsonPayload(String associatedItemId, String evaluatedItemId,
            Map<String, String> formPostParameters, Resource<ToolItemRubricAssociation> association)
            throws Exception {

        Map<String, Map<String, String>> criterionDataMap = extractCriterionDataFromParams(formPostParameters);

        String criterionJsonData = "";
        int index = 0;
        boolean pointsAdjusted = false;
        String points = null;
        String selectedRatingId = null;

        String inlineRubricUri = String.format("%s?%s", association.getLink("rubric").getHref(),
                "projection=inlineRubric");

        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add("Authorization",
                String.format("Bearer %s", generateJsonWebToken(association.getContent().getToolId())));
        HttpEntity<?> requestEntity = new HttpEntity<>(headers);
        ResponseEntity<Rubric> rubricEntity = restTemplate.exchange(inlineRubricUri, HttpMethod.GET, requestEntity,
                Rubric.class);

        Map<String, Criterion> criterions = new HashMap<>();
        for (Criterion criterion : rubricEntity.getBody().getCriterions()) {
            criterions.put(String.valueOf(criterion.getId()), criterion);
        }

        for (Map.Entry<String, Map<String, String>> criterionData : criterionDataMap.entrySet()) {
            if (index > 0) {
                criterionJsonData += ", ";
            }
            index++;

            final String selectedRatingPoints = criterionData.getValue()
                    .get(RubricsConstants.RBCS_PREFIX + evaluatedItemId + "-" + associatedItemId + "-criterion");

            if (StringUtils.isNotBlank(criterionData.getValue().get(RubricsConstants.RBCS_PREFIX + evaluatedItemId
                    + "-" + associatedItemId + "-criterion-override"))) {
                pointsAdjusted = true;
                points = criterionData.getValue().get(RubricsConstants.RBCS_PREFIX + evaluatedItemId + "-"
                        + associatedItemId + "-criterion-override");
            } else {
                pointsAdjusted = false;
                points = selectedRatingPoints;
            }

            Criterion criterion = criterions.get(criterionData.getKey());
            Optional<Rating> rating = criterion.getRatings().stream()
                    .filter(c -> String.valueOf(c.getPoints()).equals(selectedRatingPoints)).findFirst();

            if (rating.isPresent()) {
                selectedRatingId = String.valueOf(rating.get().getId());
            }

            if (StringUtils.isEmpty(points)) {
                points = "0";
            }

            criterionJsonData += String
                    .format("{ \"criterionId\" : \"%s\", \"points\" : \"%s\", "
                            + "\"comments\" : \"%s\", \"pointsAdjusted\" : %b, \"selectedRatingId\" : \"%s\"  }",
                            criterionData.getKey(), points,
                            StringEscapeUtils
                                    .escapeJson(criterionData.getValue()
                                            .get(RubricsConstants.RBCS_PREFIX + evaluatedItemId + "-"
                                                    + associatedItemId + "-criterion-comment")),
                            pointsAdjusted, selectedRatingId);
        }

        return criterionJsonData;
    }

    private Map<String, Map<String, String>> extractCriterionDataFromParams(Map<String, String> params) {

        Map<String, Map<String, String>> criterionDataMap = new HashMap<>();

        for (Map.Entry<String, String> param : params.entrySet()) {
            String possibleCriterionId = StringUtils.substringAfterLast(param.getKey(), "-");
            String criterionDataLabel = StringUtils.substringBeforeLast(param.getKey(), "-");
            if (StringUtils.isNumeric(possibleCriterionId)) {
                if (!criterionDataMap.containsKey(possibleCriterionId)) {
                    criterionDataMap.put(possibleCriterionId, new HashMap<>());
                }
                criterionDataMap.get(possibleCriterionId).put(criterionDataLabel, param.getValue());
            }
        }

        return criterionDataMap;
    }

    /**
     * Prepare the association params in json format
     * @param params the full list of rubrics params coming from the component
     * @return
     */

    private String setConfigurationParameters(Map<String, String> params, Map<String, Boolean> oldParams) {
        String configuration = "";
        Boolean noFirst = false;
        //Get the parameters
        Iterator it2 = params.keySet().iterator();
        while (it2.hasNext()) {
            String name = it2.next().toString();
            if (name.startsWith(RubricsConstants.RBCS_CONFIG)) {
                if (noFirst) {
                    configuration = configuration + " , ";
                }
                String value = "false";
                if ((params.get(name) != null) && (params.get(name).equals("1"))) {
                    value = "true";
                }
                configuration = configuration + "\"" + name.substring(12) + "\" : " + value;
                noFirst = true;
            }
        }
        Iterator itOld = oldParams.keySet().iterator();
        while (itOld.hasNext()) {
            String name = itOld.next().toString();
            if (!(params.containsKey(RubricsConstants.RBCS_CONFIG + name))) {
                if (noFirst) {
                    configuration = configuration + " , ";
                }
                configuration = configuration + "\"" + name + "\" : false";
                noFirst = true;
            }
        }
        log.debug(configuration);
        return configuration;
    }

    private String setConfigurationParametersForDuplication(Map<String, Boolean> params) {
        String configuration = "";
        Boolean noFirst = false;
        for (Map.Entry<String, Boolean> parameter : params.entrySet()) {
            if (noFirst) {
                configuration = configuration + " , ";
            }
            configuration = configuration + "\"" + parameter.getKey() + "\" : " + parameter.getValue();
            noFirst = true;
        }
        log.debug(configuration);
        return configuration;
    }

    public Optional<ToolItemRubricAssociation> getRubricAssociation(String toolId, String associatedToolItemId)
            throws Exception {
        return getRubricAssociation(toolId, associatedToolItemId, getCurrentSiteId("getRubricAssociation"));
    }

    /**
      * Returns the ToolItemRubricAssociation for the given tool and associated item ID, wrapped as an Optional.
      * @param toolId the tool id, something like "sakai.assignment"
      * @param associatedToolItemId the id of the associated element within the tool
      * @return
      */
    public Optional<ToolItemRubricAssociation> getRubricAssociation(String toolId, String associatedToolItemId,
            String siteId) throws Exception {

        Optional<ToolItemRubricAssociation> association = Optional.empty();

        Optional<Resource<ToolItemRubricAssociation>> associationResource = getRubricAssociationResource(toolId,
                associatedToolItemId, siteId);
        if (associationResource.isPresent()) {
            association = Optional.of(associationResource.get().getContent());
        }
        return association;
    }

    /**
     * Returns the ToolItemRubricAssociation resource for the given tool and associated item ID, wrapped as an Optional.
     * @param toolId the tool id, something like "sakai.assignment"
     * @param associatedToolItemId the id of the associated element within the tool
     * @return
     */
    protected Optional<Resource<ToolItemRubricAssociation>> getRubricAssociationResource(String toolId,
            String associatedToolItemId, String siteId) throws Exception {

        TypeReferences.ResourcesType<Resource<ToolItemRubricAssociation>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<ToolItemRubricAssociation>>() {
        };

        URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
        Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);

        Traverson.TraversalBuilder builder = traverson.follow("rubric-associations", "search", "by-tool-item-ids");

        HttpHeaders headers = new HttpHeaders();
        if (siteId != null) {
            headers.add("Authorization", String.format("Bearer %s", generateJsonWebToken(toolId, siteId)));
        } else {
            headers.add("Authorization", String.format("Bearer %s", generateJsonWebToken(toolId)));
        }
        builder.withHeaders(headers);

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("toolId", toolId);
        parameters.put("itemId", associatedToolItemId);

        Resources<Resource<ToolItemRubricAssociation>> associationResources = builder
                .withTemplateParameters(parameters).toObject(resourceParameterizedTypeReference);

        // Should only be one matching this search criterion
        if (associationResources.getContent().size() > 1) {
            throw new IllegalStateException(
                    String.format("Number of rubric association resources greater than one for request: %s",
                            associationResources.getLink(Link.REL_SELF).toString()));
        }

        Optional<Resource<ToolItemRubricAssociation>> associationResource = associationResources.getContent()
                .stream().findFirst();

        return associationResource;
    }

    //TODO generate a public String postRubricAssociation(String tool, String id, HashMap<String,String> params)

    public String getRubricEvaluationObjectId(String associationId, String userId, String toolId) {
        try {
            URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
            Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);

            Traverson.TraversalBuilder builder = traverson.follow("evaluations", "search",
                    "by-association-and-user");

            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", String.format("Bearer %s", generateJsonWebToken(toolId)));
            builder.withHeaders(headers);

            Map<String, Object> parameters = new HashMap<>();
            parameters.put("associationId", associationId);
            parameters.put("userId", userId);

            String response = builder.withTemplateParameters(parameters).toObject(String.class);
            if (StringUtils.isNotEmpty(response)) {
                return response.replace("\"", "");
            }
        } catch (Exception e) {
            log.warn("Error {} while getting a rubric evaluation in assignment {} for user {}", e.getMessage(),
                    associationId, userId);
        }
        return null;
    }

    /**
     * Posts the rubric association.
     * @param json The json to post.
     * @return
     */
    private String postRubricResource(String targetUri, String json, String toolId, String siteId)
            throws IOException {
        log.debug(String.format("Post to URI '%s' body:", targetUri, json));

        HttpURLConnection conn = null;
        try {
            URL url = new URL(targetUri);
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestProperty("Content-Type", "application/hal+json; charset=UTF-8");
            conn.setRequestProperty("Content-Length", "" + json.length());
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("POST");
            String cookie = "JSESSIONID=" + sessionManager.getCurrentSession().getId() + ""
                    + System.getProperty(SERVER_ID_PROPERTY);
            conn.setRequestProperty("Cookie", cookie);
            if (siteId != null) {
                conn.setRequestProperty("Authorization", "Bearer " + generateJsonWebToken(toolId, siteId));
            } else {
                conn.setRequestProperty("Authorization", "Bearer " + generateJsonWebToken(toolId));
            }
            try (OutputStream os = conn.getOutputStream()) {
                os.write(json.getBytes("UTF-8"));
                os.close();
            }

            // read the response
            BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

            String output;
            StringWriter result = new StringWriter();
            while ((output = br.readLine()) != null) {
                result.append(output + "\n");
            }

            return result.toString();

        } catch (IOException ioException) {

            log.warn(String.format("Error creating a rubric resource at %s", targetUri), ioException);
            return null;

        } finally {
            if (conn != null) {
                try {
                    conn.disconnect();
                } catch (Exception e) {

                }
            }
        }
    }

    //TODO generate a public String putRubricAssociation(String tool, String id, HashMap<String,String> params)

    /**
     * Put the rubric association.
     * @param targetUri The association href.
     * @param json The json to post.
     * @return
     */
    private String putRubricResource(String targetUri, String json, String toolId) throws IOException {
        log.debug(String.format("PUT to URI '%s' body:", targetUri, json));

        HttpURLConnection conn = null;
        try {
            URL url = new URL(targetUri);
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            conn.setRequestProperty("Content-Length", "" + json.length());
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("PUT");
            String cookie = "JSESSIONID=" + sessionManager.getCurrentSession().getId() + ""
                    + System.getProperty(SERVER_ID_PROPERTY);
            conn.setRequestProperty("Cookie", cookie);
            conn.setRequestProperty("Authorization", "Bearer " + generateJsonWebToken(toolId));

            try (OutputStream os = conn.getOutputStream()) {
                os.write(json.getBytes("UTF-8"));
            }

            // read the response
            BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

            String output;
            StringWriter result = new StringWriter();
            while ((output = br.readLine()) != null) {
                result.append(output + "\n");
            }

            return result.toString();

        } catch (IOException ioException) {

            log.warn(String.format("Error updating a rubric resource at %s", targetUri), ioException);
            return null;
        } finally {
            if (conn != null) {
                try {
                    conn.disconnect();
                } catch (Exception e) {

                }
            }
        }
    }

    /**
     * Delete all the rubric associations starting with itemId.
     * @param itemId The formatted item id.
     */
    public void deleteRubricAssociationsByItemIdPrefix(String itemId, String toolId) {
        try {
            TypeReferences.ResourcesType<Resource<ToolItemRubricAssociation>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<ToolItemRubricAssociation>>() {
            };

            URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
            Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);

            Traverson.TraversalBuilder builder = traverson.follow("rubric-associations", "search",
                    "by-item-id-prefix");

            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", String.format("Bearer %s", generateJsonWebToken(toolId)));
            builder.withHeaders(headers);

            Map<String, Object> parameters = new HashMap<>();
            parameters.put("toolId", toolId);
            parameters.put("itemId", itemId);

            Resources<Resource<ToolItemRubricAssociation>> associationResources = builder
                    .withTemplateParameters(parameters).toObject(resourceParameterizedTypeReference);

            for (Resource<ToolItemRubricAssociation> associationResource : associationResources) {
                String associationHref = associationResource.getLink(Link.REL_SELF).getHref();
                deleteRubricEvaluationsForAssociation(associationHref, toolId);
                deleteRubricResource(associationHref, toolId, null);
                hasAssociatedRubricCache.remove(toolId + "#" + itemId);
            }
        } catch (Exception e) {
            log.warn("Error deleting rubric association for id {} : {}", itemId, e.getMessage());
        }
    }

    public void deleteRubricEvaluationsForAssociation(String associationHref, String tool) {
        try {
            String[] assocSplitted = associationHref.split("/");
            Long associationId = Long.valueOf(assocSplitted[assocSplitted.length - 1]);
            log.debug("Deleting evaluations for association {}", associationId);
            Collection<Resource<Evaluation>> evaluations = getRubricEvaluationsByAssociation(
                    Long.valueOf(associationId));
            for (Resource<Evaluation> eval : evaluations) {
                deleteRubricResource(eval.getLink(Link.REL_SELF).getHref(), tool, null);
            }
        } catch (Exception e) {
            log.warn("Error deleting rubric association for tool {} and association {} : {}", tool, associationHref,
                    e.getMessage());
        }
    }

    public void deleteRubricAssociation(String tool, String id) {
        try {
            Optional<Resource<ToolItemRubricAssociation>> associationResource = getRubricAssociationResource(tool,
                    id, null);
            if (associationResource.isPresent()) {
                String associationHref = associationResource.get().getLink(Link.REL_SELF).getHref();
                deleteRubricEvaluationsForAssociation(associationHref, tool);
                deleteRubricResource(associationHref, tool, null);
                ToolItemRubricAssociation association = associationResource.get().getContent();
                hasAssociatedRubricCache.remove(association.getToolId() + "#" + association.getItemId());
            }
        } catch (Exception e) {
            log.warn("Error deleting rubric association for tool {} and id {} : {}", tool, id, e.getMessage());
        }
    }

    /**
     * Delete a rubric resource (rubric, association).
     * @param query The resource href.
     * @return
     */
    private void deleteRubricResource(String query, String toolId, String siteId) throws IOException {
        HttpURLConnection conn = null;
        try {
            log.debug("Deleting rubric resource : {}", query);
            URL url = new URL(query);
            String cookie = "JSESSIONID=" + sessionManager.getCurrentSession().getId()
                    + System.getProperty(SERVER_ID_PROPERTY);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("DELETE");
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("Cookie", cookie);
            if (siteId != null) {
                conn.setRequestProperty("Authorization",
                        String.format("Bearer %s", generateJsonWebToken(toolId, siteId)));
            } else {
                conn.setRequestProperty("Authorization", String.format("Bearer %s", generateJsonWebToken(toolId)));
            }
            if (conn.getResponseCode() != 204) {
                throw new RuntimeException(
                        "Failed deleteRubricResource : HTTP error code : " + conn.getResponseCode());
            }
        } catch (MalformedURLException e) {
            log.error("Error deleting a rubric resource " + e.getMessage());
        } catch (IOException e) {
            log.error("Error deleting a rubric resource" + e.getMessage());
        } finally {
            if (conn != null) {
                try {
                    conn.disconnect();
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * Returns the JSON string for the rubric evaluation
     * @param toolId the tool id, something like "sakai.assignment"
     * @param associatedToolItemId the id of the tool item which has a rubric associated to it (e.g. assignment ID)
     * @param evaluatedItemId  the id of the tool item which is being evaluated using a rubric (e.g. assignment submission ID)
     * @return
     */
    public String getRubricEvaluation(String toolId, String associatedToolItemId, String evaluatedItemId)
            throws IOException {

        HttpURLConnection conn = null;
        try {
            URL url = new URL(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX
                    + "evaluations/search/by-tool-item-and-associated-item-and-evaluated-item-ids?toolId=" + toolId
                    + "&itemId=" + associatedToolItemId + "&evaluatedItemId=" + evaluatedItemId);

            String cookie = "JSESSIONID=" + sessionManager.getCurrentSession().getId() + ""
                    + System.getProperty(SERVER_ID_PROPERTY);

            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("Cookie", cookie);
            conn.setRequestProperty("Authorization", "Bearer" + generateJsonWebToken(toolId));

            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode());
            }

            BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

            String output;
            StringWriter result = new StringWriter();
            while ((output = br.readLine()) != null) {
                result.append(output + "\n");
            }

            return result.toString();

        } catch (MalformedURLException e) {

            log.warn("Error getting a rubric evaluation " + e.getMessage());
            return null;

        } catch (IOException e) {

            log.warn("Error getting a rubric evaluation" + e.getMessage());
            return null;
        } finally {
            if (conn != null) {
                try {
                    conn.disconnect();
                } catch (Exception e) {

                }
            }
        }
    }

    protected Collection<Resource<Evaluation>> getRubricEvaluationsByAssociation(Long associationId)
            throws Exception {
        TypeReferences.ResourcesType<Resource<Evaluation>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<Evaluation>>() {
        };

        URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
        Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);
        Traverson.TraversalBuilder builder = traverson.follow("evaluations", "search", "by-association-id");

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", String.format("Bearer %s", generateJsonWebToken(RubricsConstants.RBCS_TOOL)));
        builder.withHeaders(headers);

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("toolItemRubricAssociationId", associationId);
        Resources<Resource<Evaluation>> evaluationResources = builder.withTemplateParameters(parameters)
                .toObject(resourceParameterizedTypeReference);

        return evaluationResources.getContent();
    }

    public String generateLang() {

        StringBuilder lines = new StringBuilder();
        lines.append("var rubricsLang = {");

        Locale locale = rb.getLocale();
        Set properties = rb.keySet();
        lines.append("'" + locale.toLanguageTag() + "': {");
        Iterator keys = properties.iterator();
        while (keys.hasNext()) {
            String key = keys.next().toString();
            if (keys.hasNext()) {
                lines.append("'" + key + "': '" + rb.getString(key) + "',");
            } else {
                lines.append("'" + key + "': '" + rb.getString(key) + "'");
            }
        }

        lines.append("}");
        lines.append("}");

        log.debug(lines.toString());

        return lines.toString();
    }

    public String getCurrentSessionId() {
        return sessionManager.getCurrentSession().getId();
    }

    @Override
    public Map<String, String> transferCopyEntities(String fromContext, String toContext, List<String> ids,
            List<String> options) {

        Map<String, String> transversalMap = new HashMap<>();
        try {
            TypeReferences.ResourcesType<Resource<Rubric>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<Rubric>>() {
            };
            URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
            Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);
            Traverson.TraversalBuilder builder = traverson.follow("rubrics", "search", "rubrics-from-site");

            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization",
                    String.format("Bearer %s", generateJsonWebToken(RubricsConstants.RBCS_TOOL, toContext)));
            builder.withHeaders(headers);

            Map<String, Object> parameters = new HashMap<>();
            parameters.put("siteId", fromContext);

            Resources<Resource<Rubric>> rubricResources = builder.withTemplateParameters(parameters)
                    .toObject(resourceParameterizedTypeReference);
            for (Resource<Rubric> rubricResource : rubricResources) {
                RestTemplate restTemplate = new RestTemplate();
                HttpHeaders headers2 = new HttpHeaders();
                headers2.setContentType(MediaType.APPLICATION_JSON);
                headers2.add("Authorization",
                        String.format("Bearer %s", generateJsonWebToken(RubricsConstants.RBCS_TOOL, toContext)));
                HttpEntity<?> requestEntity = new HttpEntity<>(headers2);
                ResponseEntity<Rubric> rubricEntity = restTemplate.exchange(
                        rubricResource.getLink(Link.REL_SELF).getHref() + "?projection=inlineRubric",
                        HttpMethod.GET, requestEntity, Rubric.class);
                Rubric rEntity = rubricEntity.getBody();
                String newId = cloneRubricToSite(String.valueOf(rEntity.getId()), toContext);
                String oldId = String.valueOf(rEntity.getId());
                transversalMap.put(RubricsConstants.RBCS_PREFIX + oldId, RubricsConstants.RBCS_PREFIX + newId);
            }
        } catch (Exception ex) {
            log.info("Exception on duplicateRubricsFromSite: " + ex.getMessage());
        }
        return transversalMap;
    }

    @Override
    public Map<String, String> transferCopyEntities(String fromContext, String toContext, List<String> ids,
            List<String> options, boolean cleanup) {

        if (cleanup) {
            try {
                TypeReferences.ResourcesType<Resource<Rubric>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<Rubric>>() {
                };
                URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
                Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);
                Traverson.TraversalBuilder builder = traverson.follow("rubrics", "search", "rubrics-from-site");

                HttpHeaders headers = new HttpHeaders();
                headers.add("Authorization",
                        String.format("Bearer %s", generateJsonWebToken(RubricsConstants.RBCS_TOOL, toContext)));
                builder.withHeaders(headers);

                Map<String, Object> parameters = new HashMap<>();
                parameters.put("siteId", toContext);
                Resources<Resource<Rubric>> rubricResources = builder.withTemplateParameters(parameters)
                        .toObject(resourceParameterizedTypeReference);
                for (Resource<Rubric> rubricResource : rubricResources) {
                    String[] rubricSplitted = rubricResource.getLink(Link.REL_SELF).getHref().split("/");
                    Collection<Resource<ToolItemRubricAssociation>> assocs = getRubricAssociationByRubric(
                            rubricSplitted[rubricSplitted.length - 1], toContext);
                    for (Resource<ToolItemRubricAssociation> associationResource : assocs) {
                        String associationHref = associationResource.getLink(Link.REL_SELF).getHref();
                        deleteRubricResource(associationHref, RubricsConstants.RBCS_TOOL, toContext);
                    }
                    deleteRubricResource(rubricResource.getLink(Link.REL_SELF).getHref(),
                            RubricsConstants.RBCS_TOOL, toContext);
                }
            } catch (Exception e) {
                log.error("Rubrics - transferCopyEntities: error trying to delete rubric -> {}", e.getMessage());
            }
        }
        return transferCopyEntities(fromContext, toContext, ids, null);
    }

    private String cloneRubricToSite(String rubricId, String toSite) {
        try {
            String url = serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX + "rubrics/clone";
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.add("Authorization",
                    String.format("Bearer %s", generateJsonWebToken(RubricsConstants.RBCS_TOOL, toSite)));
            headers.add("x-copy-source", rubricId);
            headers.add("site", toSite);
            MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
            HttpEntity<?> requestEntity = new HttpEntity<>(body, headers);
            ResponseEntity<Rubric> rubricEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity,
                    Rubric.class);
            Rubric rub = rubricEntity.getBody();
            return String.valueOf(rub.getId());
        } catch (Exception e) {
            log.error("Exception when cloning rubric {} to site {} : {}", rubricId, toSite, e.getMessage());
        }
        return null;
    }

    @Override
    public String[] myToolIds() {
        return new String[] { RubricsConstants.RBCS_TOOL };
    }

    @Override
    public void updateEntityReferences(String toContext, Map<String, String> transversalMap) {
        if (transversalMap != null && !transversalMap.isEmpty()) {
            for (Map.Entry<String, String> entry : transversalMap.entrySet()) {
                String key = entry.getKey();
                //1 get all the rubrics from map
                if (key.startsWith(RubricsConstants.RBCS_PREFIX)) {
                    try {
                        //2 for each, get its associations
                        Collection<Resource<ToolItemRubricAssociation>> assocs = getRubricAssociationByRubric(
                                key.substring(RubricsConstants.RBCS_PREFIX.length()), toContext);

                        //2b get association params
                        for (Resource<ToolItemRubricAssociation> associationResource : assocs) {
                            ToolItemRubricAssociation association = associationResource.getContent();
                            Map<String, Boolean> originalParams = association.getParameters();

                            String newRubricId = entry.getValue().substring(RubricsConstants.RBCS_PREFIX.length());
                            String tool = association.getToolId();
                            String itemId = association.getItemId();
                            String newItemId = null;
                            //3 association type
                            if (RubricsConstants.RBCS_TOOL_ASSIGNMENT.equals(tool)) {
                                //3a if assignments
                                log.debug("Handling Rubrics association transfer for Assignment entry " + itemId);
                                if (transversalMap.get("assignment/" + itemId) != null) {
                                    newItemId = transversalMap.get("assignment/" + itemId)
                                            .substring("assignment/".length());
                                }
                            } else if (RubricsConstants.RBCS_TOOL_SAMIGO.equals(tool)) {
                                //3b if samigo
                                if (itemId.startsWith(RubricsConstants.RBCS_PUBLISHED_ASSESSMENT_ENTITY_PREFIX)) {
                                    log.debug("Skipping published item {}", itemId);
                                }
                                log.debug("Handling Rubrics association transfer for Samigo entry " + itemId);
                                if (transversalMap.get("sam_item/" + itemId) != null) {
                                    newItemId = transversalMap.get("sam_item/" + itemId)
                                            .substring("sam_item/".length());
                                }
                            } else if (RubricsConstants.RBCS_TOOL_FORUMS.equals(tool)) {
                                //3c if forums
                                newItemId = itemId.substring(0, 4);
                                String strippedId = itemId.substring(4);//every forum prefix have this size
                                log.debug("Handling Rubrics association transfer for Forums entry " + strippedId);
                                if (RubricsConstants.RBCS_FORUM_ENTITY_PREFIX.equals(newItemId)
                                        && transversalMap.get("forum/" + strippedId) != null) {
                                    newItemId += transversalMap.get("forum/" + strippedId)
                                            .substring("forum/".length());
                                } else if (RubricsConstants.RBCS_TOPIC_ENTITY_PREFIX.equals(newItemId)
                                        && transversalMap.get("forum_topic/" + strippedId) != null) {
                                    newItemId += transversalMap.get("forum_topic/" + strippedId)
                                            .substring("forum_topic/".length());
                                } else {
                                    log.debug("Not found updated id for item {}", itemId);
                                }
                            } else if (RubricsConstants.RBCS_TOOL_GRADEBOOKNG.equals(tool)) {
                                //3d if gradebook
                                log.debug("Handling Rubrics association transfer for Gradebook entry " + itemId);
                                if (transversalMap.get("gb/" + itemId) != null) {
                                    newItemId = transversalMap.get("gb/" + itemId).substring("gb/".length());
                                }
                            } else {
                                log.warn("Unhandled tool for Rubrics transfer between sites");
                            }

                            //4 save new association
                            if (newItemId != null) {
                                try {
                                    String input = "{\"toolId\" : \"" + tool + "\",\"itemId\" : \"" + newItemId
                                            + "\",\"rubricId\" : " + newRubricId + ",\"parameters\" : {"
                                            + setConfigurationParametersForDuplication(originalParams) + "}}";
                                    log.debug("New association " + input);
                                    String query = serverConfigurationService.getServerUrl()
                                            + RBCS_SERVICE_URL_PREFIX + "rubric-associations/";
                                    String resultPost = postRubricResource(query, input, tool, toContext);
                                    log.debug("resultPost: " + resultPost);
                                } catch (Exception exc) {
                                    log.error("Error while trying to save new association with item it {} : {}",
                                            newItemId, exc.getMessage());
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error("Error while trying to update association for Rubric {} : {}", key,
                                ex.getMessage());
                    }
                }
            }
        }
    }

    @Override
    public HttpAccess getHttpAccess() {
        return null;
    }

    @Override
    public Collection<String> getEntityAuthzGroups(Reference reference, String userId) {
        return null;
    }

    @Override
    public String getEntityUrl(Reference reference) {
        return getEntity(reference).getUrl();
    }

    @Override
    public Entity getEntity(Reference reference) {
        return null;
    }

    @Override
    public ResourceProperties getEntityResourceProperties(Reference ref) {
        return null;
    }

    @Override
    public String getEntityDescription(Reference ref) {
        return null;
    }

    @Override
    public boolean parseEntityReference(String reference, Reference ref) {
        return true;
    }

    @Override
    public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames,
            Map userIdTrans, Set userListAllowImport) {
        return null;
    }

    @Override
    public String archive(String siteId, Document doc, Stack<Element> stack, String archivePath,
            List<Reference> attachments) {
        return null;
    }

    @Override
    public boolean willArchiveMerge() {
        return true;
    }

    @Override
    public String getLabel() {
        return "rubric";
    }

    protected Collection<Resource<ToolItemRubricAssociation>> getRubricAssociationByRubric(String rubricId,
            String toSite) throws Exception {
        TypeReferences.ResourcesType<Resource<ToolItemRubricAssociation>> resourceParameterizedTypeReference = new TypeReferences.ResourcesType<Resource<ToolItemRubricAssociation>>() {
        };

        URI apiBaseUrl = new URI(serverConfigurationService.getServerUrl() + RBCS_SERVICE_URL_PREFIX);
        Traverson traverson = new Traverson(apiBaseUrl, MediaTypes.HAL_JSON);
        Traverson.TraversalBuilder builder = traverson.follow("rubric-associations", "search", "by-rubric-id");

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization",
                String.format("Bearer %s", generateJsonWebToken(RubricsConstants.RBCS_TOOL, toSite)));
        builder.withHeaders(headers);

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("rubricId", Long.valueOf(rubricId));
        Resources<Resource<ToolItemRubricAssociation>> associationResources = builder
                .withTemplateParameters(parameters).toObject(resourceParameterizedTypeReference);

        return associationResources.getContent();
    }

}