org.sakaiproject.conditions.impl.ResourceReleaseRule.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.conditions.impl.ResourceReleaseRule.java

Source

/**********************************************************************************
 * $URL: https://source.sakaiproject.org/contrib/conditionalrelease/tags/sakai_2-4-1/impl/src/java/org/sakaiproject/conditions/impl/ResourceReleaseRule.java $
 * $Id: ResourceReleaseRule.java 44304 2007-12-17 04:35:22Z zach.thomas@txstate.edu $
 ***********************************************************************************
 *
 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 Sakai Foundation
 *
 * 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.opensource.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.conditions.impl;

import java.util.*;

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.PredicateUtils;
import org.sakaiproject.authz.api.AuthzGroup;
import org.sakaiproject.authz.api.GroupNotDefinedException;
import org.sakaiproject.authz.api.Member;
import org.sakaiproject.authz.api.SecurityAdvisor;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.conditions.api.Condition;
import org.sakaiproject.conditions.api.ConditionService;
import org.sakaiproject.conditions.api.Rule;
import org.sakaiproject.content.api.ContentCollectionEdit;
import org.sakaiproject.content.api.ContentResourceEdit;
import org.sakaiproject.content.api.GroupAwareEdit;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.Notification;
import org.sakaiproject.event.api.NotificationAction;
import org.sakaiproject.event.api.NotificationService;
import org.sakaiproject.event.api.Obsoletable;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.InUseException;
import org.sakaiproject.exception.OverQuotaException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.exception.ServerOverloadException;
import org.sakaiproject.exception.TypeException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * @author zach
 *
 */
public class ResourceReleaseRule implements Rule, Obsoletable {
    private static final String SATISFIES_RULE = "resource.satisfies.rule";
    private static final long LENGTH_OF_A_DAY = 86400000; // length of a day in milliseconds
    private String resourceId;
    private List<Condition> predicates;
    private Conjunction conj;

    private ContentHostingService chs = (ContentHostingService) ComponentManager
            .get("org.sakaiproject.content.api.ContentHostingService");

    public void setContentHostingService(ContentHostingService chs) {
        this.chs = chs;
    }

    private ConditionService conditionService = (ConditionService) ComponentManager
            .get("org.sakaiproject.conditions.api.ConditionService");

    public void setConditionService(ConditionService conditionService) {
        this.conditionService = conditionService;
    }

    private SecurityService securityService = (SecurityService) ComponentManager
            .get("org.sakaiproject.authz.api.SecurityService");

    public void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    private AuthzGroupService authzGroupService = (AuthzGroupService) ComponentManager
            .get("org.sakaiproject.authz.api.AuthzGroupService");;

    public void setAuthzGroupService(AuthzGroupService authzGroupService) {
        this.authzGroupService = authzGroupService;
    }

    // we need a no-arg constructor so BaseNotificationService can instantiate these things with Class.forName(className).newInstance();
    public ResourceReleaseRule() {

    }

    public ResourceReleaseRule(String resourceId, List<Condition> predicates, Conjunction conj) {
        this.resourceId = resourceId;
        this.predicates = predicates;
        this.conj = conj;
    }

    /* (non-Javadoc)
     * @see org.apache.commons.collections.Predicate#evaluate(java.lang.Object)
     */
    public boolean evaluate(Object arg0) {
        Predicate judgement = new NullPredicate();
        if (predicates.size() == 1) {
            judgement = predicates.get(0);
        } else {
            if (conj == Conjunction.AND) {
                judgement = PredicateUtils.allPredicate(predicates);
            } else if (conj == Conjunction.OR) {
                judgement = PredicateUtils.anyPredicate(predicates);
            }
        }

        return judgement.evaluate(arg0);
    }

    /* (non-Javadoc)
     * @see org.sakaiproject.event.api.NotificationAction#getClone()
     */
    public NotificationAction getClone() {
        // TODO Auto-generated method stub
        return null;
    }

