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