com.b2international.snowowl.server.console.UserSessionCommandProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.b2international.snowowl.server.console.UserSessionCommandProvider.java

Source

/*
 * Copyright 2011-2015 B2i Healthcare Pte Ltd, http://b2i.sg
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.b2international.snowowl.server.console;

import static com.google.common.collect.Lists.newArrayList;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.b2international.commons.CompareUtils;
import com.b2international.commons.Pair;
import com.b2international.commons.StringUtils;
import com.b2international.commons.collections.Collections3;
import com.b2international.commons.collections.Procedure;
import com.b2international.snowowl.core.ApplicationContext;
import com.b2international.snowowl.core.LogUtils;
import com.b2international.snowowl.core.api.IBranchPath;
import com.b2international.snowowl.core.date.DateFormats;
import com.b2international.snowowl.core.date.Dates;
import com.b2international.snowowl.datastore.BranchPathUtils;
import com.b2international.snowowl.datastore.cdo.ICDOConnection;
import com.b2international.snowowl.datastore.cdo.ICDOConnectionManager;
import com.b2international.snowowl.datastore.cdo.ICDORepository;
import com.b2international.snowowl.datastore.cdo.ICDORepositoryManager;
import com.b2international.snowowl.datastore.cdo.ICDORepositoryManager.ISessionOperationCallback;
import com.b2international.snowowl.datastore.oplock.IOperationLockTarget;
import com.b2international.snowowl.datastore.oplock.OperationLockException;
import com.b2international.snowowl.datastore.oplock.OperationLockInfo;
import com.b2international.snowowl.datastore.oplock.impl.AllRepositoriesLockTarget;
import com.b2international.snowowl.datastore.oplock.impl.DatastoreLockContext;
import com.b2international.snowowl.datastore.oplock.impl.DatastoreLockContextDescriptions;
import com.b2international.snowowl.datastore.oplock.impl.IDatastoreOperationLockManager;
import com.b2international.snowowl.datastore.oplock.impl.SingleRepositoryAndBranchLockTarget;
import com.b2international.snowowl.datastore.oplock.impl.SingleRepositoryLockTarget;
import com.b2international.snowowl.datastore.server.oplock.impl.DatastoreOperationLockManager;
import com.b2international.snowowl.datastore.session.IApplicationSessionManager;
import com.b2international.snowowl.identity.domain.User;
import com.google.common.base.Strings;
import com.google.common.primitives.Ints;

/**
 * OSGI command contribution with Snow Owl commands to manage user sessions.
 * 
 */
public class UserSessionCommandProvider implements CommandProvider {

    private static Logger USER_ACTIVITY_LOGGER = LoggerFactory.getLogger(UserSessionCommandProvider.class);

    @Override
    public String getHelp() {
        final StringBuffer buffer = new StringBuffer();
        buffer.append("---Snow Owl user session commands---\n");
        buffer.append("\tsession users - List the users that are currently logged in\n");
        buffer.append(
                "\tsession message [user1Name,userName2,userNameN|ALL] [message] - Send message to active user(s) or all users. Do not use space between the users.\n");
        buffer.append(
                "\tsession disconnect [userName1,userName2,userNameN|ALL] - Disconnect active user(s) or all users.  Do not use space between the users.\n");
        buffer.append(
                "\tsession login [enabled|disabled|status] - Enables/disables login for new, non-administrator sessions.\n");
        buffer.append("\tsession showlocks - Displays all currently acquired locks on the server.\n");
        buffer.append(
                "\tsession lock [allRepositories|repositoryUuid|repositoryUuid branchPath] - Acquires a lock in repository on the specified branch.\n");
        buffer.append(
                "\tsession unlock [allRepositories|repositoryUuid|repositoryUuid branchPath] - Releases a lock in the repository on the specified branch.\n");
        buffer.append("\tsession forceunlock [all|lock identifier] - Forcefully releases the specified locks.\n");
        buffer.append("\tsession repositories - Lists all available repositories.\n");
        return buffer.toString();
    }