    /* (non-Javadoc)
     * @see org.sakaiproject.event.api.NotificationAction#notify(org.sakaiproject.event.api.Notification, org.sakaiproject.event.api.Event)
     */
    public void notify(Notification notification, Event event) {
        securityService.pushAdvisor(new SecurityAdvisor() {
            public SecurityAdvice isAllowed(String userId, String function, String reference) {
                return SecurityAdvice.ALLOWED;
            }
        });

        if (this.isObsolete())
            return;

        if (("gradebook.updateItemScore").equals(event.getEvent())) {
            AssignmentGrading grading = produceAssignmentGradingFromEvent(event);
            boolean shouldBeAvailable = this.evaluate(grading);
            // update access control list on ContentHostingService here
            try {
                GroupAwareEdit resource = null;
                if (chs.isCollection(this.resourceId)) {
                    resource = chs.editCollection(this.resourceId);
                } else {
                    resource = chs.editResource(this.resourceId);
                }
                ResourceProperties resourceProps = resource.getProperties();
                // since we're following a per-user event now, the global rule property should be removed
                resourceProps.removeProperty(SATISFIES_RULE);
                List<String> prop = resourceProps.getPropertyList(ContentHostingService.CONDITIONAL_ACCESS_LIST);
                if (prop == null)
                    prop = new ArrayList<String>();
                Set<String> acl = new TreeSet<String>(prop);
                if ((shouldBeAvailable && acl.contains(grading.getUserId()))
                        || (!shouldBeAvailable && !acl.contains(grading.getUserId()))) {
                    // no change to the ACL necessary, but we still have to commit the change to SATISFIES_RULE
                    if (chs.isCollection(this.resourceId)) {
                        chs.commitCollection((ContentCollectionEdit) resource);
                    } else {
                        chs.commitResource((ContentResourceEdit) resource, NotificationService.NOTI_NONE);
                    }
                    securityService.popAdvisor();
                    return;
                }
                if (!shouldBeAvailable && acl.contains(grading.getUserId())) {
                    // remove user from access list
                    acl.remove(grading.getUserId());
                    // time to re-populate the property list, start by tearing it down
                    // we only have to do it this way because props does not have a removePropertyFromList method
                    resourceProps.removeProperty(ContentHostingService.CONDITIONAL_ACCESS_LIST);
                    for (String id : acl) {
                        resourceProps.addPropertyToList(ContentHostingService.CONDITIONAL_ACCESS_LIST, id);
                    }
                } else if (shouldBeAvailable && !acl.contains(grading.getUserId())) {
                    // add user to access list
                    resourceProps.addPropertyToList(ContentHostingService.CONDITIONAL_ACCESS_LIST,
                            grading.getUserId());
                }

                if (chs.isCollection(this.resourceId)) {
                    chs.commitCollection((ContentCollectionEdit) resource);
                } else {
                    chs.commitResource((ContentResourceEdit) resource, NotificationService.NOTI_NONE);
                }
            } catch (PermissionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IdUnusedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TypeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InUseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (OverQuotaException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ServerOverloadException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                securityService.popAdvisor();
            }
        } else if ("gradebook.updateAssignment".equals(event.getEvent())
                || ("cond+gradebook.updateAssignment").equals(event.getEvent())
                || ("datetime.update".equals(event.getEvent()))) {
            // this availability applies to the whole Resource, not on a per-user basis
            // TODO set the resource availability
            // foo bar baz
            AssignmentUpdate update = produceAssignmentUpdateFromEvent(event);
            boolean shouldBeAvailable = this.evaluate(update);
            try {
                GroupAwareEdit resource = null;
                if (chs.isCollection(this.resourceId)) {
                    resource = chs.editCollection(this.resourceId);
                } else {
                    resource = chs.editResource(this.resourceId);
                }
                ResourceProperties resourceProps = resource.getProperties();
                resourceProps.addProperty(SATISFIES_RULE, Boolean.valueOf(shouldBeAvailable).toString());
                if (chs.isCollection(this.resourceId)) {
                    chs.commitCollection((ContentCollectionEdit) resource);
                } else {
                    chs.commitResource((ContentResourceEdit) resource, NotificationService.NOTI_NONE);
                }
            } catch (PermissionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IdUnusedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TypeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InUseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (OverQuotaException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ServerOverloadException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                securityService.popAdvisor();
            }

        } else if (("cond+gradebook.updateItemScore").equals(event.getEvent())) {
            // this event means the Rule has just been added
            // and we need to look at any scores that may have been recorded already
            try {
                String[] assignmentRefParts = event.getResource().split("/");
                String[] resourceRefParts = this.resourceId.split("/");
                String authzRef = "/site/" + resourceRefParts[2];
                AuthzGroup group = authzGroupService.getAuthzGroup(authzRef);
                Set<Member> members = group.getMembers();

                // build access control list up from scratch using site members
                Set<String> acl = new HashSet<String>();
                for (Member member : members) {
                    boolean shouldBeAvailable = false;
                    if (member.getRole().getId().equals(group.getMaintainRole())) {
                        // we don't bother putting maintainers in the ACL
                        continue;
                    } else {
                        Map<String, String> scoreData = conditionService.getConditionProvider("gradebook").getData(
                                "grades",
                                assignmentRefParts[2] + "|" + assignmentRefParts[3] + "|" + member.getUserId());
                        String scoreString = scoreData.get("score");
                        Double score;
                        try {
                            score = Double.parseDouble(scoreString);
                        } catch (NumberFormatException e) {
                            score = null;
                        }
                        AssignmentGrading grading = produceAssignmentGrading(member.getUserId(), score);
                        shouldBeAvailable = this.evaluate(grading);
                    }
                    if (shouldBeAvailable)
                        acl.add(member.getUserId());
                }

                // update state on this resource
                GroupAwareEdit resource = null;
                if (chs.isCollection(this.resourceId)) {
                    resource = chs.editCollection(this.resourceId);
                } else {
                    resource = chs.editResource(this.resourceId);
                }
                ResourceProperties resourceProps = resource.getProperties();
                // since we're following a per-user event now, the global rule property should be removed
                resourceProps.removeProperty(SATISFIES_RULE);
                resourceProps.removeProperty(ContentHostingService.CONDITIONAL_ACCESS_LIST);
                for (String id : acl) {
                    resourceProps.addPropertyToList(ContentHostingService.CONDITIONAL_ACCESS_LIST, id);
                }

                if (chs.isCollection(this.resourceId)) {
                    chs.commitCollection((ContentCollectionEdit) resource);
                } else {
                    chs.commitResource((ContentResourceEdit) resource, NotificationService.NOTI_NONE);
                }

            } catch (GroupNotDefinedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IdUnusedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TypeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (PermissionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InUseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (OverQuotaException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ServerOverloadException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                securityService.popAdvisor();
            }

        }

    }

    public boolean isObsolete() {
        securityService.pushAdvisor(new SecurityAdvisor() {
            public SecurityAdvice isAllowed(String userId, String function, String reference) {
                return SecurityAdvice.ALLOWED;
            }
        });
        try {
            chs.getProperties(this.resourceId);
            return false;
        } catch (PermissionException e1) {
            return true;
        } catch (IdUnusedException e1) {
            return true;
        } finally {
            securityService.popAdvisor();
        }
    }

    private AssignmentUpdate produceAssignmentUpdateFromEvent(Event event) {
        // event resource of the form: /gradebook/[gradebook id]/[assignment name]/[points possible]/[due date millis]/[is released]/[is included in course grade]/[has authz]
        AssignmentUpdate rv = new AssignmentUpdate();
        if ("datetime.update".equals(event.getEvent())) {
            for (Predicate p : this.predicates) {
                if (((Condition) p).getArgument() != null) {
                    Object arg = ((Condition) p).getArgument();
                    if ((arg instanceof String) && (((String) arg).startsWith("dateMillis:"))) {
                        rv.setDueDate(new java.util.Date(Long.parseLong(
                                ((String) ((Condition) p).getArgument()).substring("dateMillis:".length()))));
                    }
                    return rv;
                }
            }
            return rv;
        }
        String[] assignmentRefParts = event.getResource().split("/");
        rv.setTitle(assignmentRefParts[3]);
        rv.setDueDate(new Date(Long.parseLong(assignmentRefParts[5])));
        rv.setIncludedInCourseGrade(Boolean.parseBoolean(assignmentRefParts[7]));
        rv.setReleasedToStudents(Boolean.parseBoolean(assignmentRefParts[6]));
        // since we've received an update, we'd better update the predicates
        for (Predicate p : this.predicates) {
            if (((Condition) p).getArgument() != null) {
                Object arg = ((Condition) p).getArgument();
                if ((arg instanceof String) && (((String) arg).startsWith("dateMillis:"))) {
                    ((BooleanExpression) p).setArgument("dateMillis:" + rv.getDueDate().getTime());
                }
            }
        }
        return rv;
    }

    private Date addADay(Date date) {
        return (date == null) ? null : new java.util.Date(date.getTime() + LENGTH_OF_A_DAY);
    }

    /* (non-Javadoc)
     * @see org.sakaiproject.event.api.NotificationAction#set(org.w3c.dom.Element)
     */
    public void set(Element el) {

        // setup for predicates
        predicates = new Vector();

        this.resourceId = el.getAttribute("resourceId");

        String conjunction = el.getAttribute("conjunction");
        if ("OR".equals(conjunction)) {
            this.conj = Rule.Conjunction.OR;
        } else if ("AND".equals(conjunction)) {
            this.conj = Rule.Conjunction.AND;
        }

        // the children (predicates)
        NodeList children = el.getChildNodes();
        final int length = children.getLength();
        for (int i = 0; i < length; i++) {
            Node child = children.item(i);
            if (child.getNodeType() != Node.ELEMENT_NODE)
                continue;
            Element element = (Element) child;

            // look for properties
            if (element.getTagName().equals("predicates")) {
                // re-create properties
                predicates = reconstitutePredicates(element);
            }
        }

    }

    private List<Condition> reconstitutePredicates(Element element) {
        List<Condition> rv = new ArrayList<Condition>();
        try {
            Condition aPredicate = null;
            NodeList children = element.getChildNodes();
            final int length = children.getLength();
            for (int i = 0; i < length; i++) {
                Node child = children.item(i);
                if (child.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                Element predicate = (Element) child;

                // look for properties
                if (predicate.getTagName().equals("predicate")) {
                    String className = predicate.getAttribute("class");
                    aPredicate = (Condition) Class.forName(className).newInstance();
                    ((BooleanExpression) aPredicate).setReceiver(predicate.getAttribute("receiver"));
                    ((BooleanExpression) aPredicate).setMethod(predicate.getAttribute("method"));
                    ((BooleanExpression) aPredicate).setOperator(predicate.getAttribute("operator"));
                    ((BooleanExpression) aPredicate).setArgument(predicate.getAttribute("argument"));
                    rv.add(aPredicate);
                }
            }
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return rv;
    }

    /* (non-Javadoc)
     * @see org.sakaiproject.event.api.NotificationAction#set(org.sakaiproject.event.api.NotificationAction)
     */
    public void set(NotificationAction other) {
        ResourceReleaseRule eOther = (ResourceReleaseRule) other;
        resourceId = eOther.resourceId;

    }

    /* (non-Javadoc)
     * @see org.sakaiproject.event.api.NotificationAction#toXml(org.w3c.dom.Element)
     */
    public void toXml(Element el) {
        el.setAttribute("resourceId", this.resourceId);

        if (this.conj == Rule.Conjunction.OR) {
            el.setAttribute("conjunction", "OR");
        } else if (this.conj == Rule.Conjunction.AND) {
            el.setAttribute("conjunction", "AND");
        }

        Element predicates = el.getOwnerDocument().createElement("predicates");
        el.appendChild(predicates);
        for (Predicate p : this.predicates) {
            Element predicateElement = el.getOwnerDocument().createElement("predicate");
            predicateElement.setAttribute("class", p.getClass().getName());
            predicateElement.setAttribute("receiver", ((BooleanExpression) p).getReceiver());
            predicateElement.setAttribute("method", ((BooleanExpression) p).getMethod());
            predicateElement.setAttribute("operator", ((BooleanExpression) p).getOperator());
            Object argument = ((BooleanExpression) p).getArgument();
            if (argument == null) {
                argument = "";
            }
            predicateElement.setAttribute("argument", argument.toString());
            predicates.appendChild(predicateElement);
        }

    }

    private AssignmentGrading produceAssignmentGradingFromEvent(Event event) {
        Double score;
        String userId;
        String[] assignmentRefParts = event.getResource().split("/");
        // a score may be null after a grading event
        try {
            score = new Double(assignmentRefParts[5]);
        } catch (NumberFormatException e) {
            score = null;
        }
        userId = assignmentRefParts[4];

        return produceAssignmentGrading(userId, score);
    }

    private AssignmentGrading produceAssignmentGrading(String userId, Double score) {
        AssignmentGrading rv = new AssignmentGrading();
        rv.setUserId(userId);
        rv.setScore(score);
        return rv;
    }

}