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.console.databasemanager.wizard;
018
019 import java.io.BufferedOutputStream;
020 import java.io.ByteArrayOutputStream;
021 import java.io.File;
022 import java.io.FileOutputStream;
023 import java.io.FileReader;
024 import java.io.IOException;
025 import java.io.PrintWriter;
026 import java.io.Serializable;
027 import java.io.StringReader;
028 import java.io.StringWriter;
029 import java.io.UnsupportedEncodingException;
030 import java.net.MalformedURLException;
031 import java.net.URI;
032 import java.net.URL;
033 import java.net.URLClassLoader;
034 import java.net.URLDecoder;
035 import java.net.URLEncoder;
036 import java.sql.Connection;
037 import java.sql.DatabaseMetaData;
038 import java.sql.Driver;
039 import java.sql.SQLException;
040 import java.util.ArrayList;
041 import java.util.Arrays;
042 import java.util.Collections;
043 import java.util.Comparator;
044 import java.util.HashMap;
045 import java.util.Iterator;
046 import java.util.List;
047 import java.util.Map;
048 import java.util.Properties;
049 import java.util.SortedSet;
050 import javax.enterprise.deploy.model.DDBean;
051 import javax.enterprise.deploy.model.DDBeanRoot;
052 import javax.enterprise.deploy.shared.ModuleType;
053 import javax.enterprise.deploy.spi.DeploymentConfiguration;
054 import javax.enterprise.deploy.spi.DeploymentManager;
055 import javax.enterprise.deploy.spi.Target;
056 import javax.enterprise.deploy.spi.TargetModuleID;
057 import javax.enterprise.deploy.spi.status.ProgressObject;
058 import javax.portlet.ActionRequest;
059 import javax.portlet.ActionResponse;
060 import javax.portlet.PortletConfig;
061 import javax.portlet.PortletException;
062 import javax.portlet.PortletRequest;
063 import javax.portlet.PortletRequestDispatcher;
064 import javax.portlet.PortletSession;
065 import javax.portlet.RenderRequest;
066 import javax.portlet.RenderResponse;
067 import javax.portlet.WindowState;
068 import javax.xml.parsers.DocumentBuilder;
069 import javax.xml.parsers.DocumentBuilderFactory;
070 import org.apache.commons.fileupload.FileItem;
071 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
072 import org.apache.commons.fileupload.portlet.PortletFileUpload;
073 import org.apache.commons.logging.Log;
074 import org.apache.commons.logging.LogFactory;
075 import org.apache.geronimo.connector.deployment.jsr88.ConfigPropertySetting;
076 import org.apache.geronimo.connector.deployment.jsr88.ConnectionDefinition;
077 import org.apache.geronimo.connector.deployment.jsr88.ConnectionDefinitionInstance;
078 import org.apache.geronimo.connector.deployment.jsr88.ConnectionManager;
079 import org.apache.geronimo.connector.deployment.jsr88.Connector15DCBRoot;
080 import org.apache.geronimo.connector.deployment.jsr88.ConnectorDCB;
081 import org.apache.geronimo.connector.deployment.jsr88.ResourceAdapter;
082 import org.apache.geronimo.connector.deployment.jsr88.SinglePool;
083 import org.apache.geronimo.connector.outbound.PoolingAttributes;
084 import org.apache.geronimo.console.BasePortlet;
085 import org.apache.geronimo.console.ajax.ProgressInfo;
086 import org.apache.geronimo.console.util.PortletManager;
087 import org.apache.geronimo.converter.DatabaseConversionStatus;
088 import org.apache.geronimo.converter.JDBCPool;
089 import org.apache.geronimo.converter.bea.WebLogic81DatabaseConverter;
090 import org.apache.geronimo.converter.jboss.JBoss4DatabaseConverter;
091 import org.apache.geronimo.deployment.service.jsr88.EnvironmentData;
092 import org.apache.geronimo.deployment.tools.loader.ConnectorDeployable;
093 import org.apache.geronimo.gbean.AbstractName;
094 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
095 import org.apache.geronimo.kernel.management.State;
096 import org.apache.geronimo.kernel.proxy.GeronimoManagedBean;
097 import org.apache.geronimo.kernel.repository.Artifact;
098 import org.apache.geronimo.kernel.repository.FileWriteMonitor;
099 import org.apache.geronimo.kernel.repository.ListableRepository;
100 import org.apache.geronimo.kernel.repository.WriteableRepository;
101 import org.apache.geronimo.kernel.util.XmlUtil;
102 import org.apache.geronimo.management.geronimo.JCAManagedConnectionFactory;
103 import org.apache.geronimo.management.geronimo.ResourceAdapterModule;
104 import org.w3c.dom.Document;
105 import org.w3c.dom.Element;
106 import org.w3c.dom.Node;
107 import org.w3c.dom.NodeList;
108 import org.xml.sax.InputSource;
109
110 /**
111 * A portlet that lets you configure and deploy JDBC connection pools.
112 *
113 * @version $Rev: 550963 $ $Date: 2007-06-26 17:38:01 -0400 (Tue, 26 Jun 2007) $
114 */
115 public class DatabasePoolPortlet extends BasePortlet {
116 private final static Log log = LogFactory.getLog(DatabasePoolPortlet.class);
117 private final static String[] SKIP_ENTRIES_WITH = new String[]{"geronimo", "tomcat", "tranql", "commons", "directory", "activemq"};
118 private final static String DRIVER_SESSION_KEY = "org.apache.geronimo.console.dbpool.Drivers";
119 private final static String CONFIG_SESSION_KEY = "org.apache.geronimo.console.dbpool.ConfigParam";
120 private final static String DRIVER_INFO_URL = "http://geronimo.apache.org/driver-downloads.properties";
121 private static final String LIST_VIEW = "/WEB-INF/view/dbwizard/list.jsp";
122 private static final String EDIT_VIEW = "/WEB-INF/view/dbwizard/edit.jsp";
123 private static final String SELECT_RDBMS_VIEW = "/WEB-INF/view/dbwizard/selectDatabase.jsp";
124 private static final String BASIC_PARAMS_VIEW = "/WEB-INF/view/dbwizard/basicParams.jsp";
125 private static final String CONFIRM_URL_VIEW = "/WEB-INF/view/dbwizard/confirmURL.jsp";
126 private static final String TEST_CONNECTION_VIEW = "/WEB-INF/view/dbwizard/testConnection.jsp";
127 private static final String DOWNLOAD_VIEW = "/WEB-INF/view/dbwizard/selectDownload.jsp";
128 private static final String DOWNLOAD_STATUS_VIEW = "/WEB-INF/view/dbwizard/downloadStatus.jsp";
129 private static final String SHOW_PLAN_VIEW = "/WEB-INF/view/dbwizard/showPlan.jsp";
130 private static final String IMPORT_UPLOAD_VIEW = "/WEB-INF/view/dbwizard/importUpload.jsp";
131 private static final String IMPORT_STATUS_VIEW = "/WEB-INF/view/dbwizard/importStatus.jsp";
132 private static final String USAGE_VIEW = "/WEB-INF/view/dbwizard/usage.jsp";
133 private static final String LIST_MODE = "list";
134 private static final String EDIT_MODE = "edit";
135 private static final String SELECT_RDBMS_MODE = "rdbms";
136 private static final String BASIC_PARAMS_MODE = "params";
137 private static final String CONFIRM_URL_MODE = "url";
138 private static final String TEST_CONNECTION_MODE = "test";
139 private static final String SHOW_PLAN_MODE = "plan";
140 private static final String DOWNLOAD_MODE = "download";
141 private static final String DOWNLOAD_STATUS_MODE = "downloadStatus";
142 private static final String EDIT_EXISTING_MODE = "editExisting";
143 private static final String DELETE_MODE = "delete";
144 private static final String SAVE_MODE = "save";
145 private static final String IMPORT_START_MODE = "startImport";
146 private static final String IMPORT_UPLOAD_MODE = "importUpload";
147 private static final String IMPORT_STATUS_MODE = "importStatus";
148 private static final String IMPORT_COMPLETE_MODE = "importComplete";
149 private static final String WEBLOGIC_IMPORT_MODE = "weblogicImport";
150 private static final String USAGE_MODE = "usage";
151 private static final String IMPORT_EDIT_MODE = "importEdit";
152 private static final String MODE_KEY = "mode";
153
154 private PortletRequestDispatcher listView;
155 private PortletRequestDispatcher editView;
156 private PortletRequestDispatcher selectRDBMSView;
157 private PortletRequestDispatcher basicParamsView;
158 private PortletRequestDispatcher confirmURLView;
159 private PortletRequestDispatcher testConnectionView;
160 private PortletRequestDispatcher downloadView;
161 private PortletRequestDispatcher downloadStatusView;
162 private PortletRequestDispatcher planView;
163 private PortletRequestDispatcher importUploadView;
164 private PortletRequestDispatcher importStatusView;
165 private PortletRequestDispatcher usageView;
166
167 public void init(PortletConfig portletConfig) throws PortletException {
168 super.init(portletConfig);
169 listView = portletConfig.getPortletContext().getRequestDispatcher(LIST_VIEW);
170 editView = portletConfig.getPortletContext().getRequestDispatcher(EDIT_VIEW);
171 selectRDBMSView = portletConfig.getPortletContext().getRequestDispatcher(SELECT_RDBMS_VIEW);
172 basicParamsView = portletConfig.getPortletContext().getRequestDispatcher(BASIC_PARAMS_VIEW);
173 confirmURLView = portletConfig.getPortletContext().getRequestDispatcher(CONFIRM_URL_VIEW);
174 testConnectionView = portletConfig.getPortletContext().getRequestDispatcher(TEST_CONNECTION_VIEW);
175 downloadView = portletConfig.getPortletContext().getRequestDispatcher(DOWNLOAD_VIEW);
176 downloadStatusView = portletConfig.getPortletContext().getRequestDispatcher(DOWNLOAD_STATUS_VIEW);
177 planView = portletConfig.getPortletContext().getRequestDispatcher(SHOW_PLAN_VIEW);
178 importUploadView = portletConfig.getPortletContext().getRequestDispatcher(IMPORT_UPLOAD_VIEW);
179 importStatusView = portletConfig.getPortletContext().getRequestDispatcher(IMPORT_STATUS_VIEW);
180 usageView = portletConfig.getPortletContext().getRequestDispatcher(USAGE_VIEW);
181 }
182
183 public void destroy() {
184 listView = null;
185 editView = null;
186 selectRDBMSView = null;
187 basicParamsView = null;
188 confirmURLView = null;
189 testConnectionView = null;
190 downloadView = null;
191 downloadStatusView = null;
192 planView = null;
193 importUploadView = null;
194 importStatusView = null;
195 usageView = null;
196 super.destroy();
197 }
198
199 public DriverDownloader.DriverInfo[] getDriverInfo(PortletRequest request) {
200 PortletSession session = request.getPortletSession(true);
201 DriverDownloader.DriverInfo[] results = (DriverDownloader.DriverInfo[]) session.getAttribute(DRIVER_SESSION_KEY, PortletSession.APPLICATION_SCOPE);
202 if(results == null) {
203 DriverDownloader downloader = new DriverDownloader();
204 try {
205 results = downloader.loadDriverInfo(new URL(DRIVER_INFO_URL));
206 session.setAttribute(DRIVER_SESSION_KEY, results, PortletSession.APPLICATION_SCOPE);
207 } catch (MalformedURLException e) {
208 log.error("Unable to download driver data", e);
209 results = new DriverDownloader.DriverInfo[0];
210 }
211 }
212 return results;
213 }
214
215 /**
216 * Loads data about a resource adapter. Depending on what we already have, may load
217 * the name and description, but always loads the config property descriptions.
218 * @param request Pass it or die
219 * @param rarPath If we're creating a new RA, the path to identify it
220 * @param displayName If we're editing an existing RA, its name
221 * @param adapterAbstractName If we're editing an existing RA, its AbstractName
222 */
223 public ResourceAdapterParams getRARConfiguration(PortletRequest request, String rarPath, String displayName, String adapterAbstractName) {
224 PortletSession session = request.getPortletSession(true);
225 if(rarPath != null && !rarPath.equals("")) {
226 ResourceAdapterParams results = (ResourceAdapterParams) session.getAttribute(CONFIG_SESSION_KEY+"-"+rarPath, PortletSession.APPLICATION_SCOPE);
227 if(results == null) {
228 results = loadConfigPropertiesByPath(request, rarPath);
229 session.setAttribute(CONFIG_SESSION_KEY+"-"+rarPath, results, PortletSession.APPLICATION_SCOPE);
230 session.setAttribute(CONFIG_SESSION_KEY+"-"+results.displayName, results, PortletSession.APPLICATION_SCOPE);
231 }
232 return results;
233 } else if(displayName != null && !displayName.equals("") && adapterAbstractName != null && !adapterAbstractName.equals("")) {
234 ResourceAdapterParams results = (ResourceAdapterParams) session.getAttribute(CONFIG_SESSION_KEY+"-"+displayName, PortletSession.APPLICATION_SCOPE);
235 if(results == null) {
236 results = loadConfigPropertiesByAbstractName(request, adapterAbstractName);
237 session.setAttribute(CONFIG_SESSION_KEY+"-"+displayName, results, PortletSession.APPLICATION_SCOPE);
238 }
239 return results;
240 } else {
241 throw new IllegalArgumentException();
242 }
243 }
244
245 public void processAction(ActionRequest actionRequest,
246 ActionResponse actionResponse) throws PortletException, IOException {
247 String mode = actionRequest.getParameter(MODE_KEY);
248 if(mode.equals(IMPORT_UPLOAD_MODE)) {
249 processImportUpload(actionRequest, actionResponse);
250 actionResponse.setRenderParameter(MODE_KEY, IMPORT_STATUS_MODE);
251 return;
252 }
253 PoolData data = new PoolData();
254 data.load(actionRequest);
255 if(mode.equals("process-"+SELECT_RDBMS_MODE)) {
256 DatabaseDriver info = null;
257 info = getDatabaseInfo(actionRequest, data);
258 if(info != null) {
259 data.rarPath = info.getRAR().toString();
260 if(info.isXA()) {
261 data.adapterDisplayName="Unknown"; // will pick these up when we process the RA type in the render request
262 data.adapterDescription="Unknown";
263 actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
264 } else {
265 if(data.getDbtype().equals("Other")) {
266 actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
267 } else {
268 data.driverClass = info.getDriverClassName();
269 data.urlPrototype = info.getURLPrototype();
270 actionResponse.setRenderParameter(MODE_KEY, BASIC_PARAMS_MODE);
271 }
272 }
273 } else {
274 actionResponse.setRenderParameter(MODE_KEY, SELECT_RDBMS_MODE);
275 }
276 } else if(mode.equals("process-"+DOWNLOAD_MODE)) {
277 String name = actionRequest.getParameter("driverName");
278 DriverDownloader.DriverInfo[] drivers = getDriverInfo(actionRequest);
279 DriverDownloader.DriverInfo found = null;
280 for (int i = 0; i < drivers.length; i++) {
281 DriverDownloader.DriverInfo driver = drivers[i];
282 if(driver.getName().equals(name)) {
283 found = driver;
284 break;
285 }
286 }
287 if(found != null) {
288 data.jars = new String[] {found.getRepositoryURI()};
289 WriteableRepository repo = PortletManager.getCurrentServer(actionRequest).getWritableRepositories()[0];
290 final PortletSession session = actionRequest.getPortletSession();
291 ProgressInfo progressInfo = new ProgressInfo();
292 progressInfo.setMainMessage("Downloading " + found.getName());
293 session.setAttribute(ProgressInfo.PROGRESS_INFO_KEY, progressInfo, PortletSession.APPLICATION_SCOPE);
294 // Start the download monitoring
295 new Thread(new Downloader(found, progressInfo, repo)).start();
296 actionResponse.setRenderParameter(MODE_KEY, DOWNLOAD_STATUS_MODE);
297 } else {
298 actionResponse.setRenderParameter(MODE_KEY, DOWNLOAD_MODE);
299 }
300 } else if(mode.equals("process-"+DOWNLOAD_STATUS_MODE)) {
301 if(data.getDbtype() == null || data.getDbtype().equals("Other")) {
302 actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
303 } else {
304 actionResponse.setRenderParameter(MODE_KEY, BASIC_PARAMS_MODE);
305 }
306 } else if(mode.equals("process-"+BASIC_PARAMS_MODE)) {
307 DatabaseDriver info = null;
308 info = getDatabaseInfo(actionRequest, data);
309 if(info != null) {
310 data.url = populateURL(info.getURLPrototype(), info.getURLParameters(), data.getUrlProperties());
311 }
312 if(attemptDriverLoad(actionRequest, data) != null) {
313 actionResponse.setRenderParameter(MODE_KEY, CONFIRM_URL_MODE);
314 } else {
315 actionResponse.setRenderParameter("driverError", "Unable to load driver "+data.driverClass);
316 actionResponse.setRenderParameter(MODE_KEY, BASIC_PARAMS_MODE);
317 }
318 } else if(mode.equals("process-"+CONFIRM_URL_MODE)) {
319 String test = actionRequest.getParameter("test");
320 if(test == null || test.equals("true")) {
321 String result = null;
322 String stack = null;
323 try {
324 result = attemptConnect(actionRequest, data);
325 } catch (Exception e) {
326 StringWriter writer = new StringWriter();
327 PrintWriter temp = new PrintWriter(writer);
328 e.printStackTrace(temp);
329 temp.flush();
330 temp.close();
331 stack = writer.getBuffer().toString();
332 }
333 if(result != null) actionResponse.setRenderParameter("connectResult", result);
334 actionRequest.getPortletSession(true).setAttribute("connectError", stack);
335 actionResponse.setRenderParameter(MODE_KEY, TEST_CONNECTION_MODE);
336 } else {
337 save(actionRequest, actionResponse, data, false);
338 }
339 } else if(mode.equals(SAVE_MODE)) {
340 save(actionRequest, actionResponse, data, false);
341 } else if(mode.equals(SHOW_PLAN_MODE)) {
342 String plan = save(actionRequest, actionResponse, data, true);
343 actionRequest.getPortletSession(true).setAttribute("deploymentPlan", plan);
344 actionResponse.setRenderParameter(MODE_KEY, SHOW_PLAN_MODE);
345 } else if(mode.equals(EDIT_EXISTING_MODE)) {
346 final String name = actionRequest.getParameter("adapterAbstractName");
347 loadConnectionFactory(actionRequest, name, data.getAbstractName(), data);
348 actionResponse.setRenderParameter("adapterAbstractName", name);
349 actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
350 } else if(mode.equals(SELECT_RDBMS_MODE)) {
351 if(data.getAdapterDisplayName() == null) { // Set a default for a new pool
352 data.adapterDisplayName = "TranQL Generic JDBC Resource Adapter";
353 }
354 actionResponse.setRenderParameter(MODE_KEY, mode);
355 } else if(mode.equals(WEBLOGIC_IMPORT_MODE)) {
356 String domainDir = actionRequest.getParameter("weblogicDomainDir");
357 String libDir = actionRequest.getParameter("weblogicLibDir");
358 try {
359 DatabaseConversionStatus status = WebLogic81DatabaseConverter.convert(libDir, domainDir);
360 actionRequest.getPortletSession(true).setAttribute("ImportStatus", new ImportStatus(status));
361 actionResponse.setRenderParameter(MODE_KEY, IMPORT_STATUS_MODE);
362 } catch (Exception e) {
363 log.error("Unable to import", e);
364 actionResponse.setRenderParameter("from", actionRequest.getParameter("from"));
365 actionResponse.setRenderParameter(MODE_KEY, IMPORT_START_MODE);
366 }
367 } else if(mode.equals(IMPORT_START_MODE)) {
368 actionResponse.setRenderParameter("from", actionRequest.getParameter("from"));
369 actionResponse.setRenderParameter(MODE_KEY, mode);
370 } else if(mode.equals(IMPORT_EDIT_MODE)) {
371 ImportStatus status = getImportStatus(actionRequest);
372 int index = Integer.parseInt(actionRequest.getParameter("importIndex"));
373 status.setCurrentPoolIndex(index);
374 loadImportedData(actionRequest, data, status.getCurrentPool());
375 actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
376 } else if(mode.equals(IMPORT_COMPLETE_MODE)) {
377 ImportStatus status = getImportStatus(actionRequest);
378 log.warn("Import Results:"); //todo: create a screen for this
379 log.warn(" "+status.getSkippedCount()+" ignored");
380 log.warn(" "+status.getStartedCount()+" reviewed but not deployed");
381 log.warn(" "+status.getPendingCount()+" not reviewed");
382 log.warn(" "+status.getFinishedCount()+" deployed");
383 actionRequest.getPortletSession().removeAttribute("ImportStatus");
384 } else if(mode.equals(DELETE_MODE)) {
385 String name = actionRequest.getParameter("adapterAbstractName");
386 loadConnectionFactory(actionRequest, name, data.getAbstractName(), data);
387 delete(actionRequest, actionResponse, data);
388 } else {
389 actionResponse.setRenderParameter(MODE_KEY, mode);
390 }
391 data.store(actionResponse);
392 }
393
394 private static class Downloader implements Runnable {
395 private WriteableRepository repo;
396 private DriverDownloader.DriverInfo driver;
397 private ProgressInfo progressInfo;
398
399 public Downloader(DriverDownloader.DriverInfo driver, ProgressInfo progressInfo, WriteableRepository repo) {
400 this.driver = driver;
401 this.progressInfo = progressInfo;
402 this.repo = repo;
403 }
404
405 public void run() {
406 DriverDownloader downloader = new DriverDownloader();
407 try {
408 downloader.loadDriver(repo, driver, new FileWriteMonitor() {
409 private int fileSize;
410
411 public void writeStarted(String fileDescription, int fileSize) {
412 this.fileSize = fileSize;
413 log.info("Downloading "+fileDescription);
414 }
415
416 public void writeProgress(int bytes) {
417 int kbDownloaded = (int)Math.floor(bytes/1024);
418 if (fileSize > 0) {
419 int percent = (bytes*100)/fileSize;
420 progressInfo.setProgressPercent(percent);
421 progressInfo.setSubMessage(kbDownloaded + " / " + fileSize/1024 + " Kb downloaded");
422 } else {
423 progressInfo.setSubMessage(kbDownloaded + " Kb downloaded");
424 }
425 }
426
427 public void writeComplete(int bytes) {
428 log.info("Finished downloading "+bytes+" b");
429 }
430 });
431 } catch (IOException e) {
432 log.error("Unable to download database driver", e);
433 } finally {
434 progressInfo.setFinished(true);
435 }
436 }
437 }
438
439 private void loadImportedData(PortletRequest request, PoolData data, ImportStatus.PoolProgress progress) throws PortletException { if(!progress.getType().equals(ImportStatus.PoolProgress.TYPE_XA)) {
440 JDBCPool pool = (JDBCPool) progress.getPool();
441 data.dbtype = "Other";
442 data.adapterDisplayName = "TranQL Generic JDBC Resource Adapter";
443 data.blockingTimeout = getImportString(pool.getBlockingTimeoutMillis());
444 data.driverClass = pool.getDriverClass();
445 data.idleTimeout = pool.getIdleTimeoutMillis() == null ? null : Integer.toString(pool.getIdleTimeoutMillis().intValue() / (60 * 1000));
446 data.maxSize = getImportString(pool.getMaxSize());
447 data.minSize = getImportString(pool.getMinSize());
448 data.name = pool.getName();
449 data.password = pool.getPassword();
450 data.url = pool.getJdbcURL();
451 data.user = pool.getUsername();
452 if(pool.getDriverClass() != null) {
453 DatabaseDriver info = getDatabaseInfoFromDriver(request, data);
454 if(info != null) {
455 data.rarPath = info.getRAR().toString();
456 data.urlPrototype = info.getURLPrototype();
457 } else {
458 throw new PortletException("Don't recognize database driver "+data.driverClass+"!");
459 }
460 }
461 } else {
462 //todo: handle XA
463 }
464 }
465
466 private static String getImportString(Integer value) {
467 return value == null ? null : value.toString();
468 }
469
470 private boolean processImportUpload(ActionRequest request, ActionResponse response) throws PortletException {
471 String type = request.getParameter("importSource");
472 response.setRenderParameter("importSource", type);
473 if (!PortletFileUpload.isMultipartContent(request)) {
474 throw new PortletException("Expected file upload");
475 }
476
477 PortletFileUpload uploader = new PortletFileUpload(new DiskFileItemFactory());
478 try {
479 List items = uploader.parseRequest(request);
480 for (Iterator i = items.iterator(); i.hasNext();) {
481 FileItem item = (FileItem) i.next();
482 if (!item.isFormField()) {
483 File file = File.createTempFile("geronimo-import", "");
484 file.deleteOnExit();
485 log.debug("Writing database pool import file to "+file.getAbsolutePath());
486 item.write(file);
487 DatabaseConversionStatus status = processImport(file, type);
488 request.getPortletSession(true).setAttribute("ImportStatus", new ImportStatus(status));
489 return true;
490 } else {
491 throw new PortletException("Not expecting any form fields");
492 }
493 }
494 } catch(PortletException e) {
495 throw e;
496 } catch(Exception e) {
497 throw new PortletException(e);
498 }
499 return false;
500 }
501
502 private DatabaseConversionStatus processImport(File importFile, String type) throws PortletException, IOException {
503 if(type.equals("JBoss 4")) {
504 return JBoss4DatabaseConverter.convert(new FileReader(importFile));
505 } else if(type.equals("WebLogic 8.1")) {
506 return WebLogic81DatabaseConverter.convert(new FileReader(importFile));
507 } else {
508 throw new PortletException("Unknown import type '"+type+"'");
509 }
510 }
511
512 private ResourceAdapterParams loadConfigPropertiesByPath(PortletRequest request, String rarPath) {
513 DeploymentManager mgr = PortletManager.getDeploymentManager(request);
514 try {
515 URL url = getRAR(request, rarPath).toURL();
516 ConnectorDeployable deployable = new ConnectorDeployable(url);
517 final DDBeanRoot ddBeanRoot = deployable.getDDBeanRoot();
518 String adapterName = null, adapterDesc = null;
519 String[] test = ddBeanRoot.getText("connector/display-name");
520 if(test != null && test.length > 0) {
521 adapterName = test[0];
522 }
523 test = ddBeanRoot.getText("connector/description");
524 if(test != null && test.length > 0) {
525 adapterDesc = test[0];
526 }
527 DDBean[] definitions = ddBeanRoot.getChildBean("connector/resourceadapter/outbound-resourceadapter/connection-definition");
528 List configs = new ArrayList();
529 if(definitions != null) {
530 for (int i = 0; i < definitions.length; i++) {
531 DDBean definition = definitions[i];
532 String iface = definition.getText("connectionfactory-interface")[0];
533 if(iface.equals("javax.sql.DataSource")) {
534 DDBean[] beans = definition.getChildBean("config-property");
535 for (int j = 0; j < beans.length; j++) {
536 DDBean bean = beans[j];
537 String name = bean.getText("config-property-name")[0].trim();
538 String type = bean.getText("config-property-type")[0].trim();
539 test = bean.getText("config-property-value");
540 String value = test == null || test.length == 0 ? null : test[0].trim();
541 test = bean.getText("description");
542 String desc = test == null || test.length == 0 ? null : test[0].trim();
543 configs.add(new ConfigParam(name, type, desc, value));
544 }
545 }
546 }
547 }
548 return new ResourceAdapterParams(adapterName, adapterDesc, (ConfigParam[]) configs.toArray(new ConfigParam[configs.size()]));
549 } catch (Exception e) {
550 log.error("Unable to read configuration properties", e);
551 return null;
552 } finally {
553 if(mgr != null) mgr.release();
554 }
555 }
556
557 private ResourceAdapterParams loadConfigPropertiesByAbstractName(PortletRequest request, String abstractName) {
558 ResourceAdapterModule module = (ResourceAdapterModule) PortletManager.getManagedBean(request, new AbstractName(URI.create(abstractName)));
559 String dd = module.getDeploymentDescriptor();
560 DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
561 factory.setValidating(false);
562 factory.setNamespaceAware(true);
563 try {
564 DocumentBuilder builder = factory.newDocumentBuilder();
565 final StringReader reader = new StringReader(dd);
566 Document doc = builder.parse(new InputSource(reader));
567 reader.close();
568 Element elem = doc.getDocumentElement(); // connector
569 String displayName = getFirstText(elem.getElementsByTagName("display-name"));
570 String description = getFirstText(elem.getElementsByTagName("description"));
571 elem = (Element) elem.getElementsByTagName("resourceadapter").item(0);
572 elem = (Element) elem.getElementsByTagName("outbound-resourceadapter").item(0);
573 NodeList defs = elem.getElementsByTagName("connection-definition");
574 List all = new ArrayList();
575 for(int i=0; i<defs.getLength(); i++) {
576 final Element def = (Element)defs.item(i);
577 String iface = getFirstText(def.getElementsByTagName("connectionfactory-interface")).trim();
578 if(iface.equals("javax.sql.DataSource")) {
579 NodeList configs = def.getElementsByTagName("config-property");
580 for(int j=0; j<configs.getLength(); j++) {
581 Element config = (Element) configs.item(j);
582 String name = getFirstText(config.getElementsByTagName("config-property-name")).trim();
583 String type = getFirstText(config.getElementsByTagName("config-property-type")).trim();
584 String test = getFirstText(config.getElementsByTagName("config-property-value"));
585 String value = test == null ? null : test.trim();
586 test = getFirstText(config.getElementsByTagName("description"));
587 String desc = test == null ? null : test.trim();
588 all.add(new ConfigParam(name, type, desc, value));
589 }
590 }
591 }
592 return new ResourceAdapterParams(displayName, description, (ConfigParam[]) all.toArray(new ConfigParam[all.size()]));
593 } catch (Exception e) {
594 log.error("Unable to read resource adapter DD", e);
595 return null;
596 }
597 }
598
599 private String getFirstText(NodeList list) {
600 if(list.getLength() == 0) {
601 return null;
602 }
603 Element first = (Element) list.item(0);
604 StringBuffer buf = new StringBuffer();
605 NodeList all = first.getChildNodes();
606 for(int i=0; i<all.getLength(); i++) {
607 Node node = all.item(i);
608 if(node.getNodeType() == Node.TEXT_NODE) {
609 buf.append(node.getNodeValue());
610 }
611 }
612 return buf.toString();
613 }
614
615 private void loadConnectionFactory(ActionRequest actionRequest, String adapterName, String factoryName, PoolData data) {
616 AbstractName abstractAdapterName = new AbstractName(URI.create(adapterName));
617 AbstractName abstractFactoryName = new AbstractName(URI.create(factoryName));
618
619 ResourceAdapterModule adapter = (ResourceAdapterModule) PortletManager.getManagedBean(actionRequest,abstractAdapterName);
620 JCAManagedConnectionFactory factory = (JCAManagedConnectionFactory) PortletManager.getManagedBean(actionRequest, abstractFactoryName);
621 data.adapterDisplayName = adapter.getDisplayName();
622 data.adapterDescription = adapter.getDescription();
623 try {
624 data.name = (String)abstractFactoryName.getName().get("name");
625 if(data.isGeneric()) {
626 data.url = (String) factory.getConfigProperty("ConnectionURL");
627 data.driverClass = (String) factory.getConfigProperty("Driver");
628 data.user = (String) factory.getConfigProperty("UserName");
629 data.password = (String) factory.getConfigProperty("Password");
630 } else {
631 ResourceAdapterParams params = getRARConfiguration(actionRequest, data.getRarPath(), data.getAdapterDisplayName(), adapterName);
632 for(int i=0; i<params.getConfigParams().length; i++) {
633 ConfigParam cp = params.getConfigParams()[i];
634 Object value = factory.getConfigProperty(cp.getName());
635 data.properties.put("property-"+cp.getName(), value == null ? null : value.toString());
636 }
637 }
638 } catch (Exception e) {
639 log.error("Unable to look up connection property", e);
640 }
641 //todo: push the lookup into ManagementHelper
642 PoolingAttributes pool = (PoolingAttributes) factory.getConnectionManagerContainer();
643 data.minSize = Integer.toString(pool.getPartitionMinSize());
644 data.maxSize = Integer.toString(pool.getPartitionMaxSize());
645 data.blockingTimeout = Integer.toString(pool.getBlockingTimeoutMilliseconds());
646 data.idleTimeout = Integer.toString(pool.getIdleTimeoutMinutes());
647
648 }
649
650 protected void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
651 if (WindowState.MINIMIZED.equals(renderRequest.getWindowState())) {
652 return;
653 }
654 try {
655 String mode = renderRequest.getParameter(MODE_KEY);
656 PoolData data = new PoolData();
657 data.load(renderRequest);
658 renderRequest.setAttribute("pool", data);
659 // If not headed anywhere in particular, send to list
660 if(mode == null || mode.equals("")) {
661 mode = LIST_MODE;
662 }
663 // If headed to list but there's an import in progress, redirect to import status
664 if(mode.equals(LIST_MODE) && getImportStatus(renderRequest) != null) {
665 mode = IMPORT_STATUS_MODE;
666 }
667
668 if(mode.equals(LIST_MODE)) {
669 renderList(renderRequest, renderResponse);
670 } else if(mode.equals(EDIT_MODE)) {
671 renderEdit(renderRequest, renderResponse, data);
672 } else if(mode.equals(SELECT_RDBMS_MODE)) {
673 renderSelectRDBMS(renderRequest, renderResponse);
674 } else if(mode.equals(DOWNLOAD_MODE)) {
675 renderDownload(renderRequest, renderResponse);
676 } else if(mode.equals(DOWNLOAD_STATUS_MODE)) {
677 renderDownloadStatus(renderRequest, renderResponse);
678 } else if(mode.equals(BASIC_PARAMS_MODE)) {
679 renderBasicParams(renderRequest, renderResponse, data);
680 } else if(mode.equals(CONFIRM_URL_MODE)) {
681 renderConfirmURL(renderRequest, renderResponse);
682 } else if(mode.equals(TEST_CONNECTION_MODE)) {
683 renderTestConnection(renderRequest, renderResponse);
684 } else if(mode.equals(SHOW_PLAN_MODE)) {
685 renderPlan(renderRequest, renderResponse, data);
686 } else if(mode.equals(IMPORT_START_MODE)) {
687 renderImportUploadForm(renderRequest, renderResponse);
688 } else if(mode.equals(IMPORT_STATUS_MODE)) {
689 renderImportStatus(renderRequest, renderResponse);
690 } else if(mode.equals(USAGE_MODE)) {
691 renderUsage(renderRequest, renderResponse);
692 }
693 } catch (Throwable e) {
694 log.error("Unable to render portlet", e);
695 }
696 }
697
698 private void renderUsage(RenderRequest request, RenderResponse response) throws IOException, PortletException {
699 usageView.include(request, response);
700 }
701
702 private void renderImportStatus(RenderRequest request, RenderResponse response) throws IOException, PortletException {
703 request.setAttribute("status", getImportStatus(request));
704 populatePoolList(request);
705 importStatusView.include(request, response);
706 }
707
708 private void renderImportUploadForm(RenderRequest request, RenderResponse response) throws IOException, PortletException {
709 request.setAttribute("from", request.getParameter("from"));
710 importUploadView.include(request, response);
711 }
712
713 private void renderList(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
714 populatePoolList(renderRequest);
715 listView.include(renderRequest, renderResponse);
716 }
717
718 private void populatePoolList(PortletRequest renderRequest) {
719 ResourceAdapterModule[] modules = PortletManager.getOutboundRAModules(renderRequest, "javax.sql.DataSource");
720 List list = new ArrayList();
721 for (int i = 0; i < modules.length; i++) {
722 ResourceAdapterModule module = modules[i];
723 AbstractName moduleName = PortletManager.getManagementHelper(renderRequest).getNameFor(module);
724
725 JCAManagedConnectionFactory[] databases = PortletManager.getOutboundFactoriesForRA(renderRequest, module, "javax.sql.DataSource");
726 for (int j = 0; j < databases.length; j++) {
727 JCAManagedConnectionFactory db = databases[j];
728 AbstractName dbName = PortletManager.getManagementHelper(renderRequest).getNameFor(db);
729 list.add(new ConnectionPool(moduleName, dbName, (String)dbName.getName().get(NameFactory.J2EE_NAME), ((GeronimoManagedBean)db).getState()));
730 }
731 }
732 Collections.sort(list);
733 renderRequest.setAttribute("pools", list);
734 }
735
736 private void renderEdit(RenderRequest renderRequest, RenderResponse renderResponse, PoolData data) throws IOException, PortletException {
737 if(data.abstractName == null || data.abstractName.equals("")) {
738 loadDriverJARList(renderRequest);
739 }
740 if(!data.isGeneric()) {
741 ResourceAdapterParams params = getRARConfiguration(renderRequest, data.getRarPath(), data.getAdapterDisplayName(), renderRequest.getParameter("adapterAbstractName"));
742 data.adapterDisplayName = params.getDisplayName();
743 data.adapterDescription = params.getDescription();
744 Map map = new HashMap();
745 boolean more = false;
746 for (int i = 0; i < params.getConfigParams().length; i++) {
747 ConfigParam param = params.getConfigParams()[i];
748 if(!data.properties.containsKey("property-"+param.getName())) {
749 data.properties.put("property-"+param.getName(), param.getDefaultValue());
750 more = true;
751 }
752 map.put("property-"+param.getName(), param);
753 }
754 if(more) {
755 data.loadPropertyNames();
756 }
757 renderRequest.setAttribute("ConfigParams", map);
758 }
759 editView.include(renderRequest, renderResponse);
760 }
761
762 private void renderSelectRDBMS(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
763 renderRequest.setAttribute("databases", getAllDrivers(renderRequest));
764 selectRDBMSView.include(renderRequest, renderResponse);
765 }
766
767 private void renderDownload(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
768 renderRequest.setAttribute("drivers", getDriverInfo(renderRequest));
769 downloadView.include(renderRequest, renderResponse);
770 }
771
772 private void renderDownloadStatus(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
773 downloadStatusView.include(renderRequest, renderResponse);
774 }
775
776 private void renderBasicParams(RenderRequest renderRequest, RenderResponse renderResponse, PoolData data) throws IOException, PortletException {
777 loadDriverJARList(renderRequest);
778 // Make sure all properties available for the DB are listed
779 DatabaseDriver info = getDatabaseInfo(renderRequest, data);
780 if(info != null) {
781 String[] params = info.getURLParameters();
782 for (int i = 0; i < params.length; i++) {
783 String param = params[i];
784 final String key = "urlproperty-"+param;
785 if(!data.getUrlProperties().containsKey(key)) {
786 data.getUrlProperties().put(key, param.equalsIgnoreCase("port") && info.getDefaultPort() > 0 ? new Integer(info.getDefaultPort()) : null);
787 }
788 }
789 }
790 // Pass on errors
791 renderRequest.setAttribute("driverError", renderRequest.getParameter("driverError"));
792
793 basicParamsView.include(renderRequest, renderResponse);
794 }
795
796 private void loadDriverJARList(RenderRequest renderRequest) {
797 // List the available JARs
798 List list = new ArrayList();
799 ListableRepository[] repos = PortletManager.getCurrentServer(renderRequest).getRepositories();
800 for (int i = 0; i < repos.length; i++) {
801 ListableRepository repo = repos[i];
802
803 SortedSet artifacts = repo.list();
804 outer:
805 for (Iterator iterator = artifacts.iterator(); iterator.hasNext();) {
806 Artifact artifact = (Artifact) iterator.next();
807 String test = artifact.toString();
808 // todo should only test groupId and should check for long (org.apache.geronimo) and short form
809 for (int k = 0; k < SKIP_ENTRIES_WITH.length; k++) {
810 String skip = SKIP_ENTRIES_WITH[k];
811 if(test.indexOf(skip) > -1) {
812 continue outer;
813 }
814 }
815 list.add(test);
816 }
817 }
818 Collections.sort(list);
819 renderRequest.setAttribute("availableJars", list);
820 }
821
822 private void renderConfirmURL(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
823 confirmURLView.include(renderRequest, renderResponse);
824 }
825
826 private void renderTestConnection(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
827 // Pass on results
828 renderRequest.setAttribute("connectResult", renderRequest.getParameter("connectResult"));
829 renderRequest.setAttribute("connectError", renderRequest.getPortletSession().getAttribute("connectError"));
830 testConnectionView.include(renderRequest, renderResponse);
831 }
832
833 private void renderPlan(RenderRequest renderRequest, RenderResponse renderResponse, PoolData data) throws IOException, PortletException {
834 // Pass on results
835 renderRequest.setAttribute("deploymentPlan", renderRequest.getPortletSession().getAttribute("deploymentPlan"));
836 // Digest the RAR URI
837 String path = PortletManager.getRepositoryEntry(renderRequest, data.getRarPath()).getPath();
838 String base = PortletManager.getCurrentServer(renderRequest).getServerInfo().getCurrentBaseDirectory();
839 if(base != null && path.startsWith(base)) {
840 path = path.substring(base.length());
841 if(path.startsWith("/")) {
842 path = path.substring(1);
843 }
844 } else {
845 int pos = path.lastIndexOf('/');
846 path = path.substring(pos+1);
847 }
848 renderRequest.setAttribute("rarRelativePath", path);
849
850 planView.include(renderRequest, renderResponse);
851 }
852
853 private static String attemptConnect(PortletRequest request, PoolData data) throws SQLException, IllegalAccessException, InstantiationException {
854 Class driverClass = attemptDriverLoad(request, data);
855 Driver driver = (Driver) driverClass.newInstance();
856 if(driver.acceptsURL(data.url)) {
857 Properties props = new Properties();
858 if(data.user != null) {
859 props.put("user", data.user);
860 }
861 if(data.password != null) {
862 props.put("password", data.password);
863 }
864 Connection con = null;
865 try {
866 con = driver.connect(data.url, props);
867 final DatabaseMetaData metaData = con.getMetaData();
868 return metaData.getDatabaseProductName()+" "+metaData.getDatabaseProductVersion();
869 } finally {
870 if(con != null) try{con.close();}catch(SQLException e) {}
871 }
872 } else throw new SQLException("Driver "+data.getDriverClass()+" does not accept URL "+data.url);
873 }
874
875 private void delete(PortletRequest request, ActionResponse response, PoolData data) {
876 // check to make sure the abstract name does not begin with 'org.apache.geronimo.configs'
877 // if it does not - then delete it -- otherwise it is a system database
878 if(data.getAbstractName() != null) {
879 boolean isSystemDatabasePool = (data.getAbstractName().indexOf("org.apache.geronimo.configs") == 0);
880
881 if(! isSystemDatabasePool) {
882 DeploymentManager mgr = PortletManager.getDeploymentManager(request);
883 try {
884 // retrieve all running modules
885 TargetModuleID[] runningIds = mgr.getRunningModules(ModuleType.RAR, mgr.getTargets());
886
887 // index of module to keep
888 int index = -1;
889
890 // only keep module id that is associated with selected DB pool
891 for(int i = 0; i < runningIds.length; i++) {
892 if(data.getAbstractName().contains(runningIds[i].getModuleID())) {
893 index = i;
894 break;
895 }
896 }
897 TargetModuleID[] ids = { runningIds[index] };
898
899 // undeploy the db pool
900 ProgressObject po = mgr.undeploy(ids);
901 waitForProgress(po);
902
903 if(po.getDeploymentStatus().isCompleted()) {
904 log.info("Undeployment completed successfully!");
905 }
906 } catch(Exception e) {
907 log.error("Undeployment unsuccessful!");
908 } finally {
909 if(mgr != null) mgr.release();
910 }
911 }
912 }
913 }
914
915 private static String save(PortletRequest request, ActionResponse response, PoolData data, boolean planOnly) {
916 ImportStatus status = getImportStatus(request);
917 if(data.abstractName == null || data.abstractName.equals("")) { // we're creating a new pool
918 data.name = data.name.replaceAll("\\s", "");
919 DeploymentManager mgr = PortletManager.getDeploymentManager(request);
920 try {
921 File rarFile = getRAR(request, data.getRarPath());
922 ConnectorDeployable deployable = new ConnectorDeployable(rarFile.toURL());
923 DeploymentConfiguration config = mgr.createConfiguration(deployable);
924 final DDBeanRoot ddBeanRoot = deployable.getDDBeanRoot();
925 Connector15DCBRoot root = (Connector15DCBRoot) config.getDConfigBeanRoot(ddBeanRoot);
926 ConnectorDCB connector = (ConnectorDCB) root.getDConfigBean(ddBeanRoot.getChildBean(root.getXpaths()[0])[0]);
927
928 EnvironmentData environment = new EnvironmentData();
929 connector.setEnvironment(environment);
930 org.apache.geronimo.deployment.service.jsr88.Artifact configId = new org.apache.geronimo.deployment.service.jsr88.Artifact();
931 environment.setConfigId(configId);
932 configId.setGroupId("console.dbpool");
933 String artifactId = data.name;
934 if(artifactId.indexOf('/') != -1) {
935 // slash in artifact-id results in invalid configuration-id and leads to deployment errors
936 artifactId = artifactId.replaceAll("/", "%2F");
937 }
938 configId.setArtifactId(artifactId);
939 configId.setVersion("1.0");
940 configId.setType("rar");
941
942 String[] jars = data.getJars();
943 int length = jars[jars.length - 1].length() ==0? jars.length -1: jars.length;
944 org.apache.geronimo.deployment.service.jsr88.Artifact[] dependencies = new org.apache.geronimo.deployment.service.jsr88.Artifact[length];
945 for (int i=0; i<dependencies.length; i++) {
946 dependencies[i] = new org.apache.geronimo.deployment.service.jsr88.Artifact();
947 }
948 environment.setDependencies(dependencies);
949 for (int i=0; i<dependencies.length; i++) {
950 Artifact tmp = Artifact.create(jars[i]);
951 dependencies[i].setGroupId(tmp.getGroupId());
952 dependencies[i].setArtifactId(tmp.getArtifactId());
953 dependencies[i].setVersion(tmp.getVersion().toString());
954 dependencies[i].setType(tmp.getType());
955 }
956
957 ResourceAdapter adapter = connector.getResourceAdapter()[0];
958 ConnectionDefinition definition = new ConnectionDefinition();
959 adapter.setConnectionDefinition(new ConnectionDefinition[]{definition});
960 definition.setConnectionFactoryInterface("javax.sql.DataSource");
961 ConnectionDefinitionInstance instance = new ConnectionDefinitionInstance();
962 definition.setConnectionInstance(new ConnectionDefinitionInstance[]{instance});
963 instance.setName(data.getName());
964 ConfigPropertySetting[] settings = instance.getConfigPropertySetting();
965 if(data.isGeneric()) { // it's a generic TranQL JDBC pool
966 for (int i = 0; i < settings.length; i++) {
967 ConfigPropertySetting setting = settings[i];
968 if(setting.getName().equals("UserName")) {
969 setting.setValue(data.user);
970 } else if(setting.getName().equals("Password")) {
971 setting.setValue(data.password);
972 } else if(setting.getName().equals("ConnectionURL")) {
973 setting.setValue(data.url);
974 } else if(setting.getName().equals("Driver")) {
975 setting.setValue(data.driverClass);
976 }
977 }
978 } else { // it's an XA driver or non-TranQL RA
979 for (int i = 0; i < settings.length; i++) {
980 ConfigPropertySetting setting = settings[i];
981 String value = (String) data.properties.get("property-"+setting.getName());
982 setting.setValue(value == null ? "" : value);
983 }
984 }
985 ConnectionManager manager = instance.getConnectionManager();
986 manager.setTransactionLocal(true);
987 SinglePool pool = new SinglePool();
988 manager.setPoolSingle(pool);
989 pool.setMatchOne(true);
990 // Max Size needs to be set before the minimum. This is because
991 // the connection manager will constrain the minimum based on the
992 // current maximum value in the pool. We might consider adding a
993 // setPoolConstraints method to allow specifying both at the same time.
994 if(data.maxSize != null && !data.maxSize.equals("")) {
995 pool.setMaxSize(new Integer(data.maxSize));
996 }
997 if(data.minSize != null && !data.minSize.equals("")) {
998 pool.setMinSize(new Integer(data.minSize));
999 }
1000 if(data.blockingTimeout != null && !data.blockingTimeout.equals("")) {
1001 pool.setBlockingTimeoutMillis(new Integer(data.blockingTimeout));
1002 }
1003 if(data.idleTimeout != null && !data.idleTimeout.equals("")) {
1004 pool.setIdleTimeoutMinutes(new Integer(data.idleTimeout));
1005 }
1006
1007 if(planOnly) {
1008 ByteArrayOutputStream out = new ByteArrayOutputStream();
1009 config.save(out);
1010 out.close();
1011 return new String(out.toByteArray(), "US-ASCII");
1012 } else {
1013 File tempFile = File.createTempFile("console-deployment",".xml");
1014 tempFile.deleteOnExit();
1015 log.debug("Writing database pool deployment plan to "+tempFile.getAbsolutePath());
1016 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile));
1017 config.save(out);
1018 out.flush();
1019 out.close();
1020 Target[] targets = mgr.getTargets();
1021 ProgressObject po = mgr.distribute(targets, rarFile, tempFile);
1022 waitForProgress(po);
1023 if(po.getDeploymentStatus().isCompleted()) {
1024 TargetModuleID[] ids = po.getResultTargetModuleIDs();
1025 po = mgr.start(ids);
1026 waitForProgress(po);
1027 if(po.getDeploymentStatus().isCompleted()) {
1028 ids = po.getResultTargetModuleIDs();
1029 if(status != null) {
1030 status.getCurrentPool().setName(data.getName());
1031 status.getCurrentPool().setConfigurationName(ids[0].getModuleID());
1032 status.getCurrentPool().setFinished(true);
1033 response.setRenderParameter(MODE_KEY, IMPORT_STATUS_MODE);
1034 }
1035
1036 log.info("Deployment completed successfully!");
1037 }
1038 } else if(po.getDeploymentStatus().isFailed()) {
1039 data.deployError = "Unable to deploy: " + data.name;
1040 response.setRenderParameter(MODE_KEY, EDIT_MODE);
1041 log.info("Deployment Failed!");
1042 }
1043 }
1044 } catch (Exception e) {
1045 log.error("Unable to save connection pool", e);
1046 } finally {
1047 if(mgr != null) mgr.release();
1048 }
1049 } else { // We're saving updates to an existing pool
1050 if(planOnly) {
1051 throw new UnsupportedOperationException("Can't update a plan for an existing deployment");
1052 }
1053 try {
1054 JCAManagedConnectionFactory factory = (JCAManagedConnectionFactory) PortletManager.getManagedBean(request, new AbstractName(URI.create(data.getAbstractName())));
1055 if(data.isGeneric()) {
1056 factory.setConfigProperty("ConnectionURL", data.getUrl());
1057 factory.setConfigProperty("UserName", data.getUser());
1058 factory.setConfigProperty("Password", data.getPassword());
1059 } else {
1060 for (Iterator it = data.getProperties().entrySet().iterator(); it.hasNext();) {
1061 Map.Entry entry = (Map.Entry) it.next();
1062 factory.setConfigProperty(((String) entry.getKey()).substring("property-".length()), entry.getValue());
1063 }
1064 }
1065 //todo: push the lookup into ManagementHelper
1066 PoolingAttributes pool = (PoolingAttributes) factory.getConnectionManagerContainer();
1067 pool.setPartitionMinSize(data.minSize == null || data.minSize.equals("") ? 0 : Integer.parseInt(data.minSize));
1068 pool.setPartitionMaxSize(data.maxSize == null || data.maxSize.equals("") ? 10 : Integer.parseInt(data.maxSize));
1069 pool.setBlockingTimeoutMilliseconds(data.blockingTimeout == null || data.blockingTimeout.equals("") ? 5000 : Integer.parseInt(data.blockingTimeout));
1070 pool.setIdleTimeoutMinutes(data.idleTimeout == null || data.idleTimeout.equals("") ? 15 : Integer.parseInt(data.idleTimeout));
1071 } catch (Exception e) {
1072 log.error("Unable to save connection pool", e);
1073 }
1074 }
1075 return null;
1076 }
1077
1078 private static void waitForProgress(ProgressObject po) {
1079 while(po.getDeploymentStatus().isRunning()) {
1080 try {
1081 Thread.sleep(100);
1082 } catch (InterruptedException e) {
1083 e.printStackTrace();
1084 }
1085 }
1086 }
1087
1088 private static ImportStatus getImportStatus(PortletRequest request) {
1089 return (ImportStatus) request.getPortletSession(true).getAttribute("ImportStatus");
1090 }
1091
1092 private static File getRAR(PortletRequest request, String rarPath) {
1093 org.apache.geronimo.kernel.repository.Artifact artifact = org.apache.geronimo.kernel.repository.Artifact.create(rarPath);
1094 ListableRepository[] repos = PortletManager.getCurrentServer(request).getRepositories();
1095 for (int i = 0; i < repos.length; i++) {
1096 ListableRepository repo = repos[i];
1097 // if the artifact is not fully resolved then try to resolve it
1098 if (!artifact.isResolved()) {
1099 SortedSet results = repo.list(artifact);
1100 if (!results.isEmpty()) {
1101 artifact = (org.apache.geronimo.kernel.repository.Artifact) results.first();
1102 }
1103 }
1104 File url = repo.getLocation(artifact);
1105 if (url != null) {
1106 if (url.exists() && url.canRead() && !url.isDirectory()) {
1107 return url;
1108 }
1109 }
1110 }
1111 return null;
1112 }
1113
1114 /**
1115 * WARNING: This method relies on having access to the same repository
1116 * URLs as the server uses.
1117 */
1118 private static Class attemptDriverLoad(PortletRequest request, PoolData data) {
1119 List list = new ArrayList();
1120 try {
1121 String[] jars = data.getJars();
1122 if(jars == null) {
1123 log.error("Driver load failed since no jar files were selected.");
1124 return null;
1125 }
1126 ListableRepository[] repos = PortletManager.getCurrentServer(request).getRepositories();
1127
1128 for (int i = 0; i < jars.length; i++) {
1129 org.apache.geronimo.kernel.repository.Artifact artifact = org.apache.geronimo.kernel.repository.Artifact.create(jars[i]);
1130 for (int j = 0; j < repos.length; j++) {
1131 ListableRepository repo = repos[j];
1132 File url = repo.getLocation(artifact);
1133 if (url != null) {
1134 list.add(url.toURL());
1135 }
1136 }
1137 }
1138 URLClassLoader loader = new URLClassLoader((URL[]) list.toArray(new URL[list.size()]), DatabasePoolPortlet.class.getClassLoader());
1139 try {
1140 return loader.loadClass(data.driverClass);
1141 } catch (ClassNotFoundException e) {
1142 return null;
1143 }
1144 } catch (Exception e) {
1145 e.printStackTrace();
1146 return null;
1147 }
1148 }
1149
1150 private static String populateURL(String url, String[] keys, Map properties) {
1151 for (int i = 0; i < keys.length; i++) {
1152 String key = keys[i];
1153 String value = (String) properties.get("urlproperty-"+key);
1154 if(value == null || value.equals("")) {
1155 int begin = url.indexOf("{"+key+"}");
1156 int end = begin + key.length() + 2;
1157 for(int j=begin-1; j>=0; j--) {
1158 char c = url.charAt(j);
1159 if(c == ';' || c == ':') {
1160 begin = j;
1161 break;
1162 } else if(c == '/') {
1163 if(url.length() > end && url.charAt(end) == '/') {
1164 begin = j; // Don't leave // if foo is null for /<foo>/
1165 }
1166 break;
1167 }
1168 }
1169 url = url.substring(0, begin)+url.substring(end);
1170 } else {
1171 if(value.indexOf('\\') != -1 || value.indexOf('$') != -1) {
1172 // value contains backslash or dollar sign and needs preprocessing for replaceAll to work properly
1173 StringBuffer temp = new StringBuffer();
1174 char[] valueChars = value.toCharArray();
1175 for(int j = 0; j < valueChars.length; ++j) {
1176 if(valueChars[j] == '\\' || valueChars[j] == '$') {
1177 temp.append('\\');
1178 }
1179 temp.append(valueChars[j]);
1180 }
1181 value = temp.toString();
1182 }
1183 url = url.replaceAll("\\{"+key+"\\}", value);
1184 }
1185 }
1186 return url;
1187 }
1188
1189 private static DatabaseDriver[] getAllDrivers(PortletRequest request) {
1190 DatabaseDriver[] result = (DatabaseDriver[]) PortletManager.getGBeansImplementing(request, DatabaseDriver.class);
1191 Arrays.sort(result, new Comparator() {
1192 public int compare(Object o1, Object o2) {
1193 String name1 = ((DatabaseDriver) o1).getName();
1194 String name2 = ((DatabaseDriver) o2).getName();
1195 if (name1.equals("Other")) name1 = "zzzOther";
1196 if (name2.equals("Other")) name2 = "zzzOther";
1197 return name1.compareTo(name2);
1198 }
1199 });
1200 return result;
1201 }
1202
1203 private static DatabaseDriver getDatabaseInfo(PortletRequest request, PoolData data) {
1204 DatabaseDriver info = null;
1205 DatabaseDriver[] all = getAllDrivers(request);
1206 for (int i = 0; i < all.length; i++) {
1207 DatabaseDriver next = all[i];
1208 if (next.getName().equals(data.getDbtype())) {
1209 info = next;
1210 break;
1211 }
1212 }
1213 return info;
1214 }
1215
1216 private static DatabaseDriver getDatabaseInfoFromDriver(PortletRequest request, PoolData data) {
1217 DatabaseDriver info = null;
1218 DatabaseDriver[] all = getAllDrivers(request);
1219 for (int i = 0; i < all.length; i++) {
1220 DatabaseDriver next = all[i];
1221 if (next.getDriverClassName()!=null && next.getDriverClassName().equals(data.getDriverClass())) {
1222 info = next;
1223 break;
1224 }
1225 }
1226 return info;
1227 }
1228
1229 public static class PoolData implements Serializable {
1230 private static final long serialVersionUID = 1L;
1231 private String name;
1232 private String dbtype;
1233 private String user;
1234 private String password;
1235 private Map properties = new HashMap(); // Configuration for non-Generic drivers
1236 private Map urlProperties = new HashMap(); // URL substitution for Generic drivers
1237 private Map propertyNames; //todo: store these in the ConfigParam instead
1238 private String driverClass;
1239 private String url;
1240 private String urlPrototype;
1241 private String[] jars;
1242 private String minSize;
1243 private String maxSize;
1244 private String blockingTimeout;
1245 private String idleTimeout;
1246 private String abstractName;
1247 private String adapterDisplayName;
1248 private String adapterDescription;
1249 private String rarPath;
1250 private String importSource;
1251 private Map abstractNameMap; // generated as needed, don't need to read/write it
1252 private String deployError;
1253
1254 public void load(PortletRequest request) {
1255 name = request.getParameter("name");
1256 if(name != null && name.equals("")) name = null;
1257 driverClass = request.getParameter("driverClass");
1258 if(driverClass != null && driverClass.equals("")) driverClass = null;
1259 dbtype = request.getParameter("dbtype");
1260 if(dbtype != null && dbtype.equals("")) dbtype = null;
1261 user = request.getParameter("user");
1262 if(user != null && user.equals("")) user = null;
1263 password = request.getParameter("password");
1264 if(password != null && password.equals("")) password = null;
1265 url = request.getParameter("url");
1266 if(url != null && url.equals("")) {
1267 url = null;
1268 } else if(url != null && url.startsWith("URLENCODED")) {
1269 try {
1270 url = URLDecoder.decode(url.substring(10), "UTF-8");
1271 } catch (UnsupportedEncodingException e) {
1272 throw new RuntimeException("Unable to decode URL", e);
1273 } catch(IllegalArgumentException e) { // not encoded after all??
1274 url = url.substring(10);
1275 }
1276 }
1277 urlPrototype = request.getParameter("urlPrototype");
1278 if(urlPrototype != null && urlPrototype.equals("")) urlPrototype = null;
1279 jars = request.getParameterValues("jars");
1280 minSize = request.getParameter("minSize");
1281 if(minSize != null && minSize.equals("")) minSize = null;
1282 maxSize = request.getParameter("maxSize");
1283 if(maxSize != null && maxSize.equals("")) maxSize = null;
1284 blockingTimeout = request.getParameter("blockingTimeout");
1285 if(blockingTimeout != null && blockingTimeout.equals("")) blockingTimeout = null;
1286 idleTimeout = request.getParameter("idleTimeout");
1287 if(idleTimeout != null && idleTimeout.equals("")) idleTimeout = null;
1288 abstractName = request.getParameter("abstractName");
1289 if(abstractName != null && abstractName.equals("")) abstractName = null;
1290 adapterDisplayName = request.getParameter("adapterDisplayName");
1291 if(adapterDisplayName != null && adapterDisplayName.equals("")) adapterDisplayName = null;
1292 adapterDescription = request.getParameter("adapterDescription");
1293 if(adapterDescription != null && adapterDescription.equals("")) adapterDescription = null;
1294 rarPath = request.getParameter("rarPath");
1295 if(rarPath != null && rarPath.equals("")) rarPath = null;
1296 importSource = request.getParameter("importSource");
1297 if(importSource != null && importSource.equals("")) importSource = null;
1298 Map map = request.getParameterMap();
1299 propertyNames = new HashMap();
1300 for (Iterator it = map.keySet().iterator(); it.hasNext();) {
1301 String key = (String) it.next();
1302 if(key.startsWith("urlproperty-")) {
1303 urlProperties.put(key, request.getParameter(key));
1304 } else if(key.startsWith("property-")) {
1305 properties.put(key, request.getParameter(key));
1306 propertyNames.put(key, getPropertyName(key));
1307 }
1308 }
1309 deployError = request.getParameter("deployError");
1310 if(deployError != null && deployError.equals("")) deployError = null;
1311 }
1312
1313 public void loadPropertyNames() {
1314 propertyNames = new HashMap();
1315 for (Iterator it = properties.keySet().iterator(); it.hasNext();) {
1316 String key = (String) it.next();
1317 propertyNames.put(key, getPropertyName(key));
1318 }
1319 }
1320
1321 private static String getPropertyName(String key) {
1322 int pos = key.indexOf('-');
1323 key = Character.toUpperCase(key.charAt(pos+1))+key.substring(pos+2);
1324 StringBuffer buf = new StringBuffer();
1325 pos = 0;
1326 for(int i=1; i<key.length(); i++) {
1327 if(Character.isUpperCase(key.charAt(i))) {
1328 if(Character.isUpperCase(key.charAt(i-1))) { // ongoing capitalized word
1329
1330 } else { // start of a new word
1331 buf.append(key.substring(pos, i)).append(" ");
1332 pos = i;
1333 }
1334 } else {
1335 if(Character.isUpperCase(key.charAt(i-1)) && i-pos > 1) { // first lower-case after a series of caps
1336 buf.append(key.substring(pos, i-1)).append(" ");
1337 pos = i-1;
1338 }
1339 }
1340 }
1341 buf.append(key.substring(pos));
1342 return buf.toString();
1343 }
1344
1345 public void store(ActionResponse response) {
1346 if(name != null) response.setRenderParameter("name", name);
1347 if(dbtype != null) response.setRenderParameter("dbtype", dbtype);
1348 if(driverClass != null) response.setRenderParameter("driverClass", driverClass);
1349 if(user != null) response.setRenderParameter("user", user);
1350 if(password != null) response.setRenderParameter("password", password);
1351 if(url != null) { // attempt to work around Pluto/Tomcat error with ; in a stored value
1352 try {
1353 response.setRenderParameter("url", "URLENCODED"+URLEncoder.encode(url, "UTF-8"));
1354 } catch (UnsupportedEncodingException e) {
1355 throw new RuntimeException("Unable to encode URL", e);
1356 }
1357 }
1358 if(urlPrototype != null) response.setRenderParameter("urlPrototype", urlPrototype);
1359 if(jars != null) response.setRenderParameter("jars", jars);
1360 if(minSize != null) response.setRenderParameter("minSize", minSize);
1361 if(maxSize != null) response.setRenderParameter("maxSize", maxSize);
1362 if(blockingTimeout != null) response.setRenderParameter("blockingTimeout", blockingTimeout);
1363 if(idleTimeout != null) response.setRenderParameter("idleTimeout", idleTimeout);
1364 if(abstractName != null) response.setRenderParameter("abstractName", abstractName);
1365 if(adapterDisplayName != null) response.setRenderParameter("adapterDisplayName", adapterDisplayName);
1366 if(adapterDescription != null) response.setRenderParameter("adapterDescription", adapterDescription);
1367 if(importSource != null) response.setRenderParameter("importSource", importSource);
1368 if(rarPath != null) response.setRenderParameter("rarPath", rarPath);
1369 for (Iterator it = urlProperties.entrySet().iterator(); it.hasNext();) {
1370 Map.Entry entry = (Map.Entry) it.next();
1371 if(entry.getValue() != null) {
1372 response.setRenderParameter((String)entry.getKey(), (String)entry.getValue());
1373 }
1374 }
1375 for (Iterator it = properties.entrySet().iterator(); it.hasNext();) {
1376 Map.Entry entry = (Map.Entry) it.next();
1377 if(entry.getValue() != null) {
1378 response.setRenderParameter((String)entry.getKey(), (String)entry.getValue());
1379 }
1380 }
1381 if(deployError != null) response.setRenderParameter("deployError", deployError);
1382 }
1383
1384 public String getName() {
1385 return name;
1386 }
1387
1388 public String getDbtype() {
1389 return dbtype;
1390 }
1391
1392 public String getUser() {
1393 return user;
1394 }
1395
1396 public String getPassword() {
1397 return password;
1398 }
1399
1400 public Map getProperties() {
1401 return properties;
1402 }
1403
1404 public Map getPropertyNames() {
1405 return propertyNames;
1406 }
1407
1408 public Map getUrlProperties() {
1409 return urlProperties;
1410 }
1411
1412 public String getUrl() {
1413 return url;
1414 }
1415
1416 public String[] getJars() {
1417 return jars;
1418 }
1419
1420 public String getMinSize() {
1421 return minSize;
1422 }
1423
1424 public String getMaxSize() {
1425 return maxSize;
1426 }
1427
1428 public String getBlockingTimeout() {
1429 return blockingTimeout;
1430 }
1431
1432 public String getIdleTimeout() {
1433 return idleTimeout;
1434 }
1435
1436 public String getDriverClass() {
1437 return driverClass;
1438 }
1439
1440 public String getUrlPrototype() {
1441 return urlPrototype;
1442 }
1443
1444 public String getAbstractName() {
1445 return abstractName;
1446 }
1447
1448 public String getAdapterDisplayName() {
1449 return adapterDisplayName;
1450 }
1451
1452 public String getAdapterDescription() {
1453 return adapterDescription;
1454 }
1455
1456 public String getRarPath() {
1457 return rarPath;
1458 }
1459
1460 public boolean isGeneric() {
1461 //todo: is there any better way to tell?
1462 return adapterDisplayName == null || adapterDisplayName.equals("TranQL Generic JDBC Resource Adapter");
1463 }
1464
1465 public String getImportSource() {
1466 return importSource;
1467 }
1468
1469 public Map getAbstractNameMap() {
1470 if(abstractName == null) return Collections.EMPTY_MAP;
1471 if(abstractNameMap != null) return abstractNameMap;
1472 AbstractName name = new AbstractName(URI.create(abstractName));
1473 abstractNameMap = new HashMap(name.getName());
1474 abstractNameMap.put("domain", name.getObjectName().getDomain());
1475 abstractNameMap.put("groupId", name.getArtifact().getGroupId());
1476 abstractNameMap.put("artifactId", name.getArtifact().getArtifactId());
1477 abstractNameMap.put("type", name.getArtifact().getType());
1478 abstractNameMap.put("version", name.getArtifact().getVersion().toString());
1479 return abstractNameMap;
1480 }
1481
1482 public String getDeployError() {
1483 return deployError;
1484 }
1485 }
1486
1487 public static class ConnectionPool implements Serializable, Comparable {
1488 private static final long serialVersionUID = 1L;
1489 private final String adapterAbstractName;
1490 private final String factoryAbstractName;
1491 private final String name;
1492 private final String parentName;
1493 private final int state;
1494
1495 public ConnectionPool(AbstractName adapterAbstractName, AbstractName factoryAbstractName, String name, int state) {
1496 this.adapterAbstractName = adapterAbstractName.toURI().toString();
1497 String parent = (String)adapterAbstractName.getName().get(NameFactory.J2EE_APPLICATION);
1498 if(parent != null && parent.equals("null")) {
1499 parent = null;
1500 }
1501 parentName = parent;
1502 this.factoryAbstractName = factoryAbstractName.toURI().toString();
1503 this.name = name;
1504 this.state = state;
1505 }
1506
1507 public String getAdapterAbstractName() {
1508 return adapterAbstractName;
1509 }
1510
1511 public String getFactoryAbstractName() {
1512 return factoryAbstractName;
1513 }
1514
1515 public String getName() {
1516 return name;
1517 }
1518
1519 public String getParentName() {
1520 return parentName;
1521 }
1522
1523 public int getState() {
1524 return state;
1525 }
1526
1527 public String getStateName() {
1528 return State.toString(state);
1529 }
1530
1531 public int compareTo(Object o) {
1532 final ConnectionPool pool = (ConnectionPool)o;
1533 int names = name.compareTo(pool.name);
1534 if(parentName == null) {
1535 if(pool.parentName == null) {
1536 return names;
1537 } else {
1538 return -1;
1539 }
1540 } else {
1541 if(pool.parentName == null) {
1542 return 1;
1543 } else {
1544 int test = parentName.compareTo(pool.parentName);
1545 if(test != 0) {
1546 return test;
1547 } else {
1548 return names;
1549 }
1550 }
1551 }
1552 }
1553 }
1554
1555 public static class ResourceAdapterParams {
1556 private String displayName;
1557 private String description;
1558 private ConfigParam[] configParams;
1559
1560 public ResourceAdapterParams(String displayName, String description, ConfigParam[] configParams) {
1561 this.displayName = displayName;
1562 this.description = description;
1563 this.configParams = configParams;
1564 }
1565
1566 public String getDisplayName() {
1567 return displayName;
1568 }
1569
1570 public String getDescription() {
1571 return description;
1572 }
1573
1574 public ConfigParam[] getConfigParams() {
1575 return configParams;
1576 }
1577 }
1578
1579 public static class ConfigParam {
1580 private String name;
1581 private String type;
1582 private String description;
1583 private String defaultValue;
1584
1585 public ConfigParam(String name, String type, String description, String defaultValue) {
1586 this.name = name;
1587 this.type = type;
1588 this.description = description;
1589 this.defaultValue = defaultValue;
1590 }
1591
1592 public String getName() {
1593 return name;
1594 }
1595
1596 public String getType() {
1597 return type;
1598 }
1599
1600 public String getDescription() {
1601 return description;
1602 }
1603
1604 public String getDefaultValue() {
1605 return defaultValue;
1606 }
1607 }
1608 }