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 }