    /**
     * Reflective template method declaratively registered. Needs to start with
     * "_".
     * 
     * @param interpreter
     */
    public void _session(final CommandInterpreter interpreter) {

        try {

            final String cmd = Strings.nullToEmpty(interpreter.nextArgument());

            switch (cmd) {
            case "users":
                users(interpreter);
                break;
            case "disconnect":
                disconnect(interpreter);
                break;
            case "message":
                message(interpreter);
                break;
            case "login":
                login(interpreter);
                break;
            case "lock":
                lock(interpreter);
                break;
            case "unlock":
                unlock(interpreter);
                break;
            case "showlocks":
                showLocks(interpreter);
                break;
            case "forceunlock":
                forceUnlock(interpreter);
                break;
            case "repositories":
                repositories(interpreter);
                break;
            default:
                interpreter.println(getHelp());
                break;
            }

        } catch (final Exception ex) {
            interpreter.println(ex.getMessage());
        }
    }

    /**
     * List the active users of the session.
     * 
     * @param interpreter
     */
    public synchronized void users(final CommandInterpreter interpreter) {

        final List<Pair<String, String>> info = newArrayList(ApplicationContext.getInstance()
                .getService(IApplicationSessionManager.class).getConnectedSessionInfo());

        if (CompareUtils.isEmpty(info)) {

            interpreter.println("No users are connected to the server.");

        } else {

            Collections.sort(info, new Comparator<Pair<String, String>>() {
                @Override
                public int compare(final Pair<String, String> o1, final Pair<String, String> o2) {
                    return Strings.nullToEmpty(o1.getA()).compareTo(Strings.nullToEmpty(o2.getA()));
                }
            });

            for (final Pair<String, String> pair : info) {
                interpreter.println("User: " + pair.getA() + " | session ID: " + pair.getB());
            }

        }

    }

    /**
     * Sends a message to all the users.
     * 
     * @param interpreter
     */
    public synchronized void message(final CommandInterpreter interpreter) {

        final String usage = "Command usage: session message [user1Name,userName2,userNameN|ALL] [message]";
        final String userListParameter = interpreter.nextArgument();
        if (StringUtils.isEmpty(userListParameter)) {
            interpreter.println("User is null.");
            interpreter.print(usage);
            return;
        }

        final String messageBody = interpreter.nextArgument();
        if (StringUtils.isEmpty(messageBody)) {
            interpreter.println("Message is null.");
            interpreter.print(usage);
            return;
        }

        final StringBuilder sb = new StringBuilder(messageBody);
        sb.append(' ');
        while (true) {

            final String messageFragment = interpreter.nextArgument();
            if (StringUtils.isEmpty(messageFragment)) {
                break;
            } else {
                sb.append(messageFragment);
                sb.append(' ');
            }
        }

        final AtomicBoolean success = new AtomicBoolean(false);
        final ICDORepositoryManager repositoryManager = ApplicationContext.getInstance()
                .getService(ICDORepositoryManager.class);

        final String message = sb.toString();

        final ISessionOperationCallback callback = new ISessionOperationCallback() {

            @Override
            public void done(final CDORemoteSession session) {
                interpreter.println("Message sent to " + session.getUserID());
                success.compareAndSet(false, true);
            }
        };

        if ("ALL".equals(userListParameter)) {

            repositoryManager.sendMessageToAll(message, callback);

        } else {

            repositoryManager.sendMessageTo(message, tokenizeParameter(userListParameter), callback);

        }

        if (!success.get()) {
            interpreter.println("Failed to message user(s): " + userListParameter
                    + ". Are these active users? Currently active users are:\n");
            users(interpreter);
        }
    }

    /**
     * Disconnect the users.
     * 
     * @param interpreter
     */
    public synchronized void disconnect(final CommandInterpreter interpreter) {
        final String userNamesParameter = interpreter.nextArgument();

        if (StringUtils.isEmpty(userNamesParameter)) {
            interpreter.print("Command usage: session disconnect [userName1,userName2,userNameN|ALL]");
            return;
        }

        final AtomicBoolean success = new AtomicBoolean(false);
        final ICDORepositoryManager repositoryManager = ApplicationContext.getInstance()
                .getService(ICDORepositoryManager.class);

        final ISessionOperationCallback callback = new ISessionOperationCallback() {

            @Override
            public void done(final CDORemoteSession session) {
                interpreter.println("User: " + session.getUserID() + " | session ID: " + session.getSessionID()
                        + " was disconnected.");
                LogUtils.logUserEvent(USER_ACTIVITY_LOGGER, "admin", "Disconnected user: " + session.getUserID()
                        + " from session: " + session.getSessionID() + ".");
                success.compareAndSet(false, true);
            }
        };

        if ("ALL".equals(userNamesParameter)) {

            repositoryManager.disconnectAll(callback);

        } else {

            repositoryManager.disconnect(tokenizeParameter(userNamesParameter), callback);

        }

        if (!success.get()) {
            interpreter.println("Failed to disconnect user(s): " + userNamesParameter
                    + ". Are these users active? Currently active users are:\n");
            users(interpreter);
        }
    }

