blob: 2db8073e995173498e222ea4797f6a962346571c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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
*
* http://www.apache.org/licenses/LICENSE-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.apache.cocoon.portal.profile.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceSelector;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.portal.PortalService;
import org.apache.cocoon.portal.coplet.CopletData;
import org.apache.cocoon.portal.coplet.CopletFactory;
import org.apache.cocoon.portal.coplet.CopletInstanceData;
import org.apache.cocoon.portal.coplet.adapter.CopletAdapter;
import org.apache.cocoon.portal.layout.Layout;
import org.apache.cocoon.portal.profile.PortalUser;
import org.apache.cocoon.portal.profile.ProfileLS;
import org.apache.cocoon.util.ClassUtils;
import org.apache.commons.collections.map.LinkedMap;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.excalibur.source.SourceNotFoundException;
import org.apache.excalibur.source.SourceValidity;
/**
* This profile manager uses a group based approach:
* The coplet-base-data and the coplet-data are global, these are shared
* between all users.
* If the user has is own set of coplet-instance-datas/layouts these are
* loaded.
* If the user has not an own set, the group set is loaded - therefore
* each user has belong to exactly one group.
* In the case that the user does not belong to a group, a global
* profile is loaded.
*
* This profile manager does not check for changes of the profile,
* which means for example once a global profile is loaded, it is
* used until Cocoon is restarted. (This will be changed later on)
*
* THIS IS A WORK IN PROGRESS - IT'S NOT FINISHED/WORKING YET
*
* @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
*
* @version CVS $Id: AbstractUserProfileManager.java 37123 2004-08-27 12:11:53Z cziegeler $
*/
public class GroupBasedProfileManager
extends AbstractProfileManager
implements Parameterizable, Contextualizable, Initializable, Disposable {
public static final String CATEGORY_GLOBAL = "global";
public static final String CATEGORY_GROUP = "group";
public static final String CATEGORY_USER = "user";
protected static final String KEY_PREFIX = GroupBasedProfileManager.class.getName() + ':';
protected static final class ProfileInfo {
public Map objects;
public SourceValidity validity;
}
protected ProfileInfo copletBaseDatas;
protected ProfileInfo copletDatas;
/** The userinfo provider - the connection to the authentication mechanism */
protected UserInfoProvider provider;
/** The class name of the userinfo provider */
protected String userInfoProviderClassName;
/** The component context */
protected Context context;
/* (non-Javadoc)
* @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
*/
public void contextualize(Context context) throws ContextException {
this.context = context;
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters)
*/
public void parameterize(Parameters params) throws ParameterException {
this.userInfoProviderClassName = params.getParameter("userinfo-provider");
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.activity.Initializable#initialize()
*/
public void initialize() throws Exception {
this.provider = (UserInfoProvider)ClassUtils.newInstance(this.userInfoProviderClassName);
ContainerUtil.enableLogging(this.provider, this.getLogger());
ContainerUtil.contextualize(this.provider, this.context);
ContainerUtil.service(this.provider, this.manager);
ContainerUtil.initialize(this.provider);
this.copletBaseDatas = new ProfileInfo();
this.copletDatas = new ProfileInfo();
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
ContainerUtil.dispose(this.provider);
this.provider = null;
this.manager = null;
}
protected UserProfile getUserProfile(String layoutKey) {
PortalService service = null;
try {
service = (PortalService)this.manager.lookup(PortalService.ROLE);
if ( layoutKey == null ) {
layoutKey = service.getDefaultLayoutKey();
}
return (UserProfile)service.getAttribute(KEY_PREFIX + layoutKey);
} catch (ServiceException e) {
// this should never happen
throw new CascadingRuntimeException("Unable to lookup portal service.", e);
} finally {
this.manager.release(service);
}
}
protected void removeUserProfiles() {
// TODO: remove all profiles - we have to rememember all used layout keys
PortalService service = null;
try {
service = (PortalService)this.manager.lookup(PortalService.ROLE);
final String layoutKey = service.getDefaultLayoutKey();
service.removeAttribute(KEY_PREFIX + layoutKey);
} catch (ServiceException e) {
// this should never happen
throw new CascadingRuntimeException("Unable to lookup portal service.", e);
} finally {
this.manager.release(service);
}
}
protected void storeUserProfile(String layoutKey, PortalService service, UserProfile profile) {
if ( layoutKey == null ) {
layoutKey = service.getDefaultLayoutKey();
}
service.setAttribute(KEY_PREFIX + layoutKey, profile);
}
/**
* Prepares the object by using the specified factory.
*/
protected void prepareObject(Object object, PortalService service)
throws ProcessingException {
if ( object != null ) {
if ( object instanceof Map ) {
object = ((Map)object).values();
}
if (object instanceof Layout) {
service.getComponentManager().getLayoutFactory().prepareLayout((Layout)object);
} else if (object instanceof Collection) {
ServiceSelector adapterSelector = null;
try {
final CopletFactory copletFactory = service.getComponentManager().getCopletFactory();
final Iterator iterator = ((Collection)object).iterator();
while (iterator.hasNext()) {
final Object o = iterator.next();
if ( o instanceof CopletData ) {
copletFactory.prepare((CopletData)o);
} else if ( o instanceof CopletInstanceData) {
if ( adapterSelector == null ) {
adapterSelector = (ServiceSelector)this.manager.lookup(CopletAdapter.ROLE+"Selector");
}
CopletInstanceData cid = (CopletInstanceData)o;
copletFactory.prepare(cid);
// now invoke login on each instance
CopletAdapter adapter = null;
try {
adapter = (CopletAdapter) adapterSelector.select(cid.getCopletData().getCopletBaseData().getCopletAdapterName());
adapter.login( cid );
} finally {
adapterSelector.release( adapter );
}
}
}
} catch (ServiceException se) {
// this should never happen
throw new ProcessingException("Unable to get component.", se);
} finally {
this.manager.release(adapterSelector);
}
}
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#login()
*/
public void login() {
super.login();
// TODO - we should move most of the stuff from getPortalLayout to here
// for now we use a hack :)
this.getPortalLayout(null, null);
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#logout()
*/
public void logout() {
final UserProfile profile = this.getUserProfile(null);
if ( profile != null ) {
ServiceSelector adapterSelector = null;
try {
adapterSelector = (ServiceSelector)this.manager.lookup(CopletAdapter.ROLE+"Selector");
Iterator iter = profile.getCopletInstanceDatas().values().iterator();
while ( iter.hasNext() ) {
CopletInstanceData cid = (CopletInstanceData) iter.next();
CopletAdapter adapter = null;
try {
adapter = (CopletAdapter)adapterSelector.select(cid.getCopletData().getCopletBaseData().getCopletAdapterName());
adapter.logout( cid );
} finally {
adapterSelector.release( adapter );
}
}
} catch (ServiceException e) {
throw new CascadingRuntimeException("Unable to lookup portal service.", e);
} finally {
this.manager.release(adapterSelector);
}
this.removeUserProfiles();
}
super.logout();
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getCopletInstanceData(java.lang.String)
*/
public CopletInstanceData getCopletInstanceData(String copletID) {
final UserProfile profile = this.getUserProfile(null);
if ( profile != null ) {
return (CopletInstanceData)profile.getCopletInstanceDatas().get(copletID);
}
return null;
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getCopletData(java.lang.String)
*/
public CopletData getCopletData(String copletDataId) {
final UserProfile profile = this.getUserProfile(null);
if ( profile != null ) {
return (CopletData)profile.getCopletDatas().get(copletDataId);
}
return null;
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getCopletInstanceData(org.apache.cocoon.portal.coplet.CopletData)
*/
public List getCopletInstanceData(CopletData data) {
final UserProfile profile = this.getUserProfile(null);
final List coplets = new ArrayList();
if ( profile != null ) {
final Iterator iter = profile.getCopletInstanceDatas().values().iterator();
while ( iter.hasNext() ) {
final CopletInstanceData current = (CopletInstanceData)iter.next();
if ( current.getCopletData().equals(data) ) {
coplets.add( current );
}
}
}
return coplets;
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#register(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void register(CopletInstanceData coplet) {
final UserProfile profile = this.getUserProfile(null);
profile.getCopletInstanceDatas().put(coplet.getId(), coplet);
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#unregister(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void unregister(CopletInstanceData coplet) {
final UserProfile profile = this.getUserProfile(null);
profile.getCopletInstanceDatas().remove(coplet.getId());
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#register(org.apache.cocoon.portal.layout.Layout)
*/
public void register(Layout layout) {
if ( layout != null && layout.getId() != null ) {
final UserProfile profile = this.getUserProfile(null);
profile.getLayouts().put(layout.getId(), layout);
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#unregister(org.apache.cocoon.portal.layout.Layout)
*/
public void unregister(Layout layout) {
if ( layout != null && layout.getId() != null ) {
final UserProfile profile = this.getUserProfile(null);
profile.getLayouts().remove(layout.getId());
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getPortalLayout(java.lang.String, java.lang.String)
*/
public Layout getPortalLayout(String layoutKey, String layoutId) {
PortalService service = null;
try {
service = (PortalService) this.manager.lookup(PortalService.ROLE);
if ( null == layoutKey ) {
layoutKey = service.getDefaultLayoutKey();
}
UserProfile profile = this.getUserProfile(layoutKey);
if ( profile == null ) {
profile = this.loadProfile(layoutKey, service);
}
if ( profile == null ) {
throw new RuntimeException("Unable to load profile: " + layoutKey);
}
if ( layoutId != null ) {
return (Layout)profile.getLayouts().get(layoutId);
}
return profile.getRootLayout();
} catch (Exception ce) {
throw new CascadingRuntimeException("Exception during loading of profile.", ce);
} finally {
this.manager.release(service);
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getCopletDatas()
*/
public Collection getCopletDatas() {
final UserProfile profile = this.getUserProfile(null);
if ( profile != null ) {
return profile.getCopletDatas().values();
}
return null;
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getCopletInstanceDatas()
*/
public Collection getCopletInstanceDatas() {
final UserProfile profile = this.getUserProfile(null);
if ( profile != null ) {
return profile.getCopletInstanceDatas().values();
}
return null;
}
/**
* Load the profile
*/
protected UserProfile loadProfile(final String layoutKey, final PortalService service)
throws Exception {
final UserInfo info = this.provider.getUserInfo(service.getPortalName(), layoutKey);
ProfileLS loader = null;
try {
loader = (ProfileLS)this.manager.lookup( ProfileLS.ROLE );
final UserProfile profile = new UserProfile();
this.storeUserProfile(layoutKey, service, profile);
// first "load" the global data
profile.setCopletBaseDatas( this.getGlobalBaseDatas(loader, info, service) );
profile.setCopletDatas( this.getGlobalDatas(loader, info, service, profile) );
// now load the user/group specific data
if ( !this.getCopletInstanceDatas(loader, profile, info, service, CATEGORY_USER) ) {
if ( info.getGroup() == null || !this.getCopletInstanceDatas(loader, profile, info, service, CATEGORY_GROUP)) {
if ( !this.getCopletInstanceDatas(loader, profile, info, service, CATEGORY_GLOBAL) ) {
throw new ProcessingException("No profile for copletinstancedatas found.");
}
}
}
if ( !this.getLayout(loader, profile, info, service, CATEGORY_USER) ) {
if ( info.getGroup() == null || !this.getLayout(loader, profile, info, service, CATEGORY_GROUP)) {
if ( !this.getLayout(loader, profile, info, service, CATEGORY_GLOBAL) ) {
throw new ProcessingException("No profile for layout found.");
}
}
}
return profile;
} catch (ServiceException se) {
throw new CascadingRuntimeException("Unable to get component profilels.", se);
} finally {
this.manager.release( loader );
}
}
protected Map getGlobalBaseDatas(final ProfileLS loader,
final UserInfo info,
final PortalService service)
throws Exception {
synchronized ( this ) {
final Map key = this.buildKey(CATEGORY_GLOBAL,
ProfileLS.PROFILETYPE_COPLETBASEDATA,
info,
true);
final Map parameters = new HashMap();
parameters.put(ProfileLS.PARAMETER_PROFILETYPE,
ProfileLS.PROFILETYPE_COPLETBASEDATA);
if ( this.copletBaseDatas.validity != null
&& this.copletBaseDatas.validity.isValid() == SourceValidity.VALID) {
return this.copletBaseDatas.objects;
}
final SourceValidity newValidity = loader.getValidity(key, parameters);
if ( this.copletBaseDatas.validity != null
&& newValidity != null
&& this.copletBaseDatas.validity.isValid(newValidity) == SourceValidity.VALID) {
return this.copletBaseDatas.objects;
}
this.copletBaseDatas.objects = ((CopletBaseDataManager)loader.loadProfile(key, parameters)).getCopletBaseData();
this.copletBaseDatas.validity = newValidity;
this.copletDatas.objects = null;
this.copletDatas.validity = null;
this.prepareObject(this.copletBaseDatas.objects, service);
return this.copletBaseDatas.objects;
}
}
protected Map getGlobalDatas(final ProfileLS loader,
final UserInfo info,
final PortalService service,
final UserProfile profile)
throws Exception {
synchronized ( this ) {
final Map key = this.buildKey(CATEGORY_GLOBAL,
ProfileLS.PROFILETYPE_COPLETDATA,
info,
true);
final Map parameters = new HashMap();
parameters.put(ProfileLS.PARAMETER_PROFILETYPE,
ProfileLS.PROFILETYPE_COPLETDATA);
parameters.put(ProfileLS.PARAMETER_OBJECTMAP,
profile.getCopletBaseDatas());
if ( this.copletDatas.validity != null
&& this.copletDatas.validity.isValid() == SourceValidity.VALID) {
return this.copletDatas.objects;
}
final SourceValidity newValidity = loader.getValidity(key, parameters);
if ( this.copletDatas.validity != null
&& newValidity != null
&& this.copletDatas.validity.isValid(newValidity) == SourceValidity.VALID) {
return this.copletDatas.objects;
}
this.copletDatas.objects = ((CopletDataManager)loader.loadProfile(key, parameters)).getCopletData();
this.copletDatas.validity = newValidity;
this.prepareObject(this.copletDatas.objects, service);
return this.copletDatas.objects;
}
}
private boolean isSourceNotFoundException(Throwable t) {
while (t != null) {
if (t instanceof SourceNotFoundException) {
return true;
}
t = ExceptionUtils.getCause(t);
}
return false;
}
protected boolean getCopletInstanceDatas(final ProfileLS loader,
final UserProfile profile,
final UserInfo info,
final PortalService service,
final String category)
throws Exception {
Map key = this.buildKey(category,
ProfileLS.PROFILETYPE_COPLETINSTANCEDATA,
info,
true);
Map parameters = new HashMap();
parameters.put(ProfileLS.PARAMETER_PROFILETYPE,
ProfileLS.PROFILETYPE_COPLETINSTANCEDATA);
parameters.put(ProfileLS.PARAMETER_OBJECTMAP,
profile.getCopletDatas());
try {
CopletInstanceDataManager cidm = (CopletInstanceDataManager)loader.loadProfile(key, parameters);
profile.setCopletInstanceDatas(cidm.getCopletInstanceData());
this.prepareObject(profile.getCopletInstanceDatas(), service);
return true;
} catch (Exception e) {
if (!isSourceNotFoundException(e)) {
throw e;
}
return false;
}
}
protected boolean getLayout(final ProfileLS loader,
final UserProfile profile,
final UserInfo info,
final PortalService service,
final String category)
throws Exception {
final Map key = this.buildKey(category,
ProfileLS.PROFILETYPE_LAYOUT,
info,
true);
final Map parameters = new HashMap();
parameters.put(ProfileLS.PARAMETER_PROFILETYPE,
ProfileLS.PROFILETYPE_LAYOUT);
parameters.put(ProfileLS.PARAMETER_OBJECTMAP,
profile.getCopletInstanceDatas());
try {
Layout l = (Layout)loader.loadProfile(key, parameters);
this.prepareObject(l, service);
profile.setRootLayout(l);
return true;
} catch (Exception e) {
if (!isSourceNotFoundException(e)) {
throw e;
}
return false;
}
}
protected Map buildKey(String category,
String profileType,
UserInfo info,
boolean load) {
final StringBuffer config = new StringBuffer(profileType);
config.append('-');
config.append(category);
config.append('-');
if ( load ) {
config.append("load");
} else {
config.append("save");
}
final String uri = (String)info.getConfigurations().get(config.toString());
final Map key = new LinkedMap();
key.put("baseuri", uri);
key.put("separator", "?");
key.put("portal", info.getPortalName());
key.put("layout", info.getLayoutKey());
key.put("type", category);
if ( "group".equals(category) ) {
key.put("group", info.getGroup());
}
if ( "user".equals(category) ) {
key.put("user", info.getUserName());
}
return key;
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#storeProfile(org.apache.cocoon.portal.layout.Layout, java.lang.String)
*/
public void storeProfile(Layout rootLayout, String layoutKey) {
PortalService service = null;
try {
UserProfile oldProfile = this.getUserProfile(null);
service = (PortalService) this.manager.lookup(PortalService.ROLE);
if ( null == layoutKey ) {
layoutKey = service.getDefaultLayoutKey();
}
// FIXME for now we just copy the root profile, except the root layout
UserProfile newProfile = new UserProfile();
newProfile.setCopletBaseDatas(oldProfile.getCopletBaseDatas());
newProfile.setCopletDatas(oldProfile.getCopletDatas());
newProfile.setCopletInstanceDatas(oldProfile.getCopletInstanceDatas());
newProfile.setRootLayout(rootLayout);
this.storeUserProfile(layoutKey, service, newProfile);
} catch (Exception ce) {
throw new CascadingRuntimeException("Exception during loading of profile.", ce);
} finally {
this.manager.release(service);
}
}
/* (non-Javadoc)
* @see org.apache.cocoon.portal.profile.ProfileManager#getUser()
*/
public PortalUser getUser() {
PortalService service = null;
try {
service = (PortalService) this.manager.lookup(PortalService.ROLE);
final String layoutKey = service.getDefaultLayoutKey();
final UserInfo info = this.provider.getUserInfo(service.getPortalName(), layoutKey);
return info;
} catch (Exception ce) {
throw new CascadingRuntimeException("Exception during getUser().", ce);
} finally {
this.manager.release(service);
}
}
/**
* @see org.apache.cocoon.portal.profile.ProfileManager#saveUserCopletInstanceDatas(java.lang.String)
*/
public void saveUserCopletInstanceDatas(String layoutKey) {
ProfileLS adapter = null;
PortalService service = null;
try {
service = (PortalService) this.manager.lookup(PortalService.ROLE);
adapter = (ProfileLS) this.manager.lookup(ProfileLS.ROLE);
if (layoutKey == null) {
layoutKey = service.getDefaultLayoutKey();
}
final UserProfile profile = this.getUserProfile(layoutKey);
final Map parameters = new HashMap();
parameters.put(ProfileLS.PARAMETER_PROFILETYPE,
ProfileLS.PROFILETYPE_COPLETINSTANCEDATA);
final UserInfo info = this.provider.getUserInfo(service.getPortalName(), layoutKey);
final Map key = this.buildKey(CATEGORY_USER,
ProfileLS.PROFILETYPE_COPLETINSTANCEDATA,
info,
false);
// FIXME - we should be able to save without creating a CopletInstanceDataManager
CopletInstanceDataManager cidm = new CopletInstanceDataManager(profile.getCopletInstanceDatas());
adapter.saveProfile(key, parameters, cidm);
} catch (Exception e) {
// TODO
throw new CascadingRuntimeException("Exception during save profile", e);
} finally {
this.manager.release(service);
this.manager.release(adapter);
}
}
/**
* @see org.apache.cocoon.portal.profile.ProfileManager#saveUserLayout(java.lang.String)
*/
public void saveUserLayout(String layoutKey) {
ProfileLS adapter = null;
PortalService service = null;
try {
service = (PortalService) this.manager.lookup(PortalService.ROLE);
adapter = (ProfileLS) this.manager.lookup(ProfileLS.ROLE);
if (layoutKey == null) {
layoutKey = service.getDefaultLayoutKey();
}
final UserProfile profile = this.getUserProfile(layoutKey);
final Map parameters = new HashMap();
parameters.put(ProfileLS.PARAMETER_PROFILETYPE,
ProfileLS.PROFILETYPE_LAYOUT);
final UserInfo info = this.provider.getUserInfo(service.getPortalName(), layoutKey);
final Map key = this.buildKey(CATEGORY_USER,
ProfileLS.PROFILETYPE_LAYOUT,
info,
false);
adapter.saveProfile(key, parameters, profile.getRootLayout());
} catch (Exception e) {
// TODO
throw new CascadingRuntimeException("Exception during save profile", e);
} finally {
this.manager.release(service);
this.manager.release(adapter);
}
}
}