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.securitymanager.realm;
018    
019    import java.io.BufferedReader;
020    import java.io.File;
021    import java.io.FileWriter;
022    import java.io.IOException;
023    import java.io.PrintWriter;
024    import java.io.Serializable;
025    import java.io.StringReader;
026    import java.io.StringWriter;
027    import java.io.Writer;
028    import java.net.MalformedURLException;
029    import java.net.URI;
030    import java.net.URL;
031    import java.net.URLClassLoader;
032    import java.util.ArrayList;
033    import java.util.Arrays;
034    import java.util.Collections;
035    import java.util.HashMap;
036    import java.util.Iterator;
037    import java.util.LinkedHashMap;
038    import java.util.List;
039    import java.util.Map;
040    import java.util.Set;
041    import java.util.SortedSet;
042    
043    import javax.enterprise.deploy.spi.DeploymentManager;
044    import javax.enterprise.deploy.spi.Target;
045    import javax.enterprise.deploy.spi.TargetModuleID;
046    import javax.enterprise.deploy.spi.status.ProgressObject;
047    import javax.management.MalformedObjectNameException;
048    import javax.management.ObjectName;
049    import javax.portlet.ActionRequest;
050    import javax.portlet.ActionResponse;
051    import javax.portlet.PortletConfig;
052    import javax.portlet.PortletException;
053    import javax.portlet.PortletRequest;
054    import javax.portlet.PortletRequestDispatcher;
055    import javax.portlet.PortletSession;
056    import javax.portlet.RenderRequest;
057    import javax.portlet.RenderResponse;
058    import javax.portlet.WindowState;
059    import javax.security.auth.Subject;
060    import javax.security.auth.spi.LoginModule;
061    import javax.xml.namespace.QName;
062    
063    import org.apache.commons.logging.Log;
064    import org.apache.commons.logging.LogFactory;
065    import org.apache.geronimo.console.BasePortlet;
066    import org.apache.geronimo.console.car.ManagementHelper;
067    import org.apache.geronimo.console.util.PortletManager;
068    import org.apache.geronimo.deployment.xbeans.AbstractServiceType;
069    import org.apache.geronimo.deployment.xbeans.ArtifactType;
070    import org.apache.geronimo.deployment.xbeans.AttributeType;
071    import org.apache.geronimo.deployment.xbeans.DependenciesType;
072    import org.apache.geronimo.deployment.xbeans.EnvironmentType;
073    import org.apache.geronimo.deployment.xbeans.GbeanType;
074    import org.apache.geronimo.deployment.xbeans.ModuleDocument;
075    import org.apache.geronimo.deployment.xbeans.ModuleType;
076    import org.apache.geronimo.deployment.xbeans.ReferenceType;
077    import org.apache.geronimo.deployment.xbeans.ServiceDocument;
078    import org.apache.geronimo.deployment.xbeans.XmlAttributeType;
079    import org.apache.geronimo.gbean.AbstractName;
080    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
081    import org.apache.geronimo.kernel.Kernel;
082    import org.apache.geronimo.kernel.KernelRegistry;
083    import org.apache.geronimo.kernel.config.Configuration;
084    import org.apache.geronimo.kernel.config.ConfigurationManager;
085    import org.apache.geronimo.kernel.config.ConfigurationModuleType;
086    import org.apache.geronimo.kernel.config.ConfigurationUtil;
087    import org.apache.geronimo.kernel.repository.Artifact;
088    import org.apache.geronimo.kernel.repository.ListableRepository;
089    import org.apache.geronimo.management.geronimo.JCAManagedConnectionFactory;
090    import org.apache.geronimo.security.jaas.JaasLoginModuleChain;
091    import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
092    import org.apache.geronimo.security.jaas.LoginModuleSettings;
093    import org.apache.geronimo.security.jaas.LoginModuleControlFlag;
094    import org.apache.geronimo.security.jaas.LoginModuleControlFlagEditor;
095    import org.apache.geronimo.security.realm.SecurityRealm;
096    import org.apache.geronimo.security.realm.providers.FileAuditLoginModule;
097    import org.apache.geronimo.security.realm.providers.GeronimoPasswordCredentialLoginModule;
098    import org.apache.geronimo.security.realm.providers.NamedUsernamePasswordCredentialLoginModule;
099    import org.apache.geronimo.security.realm.providers.RepeatedFailureLockoutLoginModule;
100    import org.apache.geronimo.xbeans.geronimo.loginconfig.GerControlFlagType;
101    import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginConfigDocument;
102    import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginConfigType;
103    import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginModuleType;
104    import org.apache.geronimo.xbeans.geronimo.loginconfig.GerOptionType;
105    import org.apache.xmlbeans.XmlCursor;
106    import org.apache.xmlbeans.XmlObject;
107    import org.apache.xmlbeans.XmlOptions;
108    
109    /**
110     * A portlet that lists, creates, and edits security realms.
111     *
112     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
113     */
114    public class SecurityRealmPortlet extends BasePortlet {
115        private final static Log log = LogFactory.getLog(SecurityRealmPortlet.class);
116        private final static String[] SKIP_ENTRIES_WITH = new String[]{"geronimo", "tomcat", "tranql", "commons", "directory", "activemq"};
117        private static final String LIST_VIEW = "/WEB-INF/view/realmwizard/list.jsp";
118        private static final String EDIT_VIEW = "/WEB-INF/view/realmwizard/edit.jsp";
119        private static final String SELECT_TYPE_VIEW = "/WEB-INF/view/realmwizard/selectType.jsp";
120        private static final String CONFIGURE_VIEW = "/WEB-INF/view/realmwizard/configure.jsp";
121        private static final String ADVANCED_VIEW = "/WEB-INF/view/realmwizard/advanced.jsp";
122        private static final String TEST_LOGIN_VIEW = "/WEB-INF/view/realmwizard/testLogin.jsp";
123        private static final String TEST_RESULTS_VIEW = "/WEB-INF/view/realmwizard/testResults.jsp";
124        private static final String SHOW_PLAN_VIEW = "/WEB-INF/view/realmwizard/showPlan.jsp";
125        private static final String USAGE_VIEW = "/WEB-INF/view/realmwizard/usage.jsp";
126        private static final String LIST_MODE = "list";
127        private static final String EDIT_MODE = "edit";
128        private static final String SELECT_TYPE_MODE = "type";
129        private static final String CONFIGURE_MODE = "configure";
130        private static final String ADVANCED_MODE = "advanced";
131        private static final String TEST_LOGIN_MODE = "test";
132        private static final String TEST_RESULTS_MODE = "results";
133        private static final String SHOW_PLAN_MODE = "plan";
134        private static final String EDIT_EXISTING_MODE = "editExisting";
135        private static final String USAGE_MODE = "usage";
136        private static final String SAVE_MODE = "save";
137        private static final String MODE_KEY = "mode";
138        private static final String CUSTOM_MODE = "custom";
139    
140        private static Kernel kernel;
141    
142        private PortletRequestDispatcher listView;
143        private PortletRequestDispatcher editView;
144        private PortletRequestDispatcher selectTypeView;
145        private PortletRequestDispatcher configureView;
146        private PortletRequestDispatcher advancedView;
147        private PortletRequestDispatcher testLoginView;
148        private PortletRequestDispatcher testResultsView;
149        private PortletRequestDispatcher planView;
150        private PortletRequestDispatcher usageView;
151        private static final QName GBEAN_QNAME = new QName(ServiceDocument.type.getDocumentElementName().getNamespaceURI(), "gbean");
152    
153        public void init(PortletConfig portletConfig) throws PortletException {
154            super.init(portletConfig);
155            kernel = KernelRegistry.getSingleKernel();
156            listView = portletConfig.getPortletContext().getRequestDispatcher(LIST_VIEW);
157            editView = portletConfig.getPortletContext().getRequestDispatcher(EDIT_VIEW);
158            selectTypeView = portletConfig.getPortletContext().getRequestDispatcher(SELECT_TYPE_VIEW);
159            configureView = portletConfig.getPortletContext().getRequestDispatcher(CONFIGURE_VIEW);
160            advancedView = portletConfig.getPortletContext().getRequestDispatcher(ADVANCED_VIEW);
161            testLoginView = portletConfig.getPortletContext().getRequestDispatcher(TEST_LOGIN_VIEW);
162            testResultsView = portletConfig.getPortletContext().getRequestDispatcher(TEST_RESULTS_VIEW);
163            planView = portletConfig.getPortletContext().getRequestDispatcher(SHOW_PLAN_VIEW);
164            usageView = portletConfig.getPortletContext().getRequestDispatcher(USAGE_VIEW);
165        }
166    
167        public void destroy() {
168            listView = null;
169            editView = null;
170            selectTypeView = null;
171            configureView = null;
172            advancedView = null;
173            testLoginView = null;
174            usageView = null;
175            planView = null;
176            super.destroy();
177        }
178    
179        public void processAction(ActionRequest actionRequest,
180                ActionResponse actionResponse) throws PortletException, IOException {
181            String mode = actionRequest.getParameter(MODE_KEY);
182            RealmData data = new RealmData();
183            data.load(actionRequest);
184            if (mode.equals(SELECT_TYPE_MODE)) {
185                data.realmType = "Properties File Realm";
186                actionResponse.setRenderParameter(MODE_KEY, SELECT_TYPE_MODE);
187            } else if (mode.equals("process-" + SELECT_TYPE_MODE)) {
188                if (data.getName() != null && !data.getName().trim().equals("")) {
189                    // Config properties have to be set in render since they have values of null
190                    if (data.getRealmType().equals("Other")) {
191                        actionResponse.setRenderParameter(MODE_KEY, CUSTOM_MODE);
192                    } else {
193                        actionResponse.setRenderParameter(MODE_KEY, CONFIGURE_MODE);
194                    }
195                } else {
196                    actionResponse.setRenderParameter(MODE_KEY, SELECT_TYPE_MODE);
197                }
198            } else if (mode.equals("process-" + CONFIGURE_MODE)) {
199                final String error = actionTestLoginModuleLoad(actionRequest, data);
200                if (error == null) {
201                    actionResponse.setRenderParameter(MODE_KEY, ADVANCED_MODE);
202                } else {
203                    actionResponse.setRenderParameter("LoginModuleError", error);
204                    actionResponse.setRenderParameter(MODE_KEY, CONFIGURE_MODE);
205                }
206            } else if (mode.equals("process-" + ADVANCED_MODE)) {
207                String test = actionRequest.getParameter("test");
208                if (test == null || test.equals("true")) {
209                    actionResponse.setRenderParameter(MODE_KEY, TEST_LOGIN_MODE);
210                } else {
211                    actionSaveRealm(actionRequest, data);
212                    actionResponse.setRenderParameter(MODE_KEY, LIST_MODE);
213                }
214            } else if (mode.equals("process-" + TEST_LOGIN_MODE)) {
215                actionAttemptLogin(data, actionRequest, actionRequest.getPortletSession(true), actionRequest.getParameter("username"), actionRequest.getParameter("password"));
216                actionResponse.setRenderParameter(MODE_KEY, TEST_RESULTS_MODE);
217            } else if (mode.equals(SHOW_PLAN_MODE)) {
218                XmlObject object = actionGeneratePlan(actionRequest, data);
219                savePlanToSession(actionRequest.getPortletSession(true), object);
220                actionResponse.setRenderParameter(MODE_KEY, SHOW_PLAN_MODE);
221            } else if (mode.equals(EDIT_EXISTING_MODE)) {
222                actionLoadExistingRealm(actionRequest, data);
223                actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
224            } else if (mode.equals(CONFIGURE_MODE)) {
225                if (data.getAbstractName() != null) {
226                    actionResponse.setRenderParameter(MODE_KEY, EDIT_MODE);
227                } else if((data.getRealmType() != null && data.getRealmType().equals("Other"))) {
228                    actionResponse.setRenderParameter(MODE_KEY, CUSTOM_MODE);
229                } else {
230                    actionResponse.setRenderParameter(MODE_KEY, CONFIGURE_MODE);
231                }
232            } else if (mode.equals(SAVE_MODE)) {
233                actionSaveRealm(actionRequest, data);
234                actionResponse.setRenderParameter(MODE_KEY, LIST_MODE);
235            } else {
236                actionResponse.setRenderParameter(MODE_KEY, mode);
237            }
238            data.store(actionResponse);
239        }
240    
241        protected void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
242            if (WindowState.MINIMIZED.equals(renderRequest.getWindowState())) {
243                return;
244            }
245            try {
246                String mode = renderRequest.getParameter(MODE_KEY);
247                RealmData data = new RealmData();
248                data.load(renderRequest);
249                renderRequest.setAttribute("realm", data);
250                if (mode == null || mode.equals("")) {
251                    mode = LIST_MODE;
252                }
253                if (mode.equals(LIST_MODE)) {
254                    renderList(renderRequest, renderResponse);
255                } else if (mode.equals(EDIT_MODE) || mode.equals(CUSTOM_MODE)) {
256                    renderRequest.setAttribute("mode", mode);
257                    if(mode.equals(CUSTOM_MODE)) loadDriverJARList(renderRequest);
258                    renderEdit(renderRequest, renderResponse, data);
259                } else if (mode.equals(SELECT_TYPE_MODE)) {
260                    renderSelectType(renderRequest, renderResponse);
261                } else if (mode.equals(CONFIGURE_MODE)) {
262                    renderConfigure(renderRequest, renderResponse, data);
263                } else if (mode.equals(ADVANCED_MODE)) {
264                    renderAdvanced(renderRequest, renderResponse, data);
265                } else if (mode.equals(TEST_LOGIN_MODE)) {
266                    renderTestLoginForm(renderRequest, renderResponse);
267                } else if (mode.equals(TEST_RESULTS_MODE)) {
268                    renderTestResults(renderRequest, renderResponse);
269                } else if (mode.equals(SHOW_PLAN_MODE)) {
270                    renderPlan(renderRequest, renderResponse);
271                } else if (mode.equals(USAGE_MODE)) {
272                    renderUsage(renderRequest, renderResponse);
273                }
274            } catch (Throwable e) {
275                log.error("Unable to render portlet", e);
276            }
277        }
278    
279        private String actionTestLoginModuleLoad(PortletRequest request, RealmData data) {
280            Map options = new HashMap();
281            try {
282                LoginModule module = loadModule(request, data, options);
283                log.warn("Testing with options " + options);
284                try {
285                    PortletManager.testLoginModule(request, module, options);
286                    return null;
287                } catch (Exception e) {
288                    log.warn("Unable to initialize LoginModule", e);
289                    return "Unable to initialize LoginModule: " + e.getMessage();
290                }
291            } catch (Exception e) {
292                log.warn("Unable to load LoginModule class", e);
293                return "Unable to load LoginModule class: " + e.getMessage();
294            }
295        }
296    
297        private LoginModule loadModule(PortletRequest request, RealmData data, Map options) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
298            ClassLoader loader = getClass().getClassLoader();
299            if (data.jar != null && !data.jar.equals("")) {
300                try {
301                    Artifact one = Artifact.create(data.getJar());
302                    ListableRepository[] repos = PortletManager.getCurrentServer(request).getRepositories();
303                    for (int i = 0; i < repos.length; i++) {
304                        ListableRepository repo = repos[i];
305                        File file = repo.getLocation(one);
306                        if (file != null) {
307                            loader = new URLClassLoader(new URL[]{file.toURL()}, loader);
308                            break;
309                        }
310                    }
311                } catch (MalformedURLException e) {
312                    log.warn("Repository unable to look up JAR file", e);
313                }
314            }
315            Class cls = loader.loadClass(getSelectedModule(data).getClassName());
316            LoginModule module = (LoginModule) cls.newInstance();
317            for (Iterator it = data.getOptions().keySet().iterator(); it.hasNext();) {
318                String key = (String) it.next();
319                final Object value = data.getOptions().get(key);
320                if (value != null && !value.equals("")) {
321                    options.put(key, value);
322                }
323            }
324            options.put(JaasLoginModuleUse.CLASSLOADER_LM_OPTION, loader);
325            return module;
326        }
327    
328        private void actionAttemptLogin(RealmData data, PortletRequest request, PortletSession session, String username, String password) {
329            session.removeAttribute("TestLoginPrincipals");
330            session.removeAttribute("TestLoginError");
331            Map options = new HashMap();
332            try {
333                LoginModule module = loadModule(request, data, options);
334                Subject sub = PortletManager.testLoginModule(request, module, options, username, password);
335                session.setAttribute("TestLoginPrincipals", sub.getPrincipals());
336            } catch (Exception e) {
337                log.warn("Test login failed", e);
338                session.setAttribute("TestLoginError", "Login Failed: " + (e.getMessage() == null ? "no message" : e.getMessage()));
339            }
340        }
341    
342        private XmlObject actionGeneratePlan(PortletRequest request, RealmData data) {
343            normalize(data);
344            ModuleDocument doc = ModuleDocument.Factory.newInstance();
345            ModuleType root = doc.addNewModule();
346            EnvironmentType environment = root.addNewEnvironment();
347            ArtifactType configId = environment.addNewModuleId();
348            configId.setGroupId("console.realm");
349            String artifactId = data.getName();
350            if(artifactId.indexOf('/') != -1) {
351                // slash in artifact-id results in invalid configuration-id and leads to deployment errors.
352                // Note: 0x002F = '/'
353                artifactId = artifactId.replaceAll("/", "%2F");
354            }
355            configId.setArtifactId(artifactId);
356            configId.setVersion("1.0");
357            configId.setType("car");
358    
359            // Parent
360    
361            DependenciesType dependenciesType = environment.addNewDependencies();
362            ArtifactType parent = dependenciesType.addNewDependency();
363            parent.setGroupId("org.apache.geronimo.framework");
364            parent.setArtifactId("j2ee-security");
365            parent.setType("car");
366            // Dependencies
367            if (data.getJar() != null) {
368                ArtifactType artifactType = dependenciesType.addNewDependency();
369                Artifact artifact = Artifact.create(data.getJar());
370                artifactType.setGroupId(artifact.getGroupId());
371                artifactType.setArtifactId(artifact.getArtifactId());
372                artifactType.setVersion(artifact.getVersion().toString());
373                artifactType.setType(artifact.getType());
374            }
375            // Build the realm GBean
376            GbeanType realm = GbeanType.Factory.newInstance();
377            realm.setName(data.getName());
378            realm.setClass1("org.apache.geronimo.security.realm.GenericSecurityRealm");
379            AttributeType realmName = realm.addNewAttribute();
380            realmName.setName("realmName");
381            realmName.setStringValue(data.getName());
382            ReferenceType serverInfo = realm.addNewReference();
383            serverInfo.setName2("ServerInfo");
384            serverInfo.setName((String) PortletManager.getNameFor(request, PortletManager.getCurrentServer(request).getServerInfo()).getName().get("name"));
385            XmlAttributeType config = realm.addNewXmlReference();
386            // Construct the content to put in the XmlAttributeType
387            GerLoginConfigDocument lcDoc = GerLoginConfigDocument.Factory.newInstance();
388            GerLoginConfigType login = lcDoc.addNewLoginConfig();
389            for (int i = 0; i < data.getModules().length; i++) {
390                LoginModuleDetails details = data.getModules()[i];
391                if (details.getLoginDomainName() == null || details.getLoginDomainName().equals("")) {
392                    continue;
393                }
394                GerLoginModuleType module = login.addNewLoginModule();
395                module.setControlFlag(details.getControlFlag().equals(LoginModuleControlFlag.OPTIONAL) ? GerControlFlagType.OPTIONAL :
396                        details.getControlFlag().equals(LoginModuleControlFlag.REQUIRED) ? GerControlFlagType.REQUIRED :
397                                details.getControlFlag().equals(LoginModuleControlFlag.REQUISITE) ? GerControlFlagType.REQUISITE :
398                                        details.getControlFlag().equals(LoginModuleControlFlag.SUFFICIENT) ? GerControlFlagType.SUFFICIENT :
399                                                GerControlFlagType.OPTIONAL);
400                module.setLoginDomainName(details.getLoginDomainName());
401                module.setLoginModuleClass(details.getClassName());
402                module.setWrapPrincipals(details.isWrapPrincipals());
403                for (Iterator it = details.getOptions().entrySet().iterator(); it.hasNext();) {
404                    Map.Entry entry = (Map.Entry) it.next();
405                    GerOptionType option = module.addNewOption();
406                    option.setName((String) entry.getKey());
407                    option.setStringValue((String) entry.getValue());
408                }
409    
410                // bit of a hack -- to put the DataSource module in as a parent for SQL modules
411                if (details.getClassName().indexOf("SQL") > -1) {
412                    String poolName = (String) details.getOptions().get("dataSourceName");
413                    String appName = (String) details.getOptions().get("dataSourceApplication");
414                    if (poolName != null) {
415                        if (appName == null) appName = "null";
416                        JCAManagedConnectionFactory[] factories = PortletManager.getOutboundFactoriesOfType(request, "javax.sql.DataSource");
417                        for (int j = 0; j < factories.length; j++) {
418                            JCAManagedConnectionFactory factory = factories[j];
419                            try {
420                                ObjectName objectName = ObjectName.getInstance(factory.getObjectName());
421                                final String testName = objectName.getKeyProperty(NameFactory.J2EE_NAME);
422                                final String testApp = objectName.getKeyProperty(NameFactory.J2EE_APPLICATION);
423                                if (testName.equals(poolName) && testApp.equals(appName)) {
424                                    String moduleName = objectName.getKeyProperty(NameFactory.JCA_RESOURCE);
425    
426                                    ArtifactType artifactType = dependenciesType.addNewDependency();
427                                    Artifact artifact = Artifact.create(moduleName);
428                                    artifactType.setGroupId(artifact.getGroupId());
429                                    artifactType.setArtifactId(artifact.getArtifactId());
430                                    artifactType.setVersion(artifact.getVersion().toString());
431                                    artifactType.setType(artifact.getType());
432                                    break;
433                                }
434                            } catch (MalformedObjectNameException e) {
435                                log.error("Unable to parse ObjectName", e);
436                            }
437                        }
438                    }
439                }
440            }
441            // Copy the content into the XmlAttributeType
442            XmlCursor loginCursor = lcDoc.newCursor();
443            loginCursor.toFirstContentToken();
444            XmlCursor destination = config.newCursor();
445            destination.toNextToken();
446            loginCursor.moveXml(destination);
447            loginCursor.dispose();
448            destination.dispose();
449            config.setName("LoginModuleConfiguration");
450            root.setServiceArray(new AbstractServiceType[]{realm});
451    
452    
453            //Above code inserts gbean using xsi:type=dep:GBeanType.  We also need to account for the substitution group
454            //by changing the qname:
455            XmlCursor gbeanCursor = root.newCursor();
456            try {
457                if (!gbeanCursor.toChild(ServiceDocument.type.getDocumentElementName())) {
458                    throw new RuntimeException("Could not find service element");
459                }
460                gbeanCursor.setName(GBEAN_QNAME);
461            } finally {
462                gbeanCursor.dispose();
463            }
464    
465            return doc;
466        }
467    
468        private void actionLoadExistingRealm(PortletRequest request, RealmData data) {
469            SecurityRealm realm = (SecurityRealm) PortletManager.getManagedBean(request, new AbstractName(URI.create(data.getAbstractName())));
470            data.name = realm.getRealmName();
471            List list = new ArrayList();
472            JaasLoginModuleChain node = realm.getLoginModuleChain();
473            while (node != null) {
474                LoginModuleDetails details = new LoginModuleDetails();
475                details.setControlFlag(node.getControlFlag());
476                LoginModuleSettings module = node.getLoginModule();
477                details.setLoginDomainName(module.getLoginDomainName());
478                details.setClassName(module.getLoginModuleClass());
479                details.setWrapPrincipals(module.isWrapPrincipals());
480                details.setOptions(module.getOptions());
481                list.add(details);
482                node = node.getNext();
483                if (node == null) {
484                    break;
485                }
486            }
487            data.modules = (LoginModuleDetails[]) list.toArray(new LoginModuleDetails[list.size()]);
488        }
489    
490        private void actionSaveRealm(PortletRequest request, RealmData data) {
491            normalize(data);
492            if (data.getAbstractName() == null || data.getAbstractName().equals("")) { // we're creating a new realm
493                try {
494                    XmlObject plan = actionGeneratePlan(request, data);
495                    data.name = data.name.replaceAll("\\s", "");
496                    DeploymentManager mgr = ManagementHelper.getManagementHelper(request).getDeploymentManager();
497                    File tempFile = File.createTempFile("console-deployment", ".xml");
498                    tempFile.deleteOnExit();
499                    log.debug("Writing security realm deployment plan to " + tempFile.getAbsolutePath());
500                    PrintWriter out = new PrintWriter(new FileWriter(tempFile));
501                    savePlanToStream(plan, out);
502                    out.flush();
503                    out.close();
504                    Target[] targets = mgr.getTargets();
505                    if (null == targets) {
506                        throw new IllegalStateException("No target to distribute to");
507                    }
508                    targets = new Target[] {targets[0]};
509                    
510                    ProgressObject po = mgr.distribute(targets, null, tempFile);
511                    waitForProgress(po);
512                    if (po.getDeploymentStatus().isCompleted()) {
513                        TargetModuleID[] ids = po.getResultTargetModuleIDs();
514                        po = mgr.start(ids);
515                        waitForProgress(po);
516                        if (po.getDeploymentStatus().isCompleted()) {
517                            log.info("Deployment completed successfully!");
518                        }
519                    }
520                } catch (IOException e) {
521                    log.error("Unable to save security realm", e);
522                }
523            } else {
524                SecurityRealm realm = (SecurityRealm) PortletManager.getManagedBean(request, new AbstractName(URI.create(data.getAbstractName())));
525                // index existing modules
526                Map nodes = new HashMap();
527                JaasLoginModuleChain node = realm.getLoginModuleChain();
528                while (node != null) {
529                    LoginModuleSettings module = node.getLoginModule();
530                    nodes.put(module.getLoginDomainName(), node);
531                    node = node.getNext();
532                    if (node == null) {
533                        break;
534                    }
535                }
536                // apply settings
537                for (int i = 0; i < data.getModules().length; i++) {
538                    LoginModuleDetails details = data.getModules()[i];
539                    node = (JaasLoginModuleChain) nodes.get(details.getLoginDomainName());
540                    node.setControlFlag(details.getControlFlag());
541                    LoginModuleSettings module = node.getLoginModule();
542                    module.setOptions(details.getOptions());
543                    module.setWrapPrincipals(details.isWrapPrincipals());
544                    module.setLoginModuleClass(details.getClassName());
545                }
546            }
547        }
548    
549        private void renderList(RenderRequest request, RenderResponse response) throws IOException, PortletException {
550            // Unfortunately there are two classes named SecurityRealm; one extends the other
551            // The array type is management.geronimo.SecurityRealm (the superclass)
552            // The array entry types are security.realm.SecurityRealm (the subclass)
553            org.apache.geronimo.management.geronimo.SecurityRealm[] realms = PortletManager.getCurrentServer(request).getSecurityRealms();
554            ExistingRealm[] results = new ExistingRealm[realms.length];
555    
556            // ConfigurationManager is used to determine if the SecurityRealm is deployed as a "SERVICE", i.e., "Server-wide"
557            ConfigurationManager configMgr = null;
558            if(results.length > 0) {
559                // Needed only when there are any SecurityRealms
560                configMgr = ConfigurationUtil.getConfigurationManager(kernel);
561            }
562            for (int i = 0; i < results.length; i++) {
563                AbstractName abstractName = PortletManager.getNameFor(request, realms[i]);
564                String parent;
565                Configuration parentConfig = configMgr.getConfiguration(abstractName.getArtifact());
566                ConfigurationModuleType parentType = parentConfig.getModuleType();
567                if(ConfigurationModuleType.SERVICE.equals(parentType)) {
568                    parent = null; // Server-wide
569                } else {
570                    parent = abstractName.getArtifact().toString();
571                }
572                results[i] = new ExistingRealm(realms[i].getRealmName(), abstractName, parent);
573            }
574            // Once done, release the ConfigurationManager
575            if(configMgr != null) {
576                ConfigurationUtil.releaseConfigurationManager(kernel, configMgr);
577            }
578            request.setAttribute("realms", results);
579            listView.include(request, response);
580        }
581    
582        private void renderEdit(RenderRequest request, RenderResponse response, RealmData data) throws IOException, PortletException {
583            normalize(data);
584            editView.include(request, response);
585        }
586    
587        private void renderSelectType(RenderRequest request, RenderResponse response) throws IOException, PortletException {
588            request.setAttribute("moduleTypes", MasterLoginModuleInfo.getAllModules());
589            selectTypeView.include(request, response);
590        }
591    
592        private void renderConfigure(RenderRequest request, RenderResponse response, RealmData data) throws IOException, PortletException {
593            // Pass errors through
594            if (request.getParameter("LoginModuleError") != null) {
595                request.setAttribute("LoginModuleError", request.getParameter("LoginModuleError"));
596            }
597            // Clear out any cached modules
598            data.modules = null;
599            // Configure option list
600            MasterLoginModuleInfo info = getSelectedModule(data);
601            for (int i = 0; i < info.getOptions().length; i++) {
602                MasterLoginModuleInfo.OptionInfo option = info.getOptions()[i];
603                if (!data.getOptions().containsKey(option.getName())) {
604                    data.getOptions().put(option.getName(), null);
605                }
606            }
607            data.reorderOptions(info.getOptions());
608            request.setAttribute("optionMap", info.getOptionMap());
609            if (info.getName().indexOf("SQL") > -1) {
610                loadDriverJARList(request);
611                loadDatabasePoolList(request);
612            }
613            configureView.include(request, response);
614        }
615    
616        private void renderAdvanced(RenderRequest request, RenderResponse response, RealmData data) throws IOException, PortletException {
617            // Clear out any cached modules
618            data.modules = null;
619            // Show the page
620            advancedView.include(request, response);
621        }
622    
623        private void renderTestLoginForm(RenderRequest request, RenderResponse response) throws IOException, PortletException {
624            testLoginView.include(request, response);
625        }
626    
627        private void renderTestResults(RenderRequest request, RenderResponse response) throws IOException, PortletException {
628            PortletSession session = request.getPortletSession();
629            String status = (String) session.getAttribute("TestLoginError");
630            if (status == null) {
631                Set principals = (Set) session.getAttribute("TestLoginPrincipals");
632                status = "Login succeeded with " + (principals == null ? 0 : principals.size()) + " principals";
633                request.setAttribute("principals", principals);
634            }
635            request.setAttribute("LoginResults", status);
636            testResultsView.include(request, response);
637        }
638    
639        private void renderPlan(RenderRequest request, RenderResponse response) throws IOException, PortletException {
640            String plan = (String) request.getPortletSession().getAttribute("SecurityRealmPlan");
641            request.setAttribute("deploymentPlan", plan);
642            planView.include(request, response);
643        }
644    
645        private void renderUsage(RenderRequest request, RenderResponse response) throws IOException, PortletException {
646            usageView.include(request, response);
647        }
648    
649        private static MasterLoginModuleInfo getSelectedModule(RealmData data) {
650            MasterLoginModuleInfo[] all = MasterLoginModuleInfo.getAllModules();
651            for (int i = 0; i < all.length; i++) {
652                MasterLoginModuleInfo info = all[i];
653                if (info.getName().equals(data.getRealmType())) {
654                    return info;
655                }
656            }
657            return null;
658        }
659    
660        private void loadDatabasePoolList(RenderRequest renderRequest) {
661            JCAManagedConnectionFactory[] factories = PortletManager.getOutboundFactoriesOfType(renderRequest, "javax.sql.DataSource");
662            List pools = new ArrayList();
663            try {
664                for (int i = 0; i < factories.length; i++) {
665                    JCAManagedConnectionFactory factory = factories[i];
666                    ObjectName objectName = ObjectName.getInstance(factory.getObjectName());
667                    final String name = objectName.getKeyProperty(NameFactory.J2EE_NAME);
668                    String display = name;
669                    final String appName = objectName.getKeyProperty(NameFactory.J2EE_APPLICATION);
670                    if (appName != null && !appName.equals("null")) {
671                        display = display + " (" + appName + ")";
672                    }
673                    pools.add(new DatabasePool(name, display, appName, PortletManager.getNameFor(renderRequest, factory)));
674                }
675                renderRequest.setAttribute("pools", pools);
676            } catch (MalformedObjectNameException e) {
677                log.error("Unable to parse ObjectName", e);
678            }
679        }
680    
681        private void loadDriverJARList(RenderRequest renderRequest) {
682            // List the available JARs
683            List list = new ArrayList();
684            ListableRepository[] repos = PortletManager.getCurrentServer(renderRequest).getRepositories();
685            for (int i = 0; i < repos.length; i++) {
686                ListableRepository repo = repos[i];
687    
688                SortedSet artifacts = repo.list();
689                outer:
690                for (Iterator iterator = artifacts.iterator(); iterator.hasNext();) {
691                    Artifact artifact = (Artifact) iterator.next();
692                    String test = artifact.toString();
693                    // todo should only test groupId and should check for long (org.apache.geronimo) and short form
694                    for (int k = 0; k < SKIP_ENTRIES_WITH.length; k++) {
695                        String skip = SKIP_ENTRIES_WITH[k];
696                        if (test.indexOf(skip) > -1) {
697                            continue outer;
698                        }
699                    }
700                    list.add(test);
701                }
702            }
703            Collections.sort(list);
704            renderRequest.setAttribute("jars", list);
705        }
706    
707        private void savePlanToSession(PortletSession session, XmlObject object) {
708            StringWriter out = new StringWriter();
709            try {
710                savePlanToStream(object, out);
711                session.setAttribute("SecurityRealmPlan", out.getBuffer().toString());
712            } catch (IOException e) {
713                log.error("Unable to write deployment plan", e);
714            }
715        }
716    
717        private void savePlanToStream(XmlObject object, Writer out) throws IOException {
718            XmlOptions options = new XmlOptions();
719            options.setSavePrettyPrint();
720            options.setSavePrettyPrintIndent(4);
721            options.setUseDefaultNamespace();
722            object.save(out, options);
723            out.close();
724        }
725    
726        private static void waitForProgress(ProgressObject po) {
727            while (po.getDeploymentStatus().isRunning()) {
728                try {
729                    Thread.sleep(100);
730                } catch (InterruptedException e) {
731                    log.error(e.getMessage(), e);
732                }
733            }
734        }
735    
736        public static void normalize(RealmData data) {
737            List list = new ArrayList();
738            if (data.modules == null) {
739                LoginModuleDetails module = new LoginModuleDetails();
740                module.setClassName(getSelectedModule(data).getClassName());
741                module.setControlFlag(LoginModuleControlFlag.REQUIRED);
742                module.setLoginDomainName(data.getName());
743                Map<String, Object> props = module.getOptions();
744                for (Iterator it = data.getOptions().entrySet().iterator(); it.hasNext();) {
745                    Map.Entry entry = (Map.Entry) it.next();
746                    props.put((String) entry.getKey(), (String) entry.getValue());
747                }
748                list.add(module);
749                if (data.isStorePassword()) {
750                    module = new LoginModuleDetails();
751                    module.setClassName(GeronimoPasswordCredentialLoginModule.class.getName());
752                    module.setControlFlag(LoginModuleControlFlag.OPTIONAL);
753                    module.setLoginDomainName(data.getName() + "-Password");
754                    list.add(module);
755                }
756                if (data.getAuditPath() != null) {
757                    module = new LoginModuleDetails();
758                    module.setClassName(FileAuditLoginModule.class.getName());
759                    module.setControlFlag(LoginModuleControlFlag.OPTIONAL);
760                    module.setLoginDomainName(data.getName() + "-Audit");
761                    props = module.getOptions();
762                    props.put("file", data.getAuditPath());
763                    list.add(module);
764                }
765                if (data.isLockoutEnabled()) {
766                    module = new LoginModuleDetails();
767                    module.setClassName(RepeatedFailureLockoutLoginModule.class.getName());
768                    module.setControlFlag(LoginModuleControlFlag.REQUISITE);
769                    module.setLoginDomainName(data.getName() + "-Lockout");
770                    props = module.getOptions();
771                    props.put("failureCount", data.getLockoutCount());
772                    props.put("failurePeriodSecs", data.getLockoutWindow());
773                    props.put("lockoutDurationSecs", data.getLockoutDuration());
774                    list.add(module);
775                }
776                if (data.getCredentialName() != null) {
777                    module = new LoginModuleDetails();
778                    module.setClassName(NamedUsernamePasswordCredentialLoginModule.class.getName());
779                    module.setControlFlag(LoginModuleControlFlag.OPTIONAL);
780                    module.setLoginDomainName(data.getName() + "-NamedUPC");
781                    props = module.getOptions();
782                    props.put(NamedUsernamePasswordCredentialLoginModule.CREDENTIAL_NAME, data.getCredentialName());
783                    list.add(module);
784                }
785            } else {
786                list.addAll(Arrays.asList(data.modules));
787            }
788            if (data.getAbstractName() == null) {
789                for (int i = list.size(); i < 5; i++) {
790                    LoginModuleDetails module = new LoginModuleDetails();
791                    list.add(module);
792                }
793            }
794            data.modules = (LoginModuleDetails[]) list.toArray(new LoginModuleDetails[list.size()]);
795        }
796    
797        public static class RealmData implements Serializable {
798            private String name;
799            private String realmType;
800            private String jar;
801            private Map options = new LinkedHashMap();
802            private String auditPath;
803            private String lockoutCount;
804            private String lockoutWindow;
805            private String lockoutDuration;
806            private boolean storePassword;
807            private String abstractName; // used when editing existing realms
808            private LoginModuleDetails[] modules;
809            private String credentialName;
810    
811            public void load(PortletRequest request) {
812                name = request.getParameter("name");
813                if (name != null && name.equals("")) name = null;
814                realmType = request.getParameter("realmType");
815                if (realmType != null && realmType.equals("")) realmType = null;
816                jar = request.getParameter("jar");
817                if (jar != null && jar.equals("")) jar = null;
818                auditPath = request.getParameter("auditPath");
819                if (auditPath != null && auditPath.equals("")) auditPath = null;
820                lockoutCount = request.getParameter("lockoutCount");
821                if (lockoutCount != null && lockoutCount.equals("")) lockoutCount = null;
822                lockoutWindow = request.getParameter("lockoutWindow");
823                if (lockoutWindow != null && lockoutWindow.equals("")) lockoutWindow = null;
824                lockoutDuration = request.getParameter("lockoutDuration");
825                if (lockoutDuration != null && lockoutDuration.equals("")) lockoutDuration = null;
826                abstractName = request.getParameter("abstractName");
827                if (abstractName != null && abstractName.equals("")) abstractName = null;
828                String test = request.getParameter("storePassword");
829                storePassword = test != null && !test.equals("") && !test.equals("false");
830                credentialName = request.getParameter("credentialName");
831                if (credentialName != null && credentialName.equals("")) credentialName = null;
832                Map map = request.getParameterMap();
833                for (Iterator it = map.keySet().iterator(); it.hasNext();) {
834                    String key = (String) it.next();
835                    if (key.startsWith("option-")) {
836                        if (key.equals("option-databasePoolAbstractName"))
837                        { // special handling for a data source, where there's one select corresponding to two properties
838                            String nameString = request.getParameter(key);
839                            if (nameString != null && !nameString.equals("")) {
840                                AbstractName an = new AbstractName(URI.create(nameString));
841                                options.put("dataSourceName", an.getNameProperty(NameFactory.J2EE_NAME));
842                                options.put("dataSourceApplication", an.getNameProperty(NameFactory.J2EE_APPLICATION));
843                            }
844                        } else {
845                            final String optionName = key.substring(7);
846                            final String value = request.getParameter(key);
847                            if (value != null && !value.equals("")) {
848                                options.put(optionName, value);
849                            }
850                        }
851                    }
852                }
853                int count = 0;
854                List list = new ArrayList();
855                while (true) {
856                    int index = count;
857                    ++count;
858                    String name = request.getParameter("module-domain-" + index);
859                    if (name == null || name.equals("")) break;
860                    LoginModuleDetails details = new LoginModuleDetails();
861                    details.setLoginDomainName(name);
862                    String cls = request.getParameter("module-class-" + index);
863                    if (cls == null || cls.equals("")) continue;
864                    details.setClassName(cls);
865                    String flag = request.getParameter("module-control-" + index);
866                    if (flag == null || flag.equals("")) continue;
867                    details.setControlFlag(toFlag(flag));
868                    String wrap = request.getParameter("module-wrap-" + index);
869                    if (wrap == null || wrap.equals("")) continue;
870                    details.setWrapPrincipals(Boolean.valueOf(wrap).booleanValue());
871                    String options = request.getParameter("module-options-" + index);
872                    if (options != null && !options.equals("")) {
873                        BufferedReader in = new BufferedReader(new StringReader(options));
874                        String line;
875                        try {
876                            while ((line = in.readLine()) != null) {
877                                if (line.startsWith("#") || line.equals("")) {
878                                    continue;
879                                }
880                                int pos = line.indexOf('=');
881                                if (pos > -1) {
882                                    details.getOptions().put(line.substring(0, pos), line.substring(pos + 1));
883                                }
884                            }
885                        } catch (IOException e) {
886                            log.error("Unable to read properties '" + options + "'", e);
887                        }
888                    }
889                    list.add(details);
890                }
891                if (list.size() > 0) {
892                    modules = (LoginModuleDetails[]) list.toArray(new LoginModuleDetails[list.size()]);
893                }
894            }
895    
896            private LoginModuleControlFlag toFlag(String flag) {
897                LoginModuleControlFlagEditor editor = new LoginModuleControlFlagEditor();
898                editor.setAsText(flag);
899                return (LoginModuleControlFlag) editor.getValue();
900            }
901    
902            public void reorderOptions(MasterLoginModuleInfo.OptionInfo[] info) {
903                if (info == null || info.length == 0) {
904                    return; // Probably SQL or something that handles this manually
905                }
906                Map map = new LinkedHashMap();
907                for (int i = 0; i < info.length; i++) {
908                    if (options.containsKey(info[i].getName())) {
909                        map.put(info[i].getName(), options.get(info[i].getName()));
910                    }
911                }
912                options = map;
913            }
914    
915            public void store(ActionResponse response) {
916                if (name != null) response.setRenderParameter("name", name);
917                if (realmType != null) response.setRenderParameter("realmType", realmType);
918                if (jar != null) response.setRenderParameter("jar", jar);
919                if (auditPath != null) response.setRenderParameter("auditPath", auditPath);
920                if (lockoutCount != null) response.setRenderParameter("lockoutCount", lockoutCount);
921                if (lockoutWindow != null) response.setRenderParameter("lockoutWindow", lockoutWindow);
922                if (lockoutDuration != null) response.setRenderParameter("lockoutDuration", lockoutDuration);
923                if (abstractName != null) response.setRenderParameter("abstractName", abstractName);
924                if (storePassword) response.setRenderParameter("storePassword", "true");
925                if (credentialName != null) response.setRenderParameter("credentialName", credentialName);
926                for (Iterator it = options.keySet().iterator(); it.hasNext();) {
927                    String name = (String) it.next();
928                    String value = (String) options.get(name);
929                    if (value != null) {
930                        response.setRenderParameter("option-" + name, value);
931                    }
932                }
933                if (modules != null) {
934                    for (int i = 0; i < modules.length; i++) {
935                        LoginModuleDetails module = modules[i];
936                        if (module.getLoginDomainName() != null)
937                            response.setRenderParameter("module-domain-" + i, module.getLoginDomainName());
938                        if (module.getClassName() != null)
939                            response.setRenderParameter("module-class-" + i, module.getClassName());
940                        if (module.getControlFlag() != null)
941                            response.setRenderParameter("module-control-" + i,module.getControlFlag().toString());
942                        response.setRenderParameter("module-wrap-" + i, Boolean.toString(module.isWrapPrincipals()));
943                        if (module.getOptions().size() > 0)
944                            response.setRenderParameter("module-options-" + i, module.getOptionString());
945                    }
946                }
947            }
948    
949            public String getName() {
950                return name;
951            }
952    
953            public String getRealmType() {
954                return realmType;
955            }
956    
957            public Map getOptions() {
958                return options;
959            }
960    
961            public Set getOptionNames() {
962                return options.keySet();
963            }
964    
965            public String getJar() {
966                return jar;
967            }
968    
969            public String getAuditPath() {
970                return auditPath;
971            }
972    
973            public String getLockoutCount() {
974                return lockoutCount;
975            }
976    
977            public String getLockoutWindow() {
978                return lockoutWindow;
979            }
980    
981            public String getLockoutDuration() {
982                return lockoutDuration;
983            }
984    
985            public boolean isStorePassword() {
986                return storePassword;
987            }
988    
989            public boolean isLockoutEnabled() {
990                return lockoutCount != null || lockoutWindow != null || lockoutDuration != null;
991            }
992    
993            public String getCredentialName() {
994                return credentialName;
995            }
996    
997            public String getAbstractName() {
998                return abstractName;
999            }
1000    
1001            public boolean isTestable() {
1002                return getSelectedModule(this).isTestable();
1003            }
1004    
1005            public LoginModuleDetails[] getModules() {
1006                return modules;
1007            }
1008        }
1009    
1010        public static class LoginModuleDetails implements Serializable {
1011            private String loginDomainName;
1012            private String className;
1013            private LoginModuleControlFlag controlFlag;
1014            private boolean wrapPrincipals = false;
1015            private Map<String, Object> options = new HashMap<String, Object>();
1016    
1017            public String getLoginDomainName() {
1018                return loginDomainName;
1019            }
1020    
1021            public void setLoginDomainName(String loginDomainName) {
1022                this.loginDomainName = loginDomainName;
1023            }
1024    
1025            public String getClassName() {
1026                return className;
1027            }
1028    
1029            public void setClassName(String className) {
1030                this.className = className;
1031            }
1032    
1033            public LoginModuleControlFlag getControlFlag() {
1034                return controlFlag;
1035            }
1036    
1037            public void setControlFlag(LoginModuleControlFlag controlFlag) {
1038                this.controlFlag = controlFlag;
1039            }
1040    
1041            public Map<String, Object> getOptions() {
1042                return options;
1043            }
1044    
1045            public void setOptions(Map<String, Object> options) {
1046                this.options = options;
1047            }
1048    
1049            public boolean isWrapPrincipals() {
1050                return wrapPrincipals;
1051            }
1052    
1053            public void setWrapPrincipals(boolean wrapPrincipals) {
1054                this.wrapPrincipals = wrapPrincipals;
1055            }
1056    
1057            public String getOptionString() {
1058                StringBuffer buf = new StringBuffer();
1059                for (Iterator it = options.keySet().iterator(); it.hasNext();) {
1060                    String key = (String) it.next();
1061                    buf.append(key).append("=").append(options.get(key)).append("\n");
1062                }
1063                return buf.toString();
1064            }
1065        }
1066    
1067        public static class ExistingRealm implements Serializable {
1068            private final String name;
1069            private final String abstractName;
1070            private final String parentName;
1071    
1072            public ExistingRealm(String name, AbstractName abstractName, String parent) {
1073                this.name = name;
1074                this.abstractName = abstractName.toString();
1075                parentName = parent;
1076            }
1077    
1078            public String getName() {
1079                return name;
1080            }
1081    
1082            public String getAbstractName() {
1083                return abstractName;
1084            }
1085    
1086            public String getParentName() {
1087                return parentName;
1088            }
1089    
1090        }
1091    
1092        public static class DatabasePool implements Serializable, Comparable {
1093            private final String name;
1094            private final String displayName;
1095            private final String applicationName;
1096            private final String abstractName;
1097    
1098            public DatabasePool(String name, String displayName, String applicationName, AbstractName abstractName) {
1099                this.name = name;
1100                this.displayName = displayName;
1101                this.applicationName = applicationName;
1102                this.abstractName = abstractName.toString();
1103            }
1104    
1105            public String getName() {
1106                return name;
1107            }
1108    
1109            public String getApplicationName() {
1110                return applicationName;
1111            }
1112    
1113            public String getAbstractName() {
1114                return abstractName;
1115            }
1116    
1117            public String getDisplayName() {
1118                return displayName;
1119            }
1120    
1121            public int compareTo(Object o) {
1122                final DatabasePool pool = (DatabasePool) o;
1123                int names = name.compareTo(pool.name);
1124                if (applicationName == null) {
1125                    if (pool.applicationName == null) {
1126                        return names;
1127                    } else {
1128                        return -1;
1129                    }
1130                } else {
1131                    if (pool.applicationName == null) {
1132                        return 1;
1133                    } else {
1134                        int test = applicationName.compareTo(pool.applicationName);
1135                        if (test != 0) {
1136                            return test;
1137                        } else {
1138                            return names;
1139                        }
1140                    }
1141                }
1142            }
1143        }
1144    }