/* * Group.java * * Copyright (c) 2013, Instituto Superior Técnico. All rights reserved. * * This file is part of bennu-core. * * bennu-core is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * * bennu-core is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with bennu-core. If not, see * <http://www.gnu.org/licenses/>. */ package org.fenixedu.bennu.core.domain.groups; import java.util.Collection; import java.util.Collections; import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; import org.fenixedu.bennu.core.annotation.GroupOperator; import org.fenixedu.bennu.core.domain.Bennu; import org.fenixedu.bennu.core.domain.User; import org.fenixedu.bennu.core.domain.exceptions.AuthorizationException; import org.fenixedu.bennu.core.groups.Group; import org.fenixedu.bennu.core.security.Authenticate; import org.joda.time.DateTime; import pt.ist.fenixframework.Atomic; import pt.ist.fenixframework.Atomic.TxMode; import pt.ist.fenixframework.dml.runtime.Relation; /** * <p> * {@code PersistentGroup}s represent access groups. These groups are domain entities but immutable and unique in semantics (with * the sole exception of {@link PersistentDynamicGroup}). That means that there is only one instance of a group representing * authenticated users, or only one instance of a group for a specific set of users * </p> * * <p> * This is the persistent counter part of {@link Group}. * </p> * * <p> * Subclasses should not have public constructors, instead they should provide {@code getInstance(...)} methods that ensure non * duplication of groups with the same semantics. Subclasses should also ensure immutability, all operators return new instances * of groups instead of changing the current one. * </p> * * <p> * <b>Note:</b> Whereas in most situations users will not deal with {@link PersistentGroup} subclasses directly, if possible, the * core methods ( {@link #isMember(User)}, {@link #getMembers()}) should be invoked directly, as subclasses may have a more * performant way of computing the proper values. * </p> * * @see PersistentDynamicGroup * @see GroupOperator */ public abstract class PersistentGroup extends PersistentGroup_Base { protected PersistentGroup() { super(); setRoot(Bennu.getInstance()); } public abstract Group toGroup(); /** * Human readable, internationalized textual representation of this group. * * @return internationalized name of the group. */ public final String getPresentationName() { return toGroup().getPresentationName(); } /** * Textual representation of this group in the group language. * * @return this group in group language. */ public final String expression() { return toGroup().getExpression(); } /** * Computes the full member list of this group. Potentially processor consuming operation, preferably developers should orient * code to {@link #isMember(User)} or {@link #isMember(User, DateTime)} methods. * * @return all member users in the system at the exact moment of the invocation */ public Stream<User> getMembers() { return toGroup().getMembers(); } /** * Same as {@link #getMembers()} but at a given moment in time. This is like a time-machine for the groups domain. * * @param when * moment when to fetch the user list. * @return all member users in the system at the requested moment */ public Stream<User> getMembers(DateTime when) { return toGroup().getMembers(when); } /** * Tests if the given user is a member of the group. * * @param user * the user to test * @return <code>true</code> if member, <code>false</code> otherwise * * @see #verify() */ public boolean isMember(User user) { return toGroup().isMember(user); } /** * Same as {@link #isMember(User)} but at a given moment in time. This is like a time-machine for the groups domain. * * @param user * the user to test * @param when * moment when to test the user. * @return <code>true</code> if member, <code>false</code> otherwise */ public boolean isMember(User user, DateTime when) { return toGroup().isMember(user, when); } /** * Tests if the given user is a member of the group, throwing an exception if not. * * @throws AuthorizationException * if user is not a member of the group. */ public final void verify() throws AuthorizationException { if (!isMember(Authenticate.getUser())) { throw AuthorizationException.unauthorized(); } } protected Collection<Relation<?, ?>> getContextRelations() { return Collections.emptySet(); } /** * Delete the object from the system if not in use. Works by disconnecting from the context relations. */ protected void gc() { if (GroupGC.emptyCustomRelations(this)) { setRoot(null); GroupGC.cleanContext(this); deleteDomainObject(); } } public static void garbageCollect() { GroupGC.gc(); } public static void garbageCollect(PersistentGroup group) { group.gc(); } protected static <T extends PersistentGroup> T singleton(Supplier<Optional<T>> selector, Supplier<T> creator) { return selector.get().orElseGet(() -> create(selector, creator)); } @Atomic(mode = TxMode.WRITE) private static <T extends PersistentGroup> T create(Supplier<Optional<T>> selector, Supplier<T> creator) { return selector.get().orElseGet(creator); } }