    private static final List<String> ALLOWED_SUBCOMMANDS = Arrays.asList("enabled", "disabled", "status");

    public synchronized void login(final CommandInterpreter interpreter) {

        final String subCommand = interpreter.nextArgument();

        if (StringUtils.isEmpty(subCommand) || !ALLOWED_SUBCOMMANDS.contains(subCommand.toLowerCase())) {
            interpreter.print("Command usage: session login [enabled|disabled|status]");
            return;
        }

        IApplicationSessionManager applicationSessionManager = ApplicationContext.getInstance()
                .getService(IApplicationSessionManager.class);
        if (subCommand.equalsIgnoreCase("status")) {
            interpreter.println(MessageFormat.format("Non-administrative logins are currently {0}.",
                    (applicationSessionManager.isLoginEnabled() ? "enabled" : "disabled")));
            return;
        }

        final boolean loginEnabled = subCommand.equalsIgnoreCase("enabled");
        applicationSessionManager.enableLogins(loginEnabled);
        interpreter.println(
                MessageFormat.format("{0} non-administrative logins.", (loginEnabled ? "Enabled" : "Disabled")));
    }

    /*
     * There should be no spaces present in the input string.
     */
    private List<String> tokenizeParameter(final String userName) {
        final List<String> userList = new ArrayList<String>();

        final StringTokenizer st = new StringTokenizer(userName, ",");
        while (st.hasMoreElements()) {
            userList.add(st.nextToken());
        }
        return userList;
    }

    public synchronized void repositories(final CommandInterpreter interpreter) {

        Collections3.forEach(ApplicationContext.getInstance().getService(ICDORepositoryManager.class),
                new Procedure<ICDORepository>() {
                    @Override
                    protected void doApply(final ICDORepository repository) {
                        interpreter.println(
                                "\t" + repository.getRepositoryName() + " [ID: " + repository.getUuid() + "]");
                    }
                });
    }

    private static final String COLUMN_FORMAT = "%4s | %3s | %-16s | %-50s | %-50s";

    public synchronized void showLocks(final CommandInterpreter interpreter) {

        final IDatastoreOperationLockManager lockManager = ApplicationContext.getInstance()
                .getService(IDatastoreOperationLockManager.class);
        final List<OperationLockInfo<DatastoreLockContext>> locks = ((DatastoreOperationLockManager) lockManager)
                .getLocks();

        if (locks.isEmpty()) {
            interpreter.println("No locks are currently granted on this server.");
            return;
        }

        interpreter.println();
        interpreter
                .println(String.format(COLUMN_FORMAT, "Id", "Lvl", "Created on", "Locked area", "Owner context"));
        interpreter.println(Strings.repeat("-", 135));

        for (final OperationLockInfo<DatastoreLockContext> lockEntry : locks) {
            interpreter.println(String.format(COLUMN_FORMAT, lockEntry.getId(), lockEntry.getLevel(),
                    Dates.formatByHostTimeZone(lockEntry.getCreationDate(), DateFormats.MEDIUM),
                    StringUtils.truncate(StringUtils.capitalizeFirstLetter(lockEntry.getTarget().toString()), 50),
                    StringUtils.truncate("Lock owner: " + lockEntry.getContext().getUserId(), 50)));

            interpreter.println(String.format(COLUMN_FORMAT, "", "", "", "", StringUtils
                    .truncate(StringUtils.capitalizeFirstLetter(lockEntry.getContext().getDescription()), 50)));

            interpreter.println(Strings.repeat("-", 135));
        }

    }

