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