messageLogger.logInfo("DEBUG: parsedRequestAttributes=" + parsedRequestAttributes);
    public static Map attemptLogin(final String loginTicketId, final String ssoTicketId, final String userId, final String password)    throws UnknownTicketException, InoperableStateException,    IllegalInputException, AuthenticationException,    DirectoryUnavailableException {        // Sanity checks.        if (!ready)            throw new InoperableStateException(NOT_READY);        if (loginTicketId == null || loginTicketId.equals(""))            throw new IllegalInputException("Login ticket ID must be a non-empty string");        if (userId == null || userId.equals(""))            throw new IllegalInputException("User ID must be a non-empty string");        if (password == null || password.equals(""))            throw new IllegalInputException("Password must be a non-empty string");        // Look up authentication attempt from the store.        final MoriaAuthnAttempt authnAttempt;        try {            authnAttempt = store.getAuthnAttempt(loginTicketId, true, null);        } catch (NonExistentTicketException e) {            accessLogger.logUser(AccessStatusType.NONEXISTENT_LOGIN_TICKET, null, userId, loginTicketId, null);            messageLogger.logDebug(CAUGHT_NONEXISTENT_TICKET, loginTicketId, e);            throw new UnknownTicketException(NONEXISTENT_TICKET);        } catch (InvalidTicketException e) {            accessLogger.logUser(AccessStatusType.INVALID_LOGIN_TICKET, null, userId, loginTicketId, null);            messageLogger.logWarn(CAUGHT_INVALID_TICKET, loginTicketId, e);            throw new UnknownTicketException(NONEXISTENT_TICKET);        } catch (MoriaStoreException e) {            messageLogger.logCritical(CAUGHT_STORE, loginTicketId, e);            throw new InoperableStateException(STORE_DOWN);        }        // TODO: Must be done for directNonInteractiveAuthentication. Should be        // extracted to a method.        // Parse requested attributes and extract special attributes.        final String[] requestedAttributes = authnAttempt.getRequestedAttributes();        final HashSet parsedRequestAttributes = new HashSet();        boolean appendTGT = false;        for (int i = 0; i < requestedAttributes.length; i++) {            if (requestedAttributes[i].equals(TGT_IDENTIFIER)) {                appendTGT = true;            } else {                parsedRequestAttributes.add(requestedAttributes[i]);            }        }        messageLogger.logInfo("DEBUG: parsedRequestAttributes=" + parsedRequestAttributes);        // Authenticate and read requested attributes.        final HashMap fetchedAttributes;        try {            fetchedAttributes = directoryManager.authenticate(new Credentials(userId, password), (String[]) parsedRequestAttributes.toArray(new String[parsedRequestAttributes.size()]));        } catch (AuthenticationFailedException e) {            accessLogger.logUser(AccessStatusType.BAD_USER_CREDENTIALS, null, userId, loginTicketId, null);            messageLogger.logDebug("AuthenticationFailedException caught", loginTicketId, e);            throw new AuthenticationException();        } catch (BackendException e) {            messageLogger.logWarn("BackendException caught", loginTicketId, e);            throw new DirectoryUnavailableException();        }        // Remove any existing SSO ticket.        if (ssoTicketId != null && !ssoTicketId.equals("")) {            try {                store.removeSSOTicket(ssoTicketId);            } catch (NonExistentTicketException e) {                // The ticket has probably already timed out.                messageLogger.logDebug("SSO ticket does not exist - may have timed out", ssoTicketId, e);            } catch (InvalidTicketException e) {                // Using a non-SSO ticket for SSO.                messageLogger.logWarn("Using a non-SSO ticket for SSO", ssoTicketId, e);            } catch (MoriaStoreException e) {                // Unable to access the store.                messageLogger.logCritical(CAUGHT_STORE, ssoTicketId, e);                throw new InoperableStateException(STORE_DOWN);            }        }        // Cache attributes and get tickets.        final String serviceTicketId;        final String newSSOTicketId;        final HashMap cacheAttributes = new HashMap();        final HashMap authnAttemptAttrs = new HashMap();        final HashSet cachableAttributes = authzManager.getCachableAttributes();        final Iterator it = cachableAttributes.iterator();        while (it.hasNext()) {            final String attrName = (String) it.next();            // Only cache an attribute if it's in the request and is cacheable.            if (parsedRequestAttributes.contains(attrName)) {                cacheAttributes.put(attrName, fetchedAttributes.get(attrName));            }        }        for (int i = 0; i < requestedAttributes.length; i++) {            authnAttemptAttrs.put(requestedAttributes[i], fetchedAttributes.get(requestedAttributes[i]));        }        try {            newSSOTicketId = store.cacheUserData(cacheAttributes);            if (appendTGT) {                authnAttemptAttrs.put(TGT_IDENTIFIER, store.createTicketGrantingTicket(newSSOTicketId, authnAttempt.getServicePrincipal()));            }            store.setTransientAttributes(loginTicketId, authnAttemptAttrs);            serviceTicketId = store.createServiceTicket(loginTicketId);        } catch (NonExistentTicketException e) {            /* Should not happen due to previous validation in this method */            messageLogger.logWarn(CAUGHT_NONEXISTENT_TICKET + ", should not happen (already validated)", loginTicketId, e);            throw new UnknownTicketException(NONEXISTENT_TICKET);        } catch (InvalidTicketException e) {            messageLogger.logWarn(CAUGHT_INVALID_TICKET + ", should not happen (already validated)", loginTicketId, e);            throw new UnknownTicketException(NONEXISTENT_TICKET);        } catch (MoriaStoreException e) {            messageLogger.logCritical(CAUGHT_STORE, ssoTicketId, e);            throw new InoperableStateException(STORE_DOWN);        }        /* Return tickets */        final HashMap tickets = new HashMap();        tickets.put(SERVICE_TICKET, serviceTicketId);        tickets.put(SSO_TICKET, newSSOTicketId);        accessLogger.logUser(AccessStatusType.SUCCESSFUL_INTERACTIVE_AUTHENTICATION, authnAttempt.getServicePrincipal(), userId, loginTicketId, "Service: " + serviceTicketId + " SSO: " + ssoTicketId);        return tickets;    }