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    
018    package org.apache.geronimo.console.webmanager;
019    
020    import java.io.IOException;
021    import java.net.URI;
022    import java.util.ArrayList;
023    import java.util.HashMap;
024    import java.util.List;
025    
026    import javax.portlet.ActionRequest;
027    import javax.portlet.ActionResponse;
028    import javax.portlet.PortletConfig;
029    import javax.portlet.PortletContext;
030    import javax.portlet.PortletException;
031    import javax.portlet.PortletRequest;
032    import javax.portlet.PortletRequestDispatcher;
033    import javax.portlet.RenderRequest;
034    import javax.portlet.RenderResponse;
035    import javax.portlet.WindowState;
036    
037    import org.apache.commons.logging.Log;
038    import org.apache.commons.logging.LogFactory;
039    import org.apache.geronimo.console.BasePortlet;
040    import org.apache.geronimo.console.util.PortletManager;
041    import org.apache.geronimo.gbean.AbstractName;
042    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
043    import org.apache.geronimo.kernel.proxy.GeronimoManagedBean;
044    import org.apache.geronimo.management.geronimo.KeystoreException;
045    import org.apache.geronimo.management.geronimo.KeystoreInstance;
046    import org.apache.geronimo.management.geronimo.KeystoreManager;
047    import org.apache.geronimo.management.geronimo.NetworkConnector;
048    import org.apache.geronimo.management.geronimo.SecureConnector;
049    import org.apache.geronimo.management.geronimo.WebContainer;
050    import org.apache.geronimo.management.geronimo.WebManager;
051    import org.apache.geronimo.management.geronimo.WebManager.ConnectorAttribute;
052    import org.apache.geronimo.management.geronimo.WebManager.ConnectorType;
053    
054    /**
055     * A portlet that lets you list, add, remove, start, stop, and edit web
056     * connectors (currently, either Tomcat or Jetty).
057     *
058     * @version $Rev: 562021 $ $Date: 2007-08-02 01:51:18 -0400 (Thu, 02 Aug 2007) $
059     */
060    public class ConnectorPortlet extends BasePortlet {
061        private final static Log log = LogFactory.getLog(ConnectorPortlet.class);
062        
063        public static final String PARM_CONTAINER_URI = "containerURI";
064        public static final String PARM_CONNECTOR_URI = "connectorURI";
065        public static final String PARM_MANAGER_URI = "managerURI";
066        public static final String PARM_MODE = "mode";
067        public static final String PARM_CONNECTOR_TYPE = "connectorType";
068        public static final String PARM_CONNECTOR_ATTRIBUTES = "connectorAttributes";
069        public static final String PARM_DISPLAY_NAME = "uniqueName";
070        public static final String PARM_SERVER = "server";
071    
072        private PortletRequestDispatcher normalView;
073        private PortletRequestDispatcher maximizedView;
074        private PortletRequestDispatcher helpView;
075        private PortletRequestDispatcher editConnectorView;
076    
077        public void processAction(ActionRequest actionRequest,
078                                  ActionResponse actionResponse) throws PortletException, IOException {
079            String submit = actionRequest.getParameter("submit");
080            if ("Cancel".equalsIgnoreCase(submit)) {
081                // User clicked on "Cancel" button in add/edit connector page
082                actionResponse.setRenderParameter(PARM_MODE, "list");
083                return;
084            }
085            
086            String mode = actionRequest.getParameter(PARM_MODE);
087            String managerURI = actionRequest.getParameter(PARM_MANAGER_URI);
088            String containerURI = actionRequest.getParameter(PARM_CONTAINER_URI);
089            if(managerURI != null) actionResponse.setRenderParameter(PARM_MANAGER_URI, managerURI);
090            if(containerURI != null) actionResponse.setRenderParameter(PARM_CONTAINER_URI, containerURI);
091            WebContainer webContainer  = null;
092            String server = null;
093            if(containerURI != null) {
094                webContainer = PortletManager.getWebContainer(actionRequest, new AbstractName(URI.create(containerURI)));
095                server = getWebServerType(webContainer.getClass());
096            } else {
097                server = "unknown";
098            }
099            actionResponse.setRenderParameter(PARM_SERVER, server);
100            if(mode.equals("new")) {
101                // User selected to add a new connector, need to show criteria portlet
102                actionResponse.setRenderParameter(PARM_MODE, "new");
103                String connectorType = actionRequest.getParameter(PARM_CONNECTOR_TYPE);
104                actionResponse.setRenderParameter(PARM_CONNECTOR_TYPE, connectorType);
105            } else if(mode.equals("add")) { // User just submitted the form to add a new connector
106                // Create and configure the connector
107                WebManager manager = PortletManager.getWebManager(actionRequest, new AbstractName(URI.create(managerURI)));
108                ConnectorType connectorType = new ConnectorType(actionRequest.getParameter(PARM_CONNECTOR_TYPE));
109                
110                String uniqueName = actionRequest.getParameter(PARM_DISPLAY_NAME);
111                actionResponse.setRenderParameter(PARM_DISPLAY_NAME, uniqueName);
112                // set the connector attributes from the form post
113                List<ConnectorAttribute> connectorAttributes = manager.getConnectorAttributes(connectorType);
114                for (ConnectorAttribute attribute : connectorAttributes) {
115                    String name = attribute.getAttributeName();
116                    String value = actionRequest.getParameter(name);
117                    
118                    // handle booelan type special
119                    if (attribute.getAttributeClass().equals(Boolean.class)) {
120                        // browser sends value of checked checkbox as "on" or "checked"
121                        if ("on".equalsIgnoreCase(value) || "checked".equalsIgnoreCase(value)) {
122                            value=Boolean.toString(true);
123                        } else {
124                            value=Boolean.toString(false);
125                        }
126                    }
127                    // set the string form of the attribute's value as submitted by the browser
128                    if (value == null || value.trim().length()<1) {
129                        // special case for KeystoreManager gbean
130                        if ("trustStore".equals(attribute.getAttributeName())) {
131                            attribute.setValue(null);
132                        }
133                    } else {
134                        attribute.setStringValue(value.trim());
135                    }
136                }
137                // create the connector gbean based on the configuration data
138                AbstractName newConnectorName = manager.getConnectorConfiguration(connectorType, connectorAttributes, webContainer, uniqueName);
139                
140                // set the keystore properties if its a secure connector
141                setKeystoreProperties(actionRequest, newConnectorName);
142                
143                // Start the connector
144                try {
145                    GeronimoManagedBean managedBean = PortletManager.getManagedBean(actionRequest, newConnectorName);
146                    managedBean.startRecursive();
147                } catch (Exception e) {
148                    log.error("Unable to start connector", e); //TODO: get into rendered page
149                }
150                actionResponse.setRenderParameter(PARM_MODE, "list");
151            } else if(mode.equals("save")) { // User just submitted the form to update a connector
152                // Get submitted values
153                //todo: lots of validation
154                String connectorURI = actionRequest.getParameter(PARM_CONNECTOR_URI);
155                // Identify and update the connector
156                AbstractName connectorName = new AbstractName(URI.create(connectorURI));
157                NetworkConnector connector = PortletManager.getNetworkConnector(actionRequest, connectorName);
158                if(connector != null) {
159                    WebManager manager = PortletManager.getWebManager(actionRequest, new AbstractName(URI.create(managerURI)));
160                    ConnectorType connectorType = manager.getConnectorType(connectorName);
161                    
162                    // set the connector attributes from the form post
163                    for (ConnectorAttribute attribute : manager.getConnectorAttributes(connectorType)) {
164                        String name = attribute.getAttributeName();
165                        String value = actionRequest.getParameter(name);
166                        
167                        // handle booelan type special
168                        if (attribute.getAttributeClass().equals(Boolean.class)) {
169                            // browser sends value of checked checkbox as "on" or "checked"
170                            if ("on".equalsIgnoreCase(value) || "checked".equalsIgnoreCase(value)) {
171                                value=Boolean.toString(true);
172                            } else {
173                                value=Boolean.toString(false);
174                            }
175                        }
176                        // set the string form of the attribute's value as submitted by the browser
177                        if (value == null || value.trim().length()<1) {
178                            // special case for KeystoreManager gbean
179                            if ("trustStore".equals(attribute.getAttributeName())) {
180                                setProperty(connector,name,null);
181                            }
182                        } else {
183                            // set the string value on the ConnectorAttribute so 
184                            // it can handle type conversion via getValue()
185                            try {
186                                attribute.setStringValue(value);
187                                setProperty(connector,name,attribute.getValue());
188                            } catch (Exception e) {
189                                log.error("Unable to set property " + attribute.getAttributeName(), e);
190                            }
191                        }
192                    }
193                    
194                    // set the keystore properties if its a secure connector
195                    setKeystoreProperties(actionRequest, connectorName);
196                }
197                actionResponse.setRenderParameter(PARM_MODE, "list");
198            } else if(mode.equals("start")) {
199                String connectorURI = actionRequest.getParameter(PARM_CONNECTOR_URI);
200                // work with the current connector to start it.
201                NetworkConnector connector = PortletManager.getNetworkConnector(actionRequest, new AbstractName(URI.create(connectorURI)));
202                if(connector != null) {
203                    try {
204                        ((GeronimoManagedBean)connector).startRecursive();
205                    } catch (Exception e) {
206                        log.error("Unable to start connector", e); //todo: get into rendered page somehow?
207                    }
208                }
209                else {
210                    log.error("Incorrect connector reference"); //Replace this with correct error processing
211                }
212                actionResponse.setRenderParameter(PARM_CONNECTOR_URI, connectorURI);
213                actionResponse.setRenderParameter(PARM_MODE, "list");
214            } else if(mode.equals("stop")) {
215                String connectorURI = actionRequest.getParameter(PARM_CONNECTOR_URI);
216                // work with the current connector to stop it.
217                NetworkConnector connector = PortletManager.getNetworkConnector(actionRequest, new AbstractName(URI.create(connectorURI)));
218                if(connector != null) {
219                    try {
220                        ((GeronimoManagedBean)connector).stop();
221                    } catch (Exception e) {
222                        log.error("Unable to stop connector", e); //todo: get into rendered page somehow?
223                    }
224                }
225                else {
226                    log.error("Incorrect connector reference"); //Replace this with correct error processing
227                }
228                actionResponse.setRenderParameter(PARM_CONNECTOR_URI, connectorURI);
229                actionResponse.setRenderParameter(PARM_MODE, "list");
230            } else if(mode.equals("edit")) {
231                String connectorURI = actionRequest.getParameter(PARM_CONNECTOR_URI);
232                actionResponse.setRenderParameter(PARM_CONNECTOR_URI, connectorURI);
233                actionResponse.setRenderParameter(PARM_MODE, "edit");
234    
235            } else if(mode.equals("delete")) { // User chose to delete a connector
236                String connectorURI = actionRequest.getParameter(PARM_CONNECTOR_URI);
237                PortletManager.getWebManager(actionRequest, new AbstractName(URI.create(managerURI))).removeConnector(new AbstractName(URI.create(connectorURI)));
238                actionResponse.setRenderParameter(PARM_MODE, "list");
239            }
240        }
241    
242        protected void doView(RenderRequest renderRequest,
243                              RenderResponse renderResponse) throws IOException, PortletException {
244            if (WindowState.MINIMIZED.equals(renderRequest.getWindowState())) {
245                return;
246            }
247            String mode = renderRequest.getParameter(PARM_MODE);
248            if(mode == null || mode.equals("")) {
249                mode = "list";
250            }
251    
252            if(mode.equals("list")) {
253                doList(renderRequest, renderResponse);
254            } else {
255                String managerURI = renderRequest.getParameter(PARM_MANAGER_URI);
256                String containerURI = renderRequest.getParameter(PARM_CONTAINER_URI);
257                if(managerURI != null) renderRequest.setAttribute(PARM_MANAGER_URI, managerURI);
258                if(containerURI != null) renderRequest.setAttribute(PARM_CONTAINER_URI, containerURI);
259    
260                WebContainer container = PortletManager.getWebContainer(renderRequest, new AbstractName(URI.create(containerURI)));
261                String server = getWebServerType(container.getClass());
262                renderRequest.setAttribute(PARM_SERVER, server);
263    
264                if(mode.equals("new")) {
265                    String connectorType = renderRequest.getParameter(PARM_CONNECTOR_TYPE);
266                    WebManager webManager = PortletManager.getWebManager(renderRequest, new AbstractName(URI.create(managerURI)));
267                    ConnectorType type = new ConnectorType(connectorType);
268                    List<ConnectorAttribute> connectorAttributes = webManager.getConnectorAttributes(type);
269                    renderRequest.setAttribute(PARM_CONNECTOR_ATTRIBUTES, connectorAttributes);
270                    renderRequest.setAttribute(PARM_CONNECTOR_TYPE, connectorType);
271                    renderRequest.setAttribute(PARM_MODE, "add");
272                    populateEnumAttributes(renderRequest);
273                    editConnectorView.include(renderRequest, renderResponse);
274                } else if(mode.equals("edit")) {
275                    String connectorURI = renderRequest.getParameter(PARM_CONNECTOR_URI);
276                    NetworkConnector connector = PortletManager.getNetworkConnector(renderRequest, new AbstractName(URI.create(connectorURI)));
277                    if(connector == null) {
278                        doList(renderRequest, renderResponse);
279                    } else {
280                        AbstractName connectorName = new AbstractName(URI.create(connectorURI));
281                        String uniqueName = connectorName.getName().get("name").toString();
282                        renderRequest.setAttribute(PARM_DISPLAY_NAME, uniqueName);
283                        WebManager webManager = PortletManager.getWebManager(renderRequest, new AbstractName(URI.create(managerURI)));
284                        ConnectorType connectorType = webManager.getConnectorType(connectorName);
285                        List<ConnectorAttribute> connectorAttributes = webManager.getConnectorAttributes(connectorType);
286                        
287                        // populate the connector attributes from the connector
288                        for (ConnectorAttribute attribute : connectorAttributes) {
289                            try {
290                                Object value = getProperty(connector, attribute.getAttributeName());
291                                attribute.setValue(value);
292                            } catch (IllegalArgumentException e) {
293                                log.error("Unable to retrieve value of property " + attribute.getAttributeName(), e);
294                            }
295                        }
296                        
297                        renderRequest.setAttribute(PARM_CONNECTOR_ATTRIBUTES, connectorAttributes);
298                        renderRequest.setAttribute(PARM_CONNECTOR_URI, connectorURI);
299                        // populate any enum type values.  the browser will render them in a
300                        // <SELECT> input for the attribute
301                        populateEnumAttributes(renderRequest);
302                        
303                        renderRequest.setAttribute(PARM_MODE, "save");
304                        editConnectorView.include(renderRequest, renderResponse);
305                    }
306                }
307            }
308    
309        }
310    
311        private void doList(RenderRequest renderRequest, RenderResponse renderResponse) throws PortletException, IOException {
312            WebManager[] managers = PortletManager.getWebManagers(renderRequest);
313            List<ContainerInfo> all = new ArrayList<ContainerInfo>();
314            for (int i = 0; i < managers.length; i++) {
315                WebManager manager = managers[i];
316                AbstractName webManagerName = PortletManager.getNameFor(renderRequest, manager);
317                
318                WebContainer[] containers = (WebContainer[]) manager.getContainers();
319                for (int j = 0; j < containers.length; j++) {
320                    List<ConnectorInfo> beans = new ArrayList<ConnectorInfo>();
321                    WebContainer container = containers[j];
322                    AbstractName containerName = PortletManager.getNameFor(renderRequest, container);
323                    String id;
324                    if(containers.length == 1) {
325                        id = manager.getProductName();
326                    } else {
327                        id = manager.getProductName() + " (" + containerName.getName().get(NameFactory.J2EE_NAME) + ")";
328                    }
329                    ContainerInfo result = new ContainerInfo(id, webManagerName.toString(), containerName.toString());
330    
331                    for (NetworkConnector connector : manager.getConnectorsForContainer(container)) {
332                        ConnectorInfo info = new ConnectorInfo();
333                        AbstractName connectorName = PortletManager.getNameFor(renderRequest, connector);
334                        info.setConnectorURI(connectorName.toString());
335                        info.setDescription(PortletManager.getGBeanDescription(renderRequest, connectorName));
336                        info.setUniqueName((String)connectorName.getName().get(NameFactory.J2EE_NAME));
337                        info.setState(((GeronimoManagedBean)connector).getState());
338                        info.setPort(connector.getPort());
339                        try {
340                            info.setProtocol(connector.getProtocol());
341                        } catch (IllegalStateException e) {
342                            info.setProtocol("unknown");
343                        }
344                        beans.add(info);
345                    }
346                    result.setConnectors(beans);
347                    result.setConnectorTypes(manager.getConnectorTypes());
348                    all.add(result);
349                }
350            }
351            renderRequest.setAttribute("containers", all);
352            renderRequest.setAttribute("serverPort", new Integer(renderRequest.getServerPort()));
353    
354            if (WindowState.NORMAL.equals(renderRequest.getWindowState())) {
355                normalView.include(renderRequest, renderResponse);
356            } else {
357                maximizedView.include(renderRequest, renderResponse);
358            }
359        }
360    
361        public final static class ContainerInfo {
362            private String name;
363            private String managerURI;
364            private String containerURI;
365            private List connectorTypes;
366            private List connectors;
367    
368            public ContainerInfo(String name, String managerURI, String containerURI) {
369                this.name = name;
370                this.managerURI = managerURI;
371                this.containerURI = containerURI;
372            }
373    
374            public String getName() {
375                return name;
376            }
377    
378            public List getConnectorTypes() {
379                return connectorTypes;
380            }
381    
382            public void setConnectorTypes(List connectorTypes) {
383                this.connectorTypes = connectorTypes;
384            }
385    
386            public List getConnectors() {
387                return connectors;
388            }
389    
390            public void setConnectors(List connectors) {
391                this.connectors = connectors;
392            }
393    
394            public String getManagerURI() {
395                return managerURI;
396            }
397    
398            public String getContainerURI() {
399                return containerURI;
400            }
401        }
402    
403        protected void doHelp(RenderRequest renderRequest,
404                              RenderResponse renderResponse) throws PortletException, IOException {
405            helpView.include(renderRequest, renderResponse);
406        }
407    
408        public void init(PortletConfig portletConfig) throws PortletException {
409            super.init(portletConfig);
410            PortletContext pc = portletConfig.getPortletContext();
411            normalView = pc.getRequestDispatcher("/WEB-INF/view/webmanager/connector/normal.jsp");
412            maximizedView = pc.getRequestDispatcher("/WEB-INF/view/webmanager/connector/maximized.jsp");
413            helpView = pc.getRequestDispatcher("/WEB-INF/view/webmanager/connector/help.jsp");
414            editConnectorView = pc.getRequestDispatcher("/WEB-INF/view/webmanager/connector/editConnector.jsp");
415        }
416    
417        public void destroy() {
418            normalView = null;
419            maximizedView = null;
420            helpView = null;
421            editConnectorView = null;
422            super.destroy();
423        }
424    
425        public static boolean isValid(String s) {
426            return s != null && !s.equals("");
427        }
428        
429        // stash any 'enum' type values for attributes.  right now this is
430        // hardcoded, need to promote these to the ConnectorAttribute apis
431        private void populateEnumAttributes(PortletRequest request) {
432            HashMap<String,String[]> enumValues = new HashMap<String,String[]>();
433            
434            // provide the two possible values for secure protocol - TLS and SSL
435            enumValues.put("secureProtocol", new String[] { "TLS", "SSL" }); //jetty
436            enumValues.put("sslProtocol", new String[] { "TLS", "SSL" }); //tomcat
437            
438            // keystore and truststore types for tomcat
439            enumValues.put("keystoreType", new String[] { "JKS", "PKCS12" });
440            enumValues.put("truststoreType", new String[] { "JKS", "PKCS12" });
441    
442            // provide the three possible values for secure algorithm - Default, SunX509, and IbmX509 
443            enumValues.put("algorithm", new String[] { "Default", "SunX509", "IbmX509" });
444            
445            // provide the possible values for the keystore name
446            KeystoreManager mgr = PortletManager.getCurrentServer(request).getKeystoreManager();
447            KeystoreInstance[] stores = mgr.getUnlockedKeyStores();
448            String[] storeNames = new String[stores.length];
449            for (int i = 0; i < storeNames.length; i++) {
450                storeNames[i] = stores[i].getKeystoreName();
451            }
452            enumValues.put("keyStore", storeNames);
453            
454            // provide the possible values for the trust store name
455            KeystoreInstance[] trusts = mgr.getUnlockedTrustStores();
456            String[] trustNames = new String[trusts.length];
457            for (int i = 0; i < trustNames.length; i++) {
458                trustNames[i] = trusts[i].getKeystoreName();
459            }
460            enumValues.put("trustStore", trustNames);
461            
462            request.setAttribute("geronimoConsoleEnumValues", enumValues);
463        }
464        
465        // get the special keystore properties from the request and set them on the connector
466        // TODO: need a more generic way to handle this
467        private void setKeystoreProperties(PortletRequest request, AbstractName connectorName) throws PortletException {
468            String containerURI = request.getParameter(PARM_CONTAINER_URI);
469            WebContainer container = PortletManager.getWebContainer(request, new AbstractName(URI.create(containerURI)));
470            String server = getWebServerType(container.getClass());
471            NetworkConnector connector = PortletManager.getNetworkConnector(request, connectorName);
472    
473            // return if not a secure connector
474            if (!(connector instanceof SecureConnector)) {
475                return;
476            }
477            
478            // right now only jetty supports the KeystoreManager
479            if (server.equals(WEB_SERVER_JETTY)) {
480                String keyStore = request.getParameter("keyStore");
481                
482                // get the unlocked keystore object from the keystore managaer
483                // gbean and set its keyalias directly on the connector 
484                try {
485                    KeystoreInstance[] keystores = PortletManager.getCurrentServer(request)
486                            .getKeystoreManager().getKeystores();
487    
488                    String[] keys = null;
489                    for (int i = 0; i < keystores.length; i++) {
490                        KeystoreInstance keystore = keystores[i];
491                        if (keystore.getKeystoreName().equals(keyStore)) {
492                            keys = keystore.getUnlockedKeys(null);
493                        }
494                    }
495                    if (keys != null && keys.length == 1) {
496                        setProperty(connector, "keyAlias", keys[0]);
497                    } else {
498                        throw new PortletException("Cannot handle keystores with anything but 1 unlocked private key");
499                    }
500                } catch (KeystoreException e) {
501                    throw new PortletException(e);
502                }
503            }
504        }
505    }