/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/portal/trunk/portal-render-impl/impl/src/java/org/sakaiproject/portal/render/portlet/services/SakaiOptionalPortletContainerServices.java $ * $Id: SakaiOptionalPortletContainerServices.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2005, 2006, 2007, 2008 The 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.portal.render.portlet.services; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.portlet.PortletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pluto.OptionalContainerServices; import org.apache.pluto.PortletContainerException; import org.apache.pluto.PortletWindow; import org.apache.pluto.internal.InternalPortletPreference; import org.apache.pluto.internal.impl.PortletPreferenceImpl; import org.apache.pluto.spi.optional.P3PAttributes; import org.apache.pluto.spi.optional.PortalAdministrationService; import org.apache.pluto.spi.optional.PortletEnvironmentService; import org.apache.pluto.spi.optional.PortletInfoService; import org.apache.pluto.spi.optional.PortletInvocationEvent; import org.apache.pluto.spi.optional.PortletInvocationListener; import org.apache.pluto.spi.optional.PortletInvokerService; import org.apache.pluto.spi.optional.PortletPreferencesService; import org.apache.pluto.spi.optional.PortletRegistryService; import org.apache.pluto.spi.optional.UserInfoService; // This new service is added in Pluto 1.1.6 and later // https://issues.apache.org/jira/browse/PLUTO-489 // http://jira.sakaiproject.org/browse/SAK-19011 import org.apache.pluto.spi.optional.RequestAttributeService; import org.sakaiproject.authz.cover.SecurityService; import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.thread_local.cover.ThreadLocalManager; import org.sakaiproject.tool.api.Placement; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.cover.UserDirectoryService; /** * @author csev * @since Sakai 2.4 * @version $Rev: 105079 $ */ public class SakaiOptionalPortletContainerServices implements OptionalContainerServices { private static Log M_log = LogFactory .getLog(SakaiOptionalPortletContainerServices.class); protected final static String CURRENT_PLACEMENT = "sakai:ToolComponent:current.placement"; // OptionalContainerServices Impl ------------------------------------------ private UserInfoService userInfoService = new SakaiUserInfoService(); private PortalAdministrationService portalAdministrationService = new SakaiPortalAdministrationService(); private PortletPreferencesService prefService = new SakaiPortletPreferencesService(); private boolean prefLog = true; public PortletPreferencesService getPortletPreferencesService() { if (prefLog) M_log.info("Sakai Optional Portal Services returning " + prefService); prefLog = false; // Only log once return prefService; } public PortletRegistryService getPortletRegistryService() { return null; } public PortletEnvironmentService getPortletEnvironmentService() { return null; } public PortletInvokerService getPortletInvokerService() { return null; } public PortletInfoService getPortletInfoService() { return null; } // This new service is added in Pluto 1.1.6 and later // https://issues.apache.org/jira/browse/PLUTO-489 // http://jira.sakaiproject.org/browse/SAK-19011 public RequestAttributeService getRequestAttributeService() { return null; } public PortalAdministrationService getPortalAdministrationService() { return portalAdministrationService; } public UserInfoService getUserInfoService() { return userInfoService; } // Our implementations of these local services // At some level this could return a clever proxy which did lazy loading public class SakaiUserInfoService implements UserInfoService { public Map getUserInfo(PortletRequest request, PortletWindow window) throws PortletContainerException { return getUserInfo(request); } public Map getUserInfo(PortletRequest request) throws PortletContainerException { Map retval = null; setupThread(request, true); User user = UserDirectoryService.getCurrentUser(); if (user != null) { // System.out.println("Found Current User="+user.getEid()); retval = new HashMap<String, String>(); retval.put(P3PAttributes.USER_HOME_INFO_ONLINE_EMAIL, user.getEmail()); retval .put(P3PAttributes.USER_BUSINESS_INFO_ONLINE_EMAIL, user .getEmail()); retval.put(P3PAttributes.USER_NAME_GIVEN, user.getFirstName()); retval.put(P3PAttributes.USER_NAME_FAMILY, user.getLastName()); retval.put(P3PAttributes.USER_NAME_NICKNAME, user.getDisplayName()); // Add some GridSphere compatibility retval.put("user.name", user.getEid()); retval.put("user.id", user.getEid()); retval.put("user.login.id", user.getEid()); retval.put("user.name.full", user.getDisplayName()); retval.put("user.name.first", user.getFirstName()); retval.put("user.name.last", user.getLastName()); retval.put("user.email", user.getEmail()); // GridSphere not supported yet // user.organization, user.lastlogintime, user.timezone, // user.locale } if (retval == null) retval = new HashMap(); // System.out.println("Returning=" +retval); return retval; } } public class SakaiPortalAdministrationService implements PortalAdministrationService { private List administrativeRequestListeners = null; private List portletInvocationListeners = null; public List getAdministrativeRequestListeners() { if (administrativeRequestListeners == null) { administrativeRequestListeners = new ArrayList(); } return administrativeRequestListeners; } public List getPortletInvocationListeners() { if (portletInvocationListeners == null) { portletInvocationListeners = new ArrayList(1); portletInvocationListeners.add(new SakaiPortletServletListener()); } return portletInvocationListeners; } public class SakaiPortletServletListener implements PortletInvocationListener { public void onBegin(PortletInvocationEvent event) { // System.out.println("======== onBegin!"); setupThread(event.getPortletRequest(), true); } public void onEnd(PortletInvocationEvent event) { // System.out.println("======== onEnd!"); } public void onError(PortletInvocationEvent event, Throwable error) { // System.out.println("======== onError!"); } } } private void setupThread(PortletRequest request, boolean doLog) { String placementId = (String) request .getAttribute("org.sakaiproject.portal.api.PortalService_placementid"); // System.out.println("place from getAttribute = "+placementId); if (placementId == null) { if (doLog) M_log.info("No Placement found"); return; // We have nothing to work with } Session session = SessionManager.getCurrentSession(); // System.out.println("Session = "+session); if (session == null) { if (doLog) M_log.info("No Session found placementId=" + placementId); return; // We have nothing to work with } // System.out.println("UserId="+session.getUserId()+" // UserEID="+session.getUserEid()); // Check to see if there is already a placement in place Placement ppp = (Placement) ThreadLocalManager.get(CURRENT_PLACEMENT); // System.out.println("ThreadLocal CURRENT_PLACEMENT="+ppp); if (ppp != null) { // System.out.println("ThreadLocal CURRENT_PLACEMENT // ID="+ppp.getId()); if (placementId.equals(ppp.getId())) { // System.out.println("Thread already setup"); return; // Placement in place } } // find the tool from some site (ToolConfiguration extends Placement) ToolConfiguration siteTool = SiteService.findTool(placementId); // System.out.println("siteTool="+siteTool); if (siteTool == null) { if (doLog) M_log.info("No ToolConfiguration found, placementId=" + placementId + " session=" + session); return; } // Actually store the placement in Thread Local ThreadLocalManager.set(CURRENT_PLACEMENT, siteTool); // *** Testing Printout to see how well we have the APIs Configured **** // ToolSession ts = SessionManager.getCurrentToolSession(); // System.out.println("*** TEST *** \nTool Session = "+ts); // if ( ts != null ) // System.out.println("ToolSession.getId="+ts.getId()); // Placement placement = ToolManager.getCurrentPlacement(); // System.out.println("Placement = "+placement); // if ( placement != null ) { // String placementContext = placement.getContext(); // System.out.println("Context = "+placementContext); // } } public class SakaiPortletPreferencesService implements PortletPreferencesService { public SakaiPortletPreferencesService() { // Do nothing. } /** * Returns the stored portlet preferences array. The preferences managed * by this service should be protected from being directly accessed, so * this method returns a cloned copy of the stored preferences. * * @param portletWindow * the portlet window. * @param request * the portlet request from which the remote user is retrieved. * @return a copy of the stored portlet preferences array. * @throws PortletContainerException */ public InternalPortletPreference[] getStoredPreferences( PortletWindow portletWindow, PortletRequest request) throws PortletContainerException { boolean readOnly = true; // Set up the thread if we have not already done so setupThread(request, true); // Get the Placement Id String key = portletWindow.getId().getStringId(); // find the tool from some site ToolConfiguration siteTool = SiteService.findTool(key); // System.out.println("siteTool="+siteTool); ArrayList<InternalPortletPreference> prefArray = new ArrayList<InternalPortletPreference>(); if (siteTool != null) { String siteId = siteTool.getSiteId(); // System.out.println("siteId="+siteId); String siteReference = SiteService.siteReference(siteId); // System.out.println("Reference="+siteReference); // If you don't have site.upd - Mark all references as read only readOnly = !SecurityService.unlock(SiteService.SECURE_UPDATE_SITE, siteReference); Properties props = siteTool.getPlacementConfig(); // System.out.println("props = "+props); for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { String propertyName = (String) e.nextElement(); // System.out.println("Property name = "+propertyName); if (propertyName != null && propertyName.startsWith("javax.portlet:") && propertyName.length() > 14) { String propertyValue = props.getProperty(propertyName); String[] propertyList = deSerializeStringArray(propertyValue); String internalName = propertyName.substring(14); // System.out.println("internalName="+internalName+" // propertyList="+propertyList); InternalPortletPreference newPref = new PortletPreferenceImpl( internalName, propertyList, readOnly); // System.out.println("newPref = "+newPref); prefArray.add(newPref); } else if ( propertyName != null ) { String propertyValue = props.getProperty(propertyName); String internalName = "sakai:" + propertyName; String[] propertyList = new String[1]; propertyList[0] = propertyValue; // System.out.println("internalName="+internalName+"propertyList="+propertyList); InternalPortletPreference newPref = new PortletPreferenceImpl( internalName, propertyList, readOnly); // System.out.println("newPref = "+newPref); prefArray.add(newPref); } } } InternalPortletPreference[] preferences = new InternalPortletPreference[prefArray .size()]; preferences = (InternalPortletPreference[]) prefArray.toArray(preferences); if (M_log.isDebugEnabled()) { M_log.debug("Got " + preferences.length + " stored preferences."); } return preferences; } /** * Stores the portlet preferences to the in-memory storage. This method * should be invoked after the portlet preferences are validated by the * preference validator (if defined). * <p> * The preferences managed by this service should be protected from * being directly accessed, so this method clones the passed-in * preferences array and saves it. * </p> * * @see javax.portlet.PortletPreferences#store() * @param portletWindow * the portlet window * @param request * the portlet request from which the remote user is retrieved. * @param preferences * the portlet preferences to store. * @throws PortletContainerException */ public void store(PortletWindow portletWindow, PortletRequest request, InternalPortletPreference[] preferences) throws PortletContainerException { // Set up the thread if we have not already done so setupThread(request, true); String key = portletWindow.getId().getStringId(); // find the tool from some site ToolConfiguration siteTool = SiteService.findTool(key); // System.out.println("siteTool="+siteTool); if (siteTool == null) return; Properties props = siteTool.getPlacementConfig(); if (props == null) return; String siteId = siteTool.getSiteId(); // System.out.println("siteId="+siteId); String siteReference = SiteService.siteReference(siteId); // System.out.println("Reference="+siteReference); // If you don't have site.upd - silently return not storing // In an ideal world perhaps we should throw java.io.IOException // As per PortletPreferences API on the store() method if (!SecurityService.unlock(SiteService.SECURE_UPDATE_SITE, siteReference)) { // System.out.println("You do not have site.upd - silently // returning and not storing"); return; } // System.out.println("props before cleanup= "+props); boolean changed = false; // Remove properties from the placement which did not come back to // be stored if (props != null) { for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { String propertyName = (String) e.nextElement(); // System.out.println("Checking Sakai property name = "+propertyName); if (propertyName != null && propertyName.startsWith("javax.portlet:") && propertyName.length() > 14) { String internalName = propertyName.substring(14); // System.out.println("making sure we still have a prop // named internalName="+internalName); boolean found = false; for (int i = 0; i < preferences.length; i++) { if (preferences[i] != null) { String propName = preferences[i].getName(); // System.out.println("Store["+i+"] // ="+propName); if (internalName.equals(propName)) { found = true; break; } } } if (!found) { // System.out.println("Removing "+propertyName); props.remove(propertyName); changed = true; } // A sakai direct property } else { boolean found = false; for (int i = 0; i < preferences.length; i++) { if (preferences[i] != null) { String propName = "sakai:"+preferences[i].getName(); if (propertyName.equals(propName)) { found = true; break; } } } if (!found) { // System.out.println("Removing "+propertyName); props.remove(propertyName); changed = true; } } } } // System.out.println("props after cleanup= "+props); // Add / up date which are still there for (int i = 0; i < preferences.length; i++) { // System.out.println("Store["+i+"] ="+preferences[i]); if (preferences[i] != null && props != null) { String propName = preferences[i].getName(); if ( propName == null || propName.length() < 1 ) continue; // System.out.println("Property Name="+propName); String propKey = "javax.portlet:" + propName; String storeString = serializeStringArray(preferences[i].getValues()); // Write directly to the Sakai properties if ( propName.startsWith("sakai:") && propName.length() > 6 ) { propKey = propName.substring(6); storeString = preferences[i].getValues()[0]; } // Grab the property to see if it changed String oldString = props.getProperty(propKey); // System.out.println("propKey = "+propKey); // System.out.println("storeString = "+storeString); // System.out.println("oldString = "+oldString); if (storeString!=null && !storeString.equals(oldString)) { // System.out.println("Setting "+propKey+" // value="+storeString); props.setProperty(propKey, storeString); changed = true; } } } // System.out.println("props after update= "+props); // System.out.println("changed="+changed); if (changed && siteTool != null) { siteTool.save(); // System.out.println("Saved"); } if (M_log.isDebugEnabled()) { M_log.debug("Portlet preferences stored for: " + key); } } private String serializeStringArray(String[] input) { if (input == null || input.length < 1) return null; StringBuffer retval = new StringBuffer(); for (int i = 0; i < input.length; i++) { if (i > 0) retval.append("!"); if ( input[i] != null ) retval.append(URLEncoder.encode(input[i])); } return retval.toString(); } private String[] deSerializeStringArray(String input) { // System.out.println("Input="+input); String[] retval = input.split("!"); // System.out.println("Found "+retval.length+" items."); for (int i = 0; i < retval.length; i++) { // System.out.println("retval["+i+"]="+retval[i]); if ( retval[i] == null ) continue; retval[i] = URLDecoder.decode(retval[i]); } return retval; } } // End of SakaiPortletPreferencesService }