001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.pluto; 018 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.List; 022 023 import org.apache.commons.logging.Log; 024 import org.apache.commons.logging.LogFactory; 025 import org.apache.geronimo.gbean.GBeanInfo; 026 import org.apache.geronimo.gbean.GBeanInfoBuilder; 027 import org.apache.geronimo.gbean.GBeanLifecycle; 028 import org.apache.pluto.driver.services.portal.PageConfig; 029 import org.apache.pluto.driver.services.portal.PortletWindowConfig; 030 import org.apache.pluto.driver.services.portal.RenderConfigService; 031 import org.apache.pluto.driver.services.portal.admin.RenderConfigAdminService; 032 033 /* GBean for adding an item to the Administration Console's Navigator. Web apps 034 * that contain portlets can add those portlets to the navigator by including 035 * a GBean in their deployment plans like this: 036 * 037 * <gbean name="MyAdminConsoleExtension" class="org.apache.geronimo.pluto.AdminConsoleExtensionGBean"> 038 * <attribute name="pageTitle">Hello</attribute> 039 * <attribute name="portletContext">/HelloWorldPortlet</attribute> 040 * <attribute name="portletList">[portlet_id_1, portlet_id_2]</attribute> 041 * </gbean> 042 */ 043 public class AdminConsoleExtensionGBean implements GBeanLifecycle { 044 private static final Log log = LogFactory.getLog(AdminConsoleExtensionGBean.class); 045 public static final GBeanInfo GBEAN_INFO; 046 private final String pageTitle; 047 private final String portletContext; 048 private final ArrayList<String> portletList; 049 private final String icon; 050 private final PortalContainerServices portletContainerServices; 051 052 /* Constructor for GBean */ 053 public AdminConsoleExtensionGBean(String pageTitle, String portletContext, ArrayList<String> portletList, PortalContainerServices portalContainerServices) { 054 this(pageTitle, portletContext, portletList, null, portalContainerServices); 055 } 056 057 public AdminConsoleExtensionGBean(String pageTitle, String portletContext, ArrayList<String> portletList, String icon, PortalContainerServices portalContainerServices) { 058 super(); 059 this.pageTitle = pageTitle; 060 this.portletContext = portletContext; 061 this.portletList = portletList; 062 this.icon = icon; 063 this.portletContainerServices = portalContainerServices; 064 } 065 066 /* 067 * Add the PageConfig to pluto. This will overwrite any existing pages with the same page name 068 * @see org.apache.geronimo.pluto.PlutoAccessInterface#addPage(org.apache.pluto.driver.services.portal.PageConfig) 069 */ 070 private void addPage(PageConfig pageConfig) { 071 //Add the new PageConfig to Pluto 072 RenderConfigAdminService renderConfig = portletContainerServices.getAdminConfiguration().getRenderConfigAdminService(); 073 renderConfig.addPage(pageConfig); 074 } 075 076 /* 077 * This will add the portlets to the PageConfig in Pluto. 078 * @see org.apache.geronimo.pluto.PlutoAccessInterface#addPortlets(java.lang.String, java.lang.String, java.util.ArrayList) 079 */ 080 private void addPortlets() { 081 if (pageExists()) { 082 PageConfig pageConfig = getPageConfigFromPluto(); 083 int portletCount = portletList.size(); 084 for (int i = 0; i < portletCount; i++) { 085 pageConfig.addPortlet(portletContext, portletList.get(i)); 086 } 087 } else { 088 log.warn("Cannot add portlets to non-existent page " + pageTitle); 089 } 090 } 091 092 /* 093 * Removes a PageConfig object in Pluto with the pageTitle 094 * @see org.apache.geronimo.pluto.PlutoAccessInterface#removePage(java.lang.String) 095 */ 096 private void removePage() { 097 //all we really need is a PageConfig with the page name 098 PageConfig pageConfig = createPageConfig(); 099 100 RenderConfigAdminService renderConfig = portletContainerServices.getAdminConfiguration().getRenderConfigAdminService(); 101 102 //This removePage method was added into Pluto as a patch (PLUTO-387). addPage functionality 103 //was available, but removePage functionality was unavailable. 104 //NOTE: getList returns a copy of the Map that stores page data in List form. Since it's a copy, 105 //it does not serve our purpose, and since no simple workaround was available, the simpler 106 //solution was just to try to patch the code. 107 renderConfig.removePage(pageConfig); 108 } 109 110 /* 111 * Removes the portletList from the PageConfig in Pluto 112 * @see org.apache.geronimo.pluto.PlutoAccessInterface#removePortlets(java.lang.String, java.util.ArrayList) 113 */ 114 private void removePortlets() { 115 if (pageExists()) { 116 PageConfig pageConfig = getPageConfigFromPluto(); 117 int portletCount = portletList.size(); 118 Collection<String> list = pageConfig.getPortletIds(); 119 120 //run through the list of portlets to remove 121 for (int i = 0; i < portletCount; i++) { 122 String portletName = portletList.get(i); 123 124 //run through the list of portlets on this page and see if we can find a match 125 for (String pid : list) { 126 String pletContext = PortletWindowConfig.parseContextPath(pid); 127 String pletName = PortletWindowConfig.parsePortletName(pid); 128 if (portletContext.equals(pletContext) && portletName.equals(pletName)) { 129 pageConfig.removePortlet(pid); 130 break; 131 } 132 } 133 } 134 } else { 135 log.warn("can't remove portlets from non-existent page " + pageTitle); 136 } 137 } 138 139 /* 140 * Creates a new PageConfig object with this GBean's pageTitle 141 */ 142 private PageConfig createPageConfig() { 143 //Create a new PageConfig 144 PageConfig pageConfig = new PageConfig(); 145 pageConfig.setName(pageTitle); 146 pageConfig.setUri("/WEB-INF/themes/default-theme.jsp"); 147 pageConfig.setIcon(icon); 148 return pageConfig; 149 } 150 151 /* 152 * Gets the PageConfig object from Pluto with that name 153 */ 154 private PageConfig getPageConfigFromPluto() { 155 RenderConfigService service = portletContainerServices.getRenderConfigService(); 156 157 return service.getPage(pageTitle); 158 } 159 160 /* 161 * return true if Pluto contains a PageConfig with that name, else false 162 */ 163 private boolean pageExists() { 164 if (pageTitle == null) return false; 165 166 RenderConfigService service = portletContainerServices.getRenderConfigService(); 167 168 List<PageConfig> pageConfigs = service.getPages(); 169 for (PageConfig pageConfig : pageConfigs) { 170 if (pageTitle.equals(pageConfig.getName())) { 171 return true; 172 } 173 } 174 175 return false; 176 } 177 178 /* 179 * returns true if Pluto contains a PageConfig with that name and it has 0 or if there isn't a page with that name 180 */ 181 private boolean pageIsEmpty() { 182 if (pageExists()) { 183 PageConfig pageConfig = getPageConfigFromPluto(); 184 return pageConfig.getPortletIds().size() == 0; 185 } else { 186 log.debug("no pageConfig found for " + pageTitle); 187 return true; 188 } 189 } 190 191 /* 192 * Called when the GBean is started 193 * This adds/updates a Page in Pluto according to this GBean's specifications (ACE) 194 * @see org.apache.geronimo.gbean.GBeanLifecycle#doStart() 195 */ 196 public synchronized void doStart() throws Exception { 197 // check to make sure that a portal driver has registered with the container services 198 if (portletContainerServices.getAdminConfiguration() == null) { 199 throw new RuntimeException("No portal driver has been registered with the portal container services"); 200 } 201 202 //add the page if it doesn't exist yet 203 if (!pageExists()) { 204 //create a PageConfig 205 PageConfig newPageConfig = createPageConfig(); 206 addPage(newPageConfig); 207 } 208 209 //add portlets to this newly created page 210 addPortlets(); 211 log.debug("Started AdminConsoleExtensionGBean for " + pageTitle); 212 } 213 214 /* 215 * Called when the GBean is stopped 216 * This removes/updates a Page in Pluto according to this GBean's specifications 217 * @see org.apache.geronimo.gbean.GBeanLifecycle#doStop() 218 */ 219 public synchronized void doStop() throws Exception { 220 try { 221 //remove portlets from the page 222 removePortlets(); 223 224 //if the page has 0 portlets on it, then go ahead and remove it 225 if (pageIsEmpty()) { 226 removePage(); 227 } 228 } catch (NullPointerException e) { 229 // during normal server shutdown the portal driver has been shut down before 230 // the admin console extensions. the way that pluto is currently implemented 231 // is that when the portal driver shuts down it destroys all its services. 232 // this leads to an NPE when you try to use them. currently there is no 233 // way to check to see if a service has been shut down. 234 log.debug("could not remove portlets for " + pageTitle, e); 235 } 236 237 log.debug("Stopped AdminConsoleExtensionGBean for " + pageTitle); 238 } 239 240 /* 241 * Called when the GBean fails 242 * @see org.apache.geronimo.gbean.GBeanLifecycle#doFail() 243 */ 244 public synchronized void doFail() { 245 log.warn("AdminConsoleExtensionGBean for " + pageTitle + " failed."); 246 } 247 248 /* 249 * Standard GBean information 250 */ 251 static { 252 GBeanInfoBuilder infoFactory = new GBeanInfoBuilder("AdminConsoleExtensionGBean", AdminConsoleExtensionGBean.class); 253 infoFactory.addAttribute("pageTitle", String.class, true, true); 254 infoFactory.addAttribute("portletContext", String.class, true, true); 255 infoFactory.addAttribute("portletList", ArrayList.class, true, true); 256 infoFactory.addAttribute("icon", String.class, true, true); 257 infoFactory.addReference("PortalContainerServices", PortalContainerServices.class, "GBean"); 258 infoFactory.setConstructor(new String[]{ 259 "pageTitle", 260 "portletContext", 261 "portletList", 262 "icon", 263 "PortalContainerServices"}); 264 GBEAN_INFO = infoFactory.getBeanInfo(); 265 } 266 267 public static GBeanInfo getGBeanInfo() { 268 return GBEAN_INFO; 269 } 270 271 }