/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the License at the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.apereo.portal.services;
import java.util.Iterator;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import org.apereo.portal.EntityIdentifier;
import org.apereo.portal.concurrency.CachingException;
import org.apereo.portal.groups.CompositeServiceIdentifier;
import org.apereo.portal.groups.GroupServiceConfiguration;
import org.apereo.portal.groups.GroupsException;
import org.apereo.portal.groups.ICompositeGroupService;
import org.apereo.portal.groups.ICompositeGroupServiceFactory;
import org.apereo.portal.groups.IEntity;
import org.apereo.portal.groups.IEntityGroup;
import org.apereo.portal.groups.IGroupConstants;
import org.apereo.portal.groups.IGroupMember;
import org.apereo.portal.groups.ILockableEntityGroup;
import org.apereo.portal.properties.PropertiesManager;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.utils.threading.SingletonDoubleCheckedCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Bootstrap class for the IGroupService implementation.
*
*/
public final class GroupService implements IGroupConstants {
private static final String GROUP_SERVICE_KEY = "org.apereo.portal.services.GroupService.key_";
private static final Logger LOGGER = LoggerFactory.getLogger(GroupService.class);
// Singleton instance of the bootstrap class:
private static final SingletonDoubleCheckedCreator<GroupService> instance =
new SingletonDoubleCheckedCreator<GroupService>() {
@Override
protected GroupService createSingleton(Object... args) {
return new GroupService();
}
};
// The group service:
private ICompositeGroupService compositeGroupService = null;
/** Creates new GroupService */
private GroupService() throws GroupsException {
super();
initializeCompositeService();
}
/**
* Returns the groups that contain the <code>IGroupMember</code>.
*
* @param gm IGroupMember
*/
public static Iterator findParentGroups(IGroupMember gm) throws GroupsException {
LOGGER.trace("Invoking findParentGroups for IGroupMember [{}]", gm);
return instance().ifindParentGroups(gm);
}
/**
* Returns a pre-existing <code>IEntityGroup</code> or null if the <code>IGroupMember</code>
* does not exist.
*
* @param key String - the group key.
* @return org.apereo.portal.groups.IEntityGroup
*/
public static IEntityGroup findGroup(String key) throws GroupsException {
LOGGER.trace("Invoking findGroup for key='{}'", key);
return instance().ifindGroup(key);
}
/**
* Returns a pre-existing <code>ILockableEntityGroup</code> or null if the group is not found.
*
* @param key String - the group key.
* @param lockOwner String - the owner of the lock, typically the user.
* @return org.apereo.portal.groups.ILockableEntityGroup
*/
public static ILockableEntityGroup findLockableGroup(String key, String lockOwner)
throws GroupsException {
LOGGER.trace("Invoking findLockableGroup for key='{}', lockOwner='{}'", key, lockOwner);
return instance().ifindLockableGroup(key, lockOwner);
}
/**
* Receives notice that the UserInstance has been unbound from the HttpSession. In response, we
* remove the corresponding group member from the cache.
*
* @param person org.apereo.portal.security.IPerson
*/
public static void finishedSession(IPerson person) {
LOGGER.trace("Invoking finishedSession for IPerson [{}]", person);
try {
instance().ifinishedSession(person);
} catch (GroupsException ge) {
LOGGER.error("Error upon session finishing for person [{}]", person, ge);
}
}
/**
* Returns the <code>ICompositeGroupService</code> implementation in use.
*
* @return org.apereo.portal.groups.ICompositeGroupService
*/
public static ICompositeGroupService getCompositeGroupService() throws GroupsException {
LOGGER.trace("Invoking getCompositeGroupService()");
return instance().compositeGroupService;
}
/** @return java.lang.String */
private String getDefaultServiceName() throws GroupsException {
return (String) getServiceConfiguration().getAttributes().get("defaultService");
}
/**
* Refers to the PropertiesManager to get the key for the group associated with 'name' and asks
* the group store implementation for the corresponding <code>IEntityGroup</code>.
*/
public static IEntityGroup getDistinguishedGroup(String name) throws GroupsException {
LOGGER.trace("Invoking getDistinguishedGroup for name='{}'", name);
return instance().igetDistinguishedGroup(name);
}
/** @return java.lang.String */
public String getDistinguishedGroupKey(String name) {
return PropertiesManager.getProperty(GROUP_SERVICE_KEY + name, "");
}
/**
* Returns an <code>IEntity</code> representing a portal entity. This does not guarantee that
* the entity actually exists.
*
* @param key String - the group key.
* @param type Class - the Class of the underlying IGroupMember.
* @return org.apereo.portal.groups.IEntity
*/
public static IEntity getEntity(String key, Class<?> type) throws GroupsException {
return getEntity(key, type, null);
}
/**
* Returns an <code>IEntity</code> representing a portal entity. This does not guarantee that
* the entity actually exists.
*
* @param key String - the group key.
* @param type Class - the Class of the underlying IGroupMember.
* @param service String - the name of the component service.
* @return org.apereo.portal.groups.IEntity
*/
private static IEntity getEntity(String key, Class<?> type, String service)
throws GroupsException {
return instance().igetEntity(key, type, service);
}
/**
* Returns an <code> IGroupMember </code> representing either a group or a portal entity. If the
* parm <code> type </code> is the group type, the <code> IGroupMember </code> is an <code>
* IEntityGroup </code> else it is an <code> IEntity </code> .
*/
public static IGroupMember getGroupMember(String key, Class<?> type) throws GroupsException {
/*
* WARNING: The 'type' parameter is not the leafType; you're obligated
* to say whether you want a group or a non-group (i.e. some type of
* entity). In fact, the underlying implementation blindly instantiates
* whatever you tell it to.
*/
LOGGER.trace("Invoking getEntity for key='{}', type='{}'", key, type);
return instance().igetGroupMember(key, type);
}
/**
* Returns an <code>IGroupMember</code> representing either a group or a portal entity, based on
* the <code>EntityIdentifier</code>, which refers to the UNDERLYING entity for the <code>
* IGroupMember</code>.
*/
public static IGroupMember getGroupMember(EntityIdentifier underlyingEntityIdentifier)
throws GroupsException {
return getGroupMember(
underlyingEntityIdentifier.getKey(), underlyingEntityIdentifier.getType());
}
/**
* Refers to the PropertiesManager to get the key for the root group associated with 'type' and
* asks the group store implementation for the corresponding <code>IEntityGroup</code>.
*/
public static IEntityGroup getRootGroup(Class<?> type) throws GroupsException {
LOGGER.trace("Invoking getRootGroup for type='{}'", type);
return instance().igetRootGroup(type);
}
/** @return java.lang.String */
private GroupServiceConfiguration getServiceConfiguration() throws GroupsException {
try {
return GroupServiceConfiguration.getConfiguration();
} catch (Exception ex) {
throw new GroupsException("Problem retrieving service configuration", ex);
}
}
/**
* Returns the groups that contain the <code>IGroupMember</code>.
*
* @param gm IGroupMember
*/
private Iterator ifindParentGroups(IGroupMember gm) throws GroupsException {
return compositeGroupService.findParentGroups(gm);
}
/**
* Returns a pre-existing <code>IEntityGroup</code> or null if the <code>IGroupMember</code>
* does not exist.
*
* @param key String - the group key.
* @return org.apereo.portal.groups.IEntityGroup
*/
private IEntityGroup ifindGroup(String key) throws GroupsException {
return compositeGroupService.findGroup(key);
}
/**
* Returns a pre-existing <code>ILockableEntityGroup</code> or null if the group is not found.
*
* @param key String - the group key.
* @param lockOwner String - typically the user.
* @return org.apereo.portal.groups.ILockableEntityGroup
*/
private ILockableEntityGroup ifindLockableGroup(String key, String lockOwner)
throws GroupsException {
return compositeGroupService.findGroupWithLock(key, lockOwner);
}
/**
* Receives notice that the UserInstance has been unbound from the HttpSession. In response, we
* remove the corresponding group member from the cache. We use the roundabout route of creating
* a group member and then getting its EntityIdentifier because we need the EntityIdentifier for
* the group member, which is cached, not the EntityIdentifier for the IPerson, which is not.
*
* @param person org.apereo.portal.security.IPerson
*/
private void ifinishedSession(IPerson person) throws GroupsException {
IGroupMember gm = getGroupMember(person.getEntityIdentifier());
try {
final EntityIdentifier entityIdentifier = gm.getEntityIdentifier();
EntityCachingService.getEntityCachingService()
.remove(entityIdentifier.getType(), entityIdentifier.getKey());
} catch (CachingException ce) {
throw new GroupsException(
"Problem removing group member " + gm.getKey() + " from cache", ce);
}
}
/**
* Refers to the PropertiesManager to get the key for the group associated with 'name' and asks
* the group store implementation for the corresponding <code>IEntityGroup</code>.
*/
private IEntityGroup igetDistinguishedGroup(String name) throws GroupsException {
try {
String key = getDistinguishedGroupKey(name);
return compositeGroupService.findGroup(key);
} catch (Exception ex) {
throw new GroupsException(
"GroupService.getDistinguishedGroup(): " + "could not find key for: " + name,
ex);
}
}
/**
* Returns an <code>IEntity</code> representing a pre-existing portal entity.
*
* @param key String - the group key.
* @param type Class - the Class of the underlying IGroupMember.
* @return org.apereo.portal.groups.IEntity
*/
private IEntity igetEntity(String key, Class<?> type, String service) throws GroupsException {
return compositeGroupService.getEntity(key, type, service);
}
/**
* Returns an <code>IGroupMember</code> representing either a group or a portal entity. If the
* parm <code>type</code> is the group type, the <code>IGroupMember</code> is an <code>
* IEntityGroup</code> else it is an <code>IEntity</code>.
*/
private IGroupMember igetGroupMember(String key, Class<?> type) throws GroupsException {
return compositeGroupService.getGroupMember(key, type);
}
/**
* Refers to the PropertiesManager to get the key for the root group associated with 'type' and
* asks the group store implementation for the corresponding <code>IEntityGroup</code>.
*/
private IEntityGroup igetRootGroup(Class<?> type) throws GroupsException {
return igetDistinguishedGroup(type.getName());
}
/**
* Returns a new <code>IEntityGroup</code> for the given Class with an unused key.
*
* @return org.apereo.portal.groups.IEntityGroup
*/
private IEntityGroup inewGroup(Class<?> type) throws GroupsException {
return inewGroup(type, getDefaultServiceName());
}
/**
* Returns a new <code>IEntityGroup</code> for the given Class with an unused key.
*
* @return org.apereo.portal.groups.IEntityGroup
*/
private IEntityGroup inewGroup(Class<?> type, String serviceName) throws GroupsException {
try {
return compositeGroupService.newGroup(type, parseServiceName(serviceName));
} catch (InvalidNameException ine) {
throw new GroupsException("GroupService.inewGroup(): invalid service name", ine);
}
}
/** @exception GroupsException */
private void initializeCompositeService() throws GroupsException {
String eMsg = null;
try {
GroupServiceConfiguration cfg = getServiceConfiguration();
String factoryName = (String) cfg.getAttributes().get("compositeFactory");
if (factoryName == null) {
eMsg =
"GroupService.initialize(): No entry for CompositeServiceFactory in configuration";
LOGGER.error(eMsg);
throw new GroupsException(eMsg);
}
ICompositeGroupServiceFactory serviceFactory =
(ICompositeGroupServiceFactory) Class.forName(factoryName).newInstance();
compositeGroupService = serviceFactory.newGroupService();
} catch (Exception e) {
eMsg =
"GroupService.initialize(): Problem creating groups service... "
+ e.getMessage();
LOGGER.error(eMsg, e);
throw new GroupsException(eMsg, e);
}
}
private static GroupService instance() throws GroupsException {
return instance.get();
}
/**
* Returns a new <code>IEntityGroup</code> for the given Class with an unused key.
*
* @return org.apereo.portal.groups.IEntityGroup
*/
public static IEntityGroup newGroup(Class<?> type) throws GroupsException {
LOGGER.trace("Invoking newGroup for type='{}'", type);
return instance().inewGroup(type);
}
/**
* Converts the String form of a service name into a Name.
*
* @return javax.naming.Name
* @exception InvalidNameException
* @exception GroupsException
*/
public static Name parseServiceName(String serviceName)
throws InvalidNameException, GroupsException {
return new CompositeServiceIdentifier(serviceName).getServiceName();
}
public static EntityIdentifier[] searchForEntities(String query, int method, Class<?> type)
throws GroupsException {
LOGGER.trace(
"Invoking searchForEntities for query='{}', method='{}', type='{}'",
query,
method,
type);
return instance().compositeGroupService.searchForEntities(query, method, type);
}
public static EntityIdentifier[] searchForEntities(
String query, int method, Class<?> type, IEntityGroup ancestor) throws GroupsException {
LOGGER.trace(
"Invoking searchForEntities for query='{}', method='{}', type='{}', ancestor='{}'",
query,
method,
type,
ancestor);
return instance().compositeGroupService.searchForEntities(query, method, type, ancestor);
}
public static EntityIdentifier[] searchForGroups(String query, int method, Class<?> leaftype)
throws GroupsException {
LOGGER.trace(
"Invoking searchForGroups for query='{}', method='{}', leaftype='{}'",
query,
method,
leaftype);
return instance().compositeGroupService.searchForGroups(query, method, leaftype);
}
public static EntityIdentifier[] searchForGroups(
String query, int method, Class<?> leaftype, IEntityGroup ancestor)
throws GroupsException {
LOGGER.trace(
"Invoking searchForGroups for query='{}', method='{}', leaftype='{}', ancestor='{}'",
query,
method,
leaftype,
ancestor);
return instance().compositeGroupService.searchForGroups(query, method, leaftype, ancestor);
}
}