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    }