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    }