    public synchronized void forceUnlock(final CommandInterpreter interpreter) {

        final DatastoreOperationLockManager lockManager = (DatastoreOperationLockManager) ApplicationContext
                .getInstance().getService(IDatastoreOperationLockManager.class);
        final String argument = interpreter.nextArgument();

        if (null == argument) {
            interpreter.println("Missing parameter. Usage: forceunlock [all|lock identifier]");
            return;
        }

        if ("all".equalsIgnoreCase(argument)) {
            lockManager.unlockAll();
            interpreter.println("Forcefully released all acquired locks.");
            return;
        }

        final Integer parsedLockId = Ints.tryParse(argument);

        if (null != parsedLockId) {

            if (lockManager.unlockById(parsedLockId)) {
                interpreter.println("Forcefully released lock with identifier " + parsedLockId + ".");
            } else {
                interpreter.println("Lock with identifier " + parsedLockId + " could not be found.");
            }

        } else {
            interpreter.println("Lock identifier is not an interger. Usage: forceunlock [all|lock identifier]");
        }
    }

    public synchronized void lock(final CommandInterpreter interpreter) {

        final IOperationLockTarget target = parseLockTarget(interpreter);

        if (null == target) {
            return;
        }

        final IDatastoreOperationLockManager lockManager = ApplicationContext.getInstance()
                .getService(IDatastoreOperationLockManager.class);
        final DatastoreLockContext context = new DatastoreLockContext(User.SYSTEM.getUsername(),
                DatastoreLockContextDescriptions.MAINTENANCE);

        try {
            lockManager.lock(context, 3000L, target);
        } catch (final OperationLockException | InterruptedException e) {
            interpreter.println(e);
            return;
        }

        interpreter.println("Acquired lock for " + target + ".");
    }

    public synchronized void unlock(final CommandInterpreter interpreter) {

        final IOperationLockTarget target = parseLockTarget(interpreter);

        if (null == target) {
            return;
        }

        final IDatastoreOperationLockManager lockManager = ApplicationContext.getInstance()
                .getService(IDatastoreOperationLockManager.class);
        final DatastoreLockContext context = new DatastoreLockContext(User.SYSTEM.getUsername(),
                DatastoreLockContextDescriptions.MAINTENANCE);

        try {
            lockManager.unlock(context, target);
        } catch (final OperationLockException e) {
            interpreter.print(e);
            return;
        }

        interpreter.println("Released lock for " + target + ".");
    }

    private IOperationLockTarget parseLockTarget(final CommandInterpreter interpreter) {

        final String uuidOrAll = interpreter.nextArgument();

        if (StringUtils.isEmpty(uuidOrAll)) {
            interpreter.println("Repository UUID or \"allRepositories\" should be specified.");
            interpreter.println(getHelp());
            return null;
        }

        if ("allRepositories".equalsIgnoreCase(uuidOrAll)) {
            return AllRepositoriesLockTarget.INSTANCE;
        }

        final ICDORepositoryManager repositoryManager = ApplicationContext.getInstance()
                .getService(ICDORepositoryManager.class);
        final ICDORepository repository = repositoryManager.getByUuid(uuidOrAll);
        if (null == repository) {

            interpreter.println("Repository does not exist with UUID: '" + uuidOrAll + "'.");
            interpreter.println("Available stores are the followings:");
            interpreter.println("------------------------------------");
            for (final ICDORepository cdoRepository : repositoryManager) {
                interpreter.println("\t" + cdoRepository.getUuid());
            }
            interpreter.println("------------------------------------");

            interpreter.println(getHelp());
            return null;
        }

        final String path = interpreter.nextArgument();

        if (StringUtils.isEmpty(path)) {
            return new SingleRepositoryLockTarget(uuidOrAll);
        }

        final ICDOConnectionManager connectionManager = ApplicationContext.getInstance()
                .getService(ICDOConnectionManager.class);
        final ICDOConnection connection = connectionManager.getByUuid(uuidOrAll);
        IBranchPath branchPath = null;

        try {

            branchPath = BranchPathUtils.createPath(path);
            //assuming active connection manager service here

            if (null == connection.getBranch(branchPath)) {
                interpreter.println("Branch does not exist. Branch path: '" + branchPath + "'. Repository UUID: '"
                        + uuidOrAll + "'.");
                interpreter.println(getHelp());
                return null;
            }

        } catch (final Throwable t) {
            interpreter.println(
                    "Branch does not exist. Branch path: '" + path + "'. Repository UUID: '" + uuidOrAll + "'.");
            interpreter.println(getHelp());
            return null;
        }

        if (null == branchPath) {
            return null;
        }

        return new SingleRepositoryAndBranchLockTarget(uuidOrAll, branchPath);
    }
}