org.jboss.loom.actions.AbstractStatefulAction.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.loom.actions.AbstractStatefulAction.java

Source

/**
 * 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 org.jboss.loom.actions;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.jboss.loom.ctx.MigrationContext;
import org.jboss.loom.ex.MigrationException;
import org.jboss.loom.spi.IMigrator;

/**
 * Implements lifecycle methods which manage the state,
 * and some properties (context, origin message, origin stacktrace, origin migrator, warnings, dependencies).
 * 
 * @author Ondrej Zizka, ozizka at redhat.com
 *         <p/>
 *         TODO: Introduce do***(), eg. doBackup(), to manage the states here, not in the impl.
 */
public abstract class AbstractStatefulAction implements IMigrationAction {

    IMigrationAction.State state = State.INITIAL;

    private MigrationContext ctx;
    private String originMessage;
    private StackTraceElement originStacktrace;
    private Class<? extends IMigrator> fromMigrator;
    private List<String> warnings = new LinkedList();
    private List<IMigrationAction> deps = new LinkedList();

    public AbstractStatefulAction() {
        this.originStacktrace = getNonActionCallee(Thread.currentThread().getStackTrace());
    }

    public AbstractStatefulAction(Class<? extends IMigrator> fromMigrator) {
        this();
        this.fromMigrator = fromMigrator;
    }

    private StackTraceElement getNonActionCallee(StackTraceElement[] stackTrace) {
        //return Thread.currentThread().getStackTrace()[4];
        // 0 - Thread.getStackTrace().
        // 1 - This method.
        // 2 - This constructor.
        // 3 - *Action constructor.
        // 4 - Whatever called new CliCommandAction.
        // Could be better, e.g. first non-constructor after 2.

        for (StackTraceElement elm : stackTrace) {
            if (IMigrationAction.class.isAssignableFrom(elm.getClass()))
                return elm;
        }
        return stackTrace[4]; // Fallback; will not happen, unless we put main() to this class or so.
    }

    public AbstractStatefulAction addWarning(String text) {
        warnings.add(text);
        return this;
    }

    public void checkState(IMigrationAction.State... states) {
        for (State state : states) {
            if (this.state == state)
                return;
        }
        throw new RuntimeException("Action not in expected states " + StringUtils.join(states, " ") + ", but in "
                + this.getState() + ":\n    " + this.toDescription());
    }

    //<editor-fold defaultstate="collapsed" desc="get/set">
    @Override
    public void setMigrationContext(MigrationContext ctx) {
        this.ctx = ctx;
    }

    @Override
    public MigrationContext getMigrationContext() {
        return this.ctx;
    }

    @Override
    public IMigrationAction.State getState() {
        return state;
    }

    public void setState(IMigrationAction.State state) {
        this.state = state;
    }

    @Override
    public StackTraceElement getOriginStackTrace() {
        return originStacktrace;
    }

    @Override
    public String getOriginMessage() {
        return originMessage;
    }

    public AbstractStatefulAction setOriginMessage(String msg) {
        this.originMessage = msg;
        return this;
    }

    @Override
    public Class<? extends IMigrator> getFromMigrator() {
        return fromMigrator;
    }

    @Override
    public List<String> getWarnings() {
        return warnings;
    }
    //</editor-fold>

    protected boolean isAfterBackup() {
        return this.state.ordinal() >= State.BACKED_UP.ordinal();
    }

    protected boolean isAfterPerform() {
        return this.state.ordinal() >= State.DONE.ordinal();
    }

    /* ----- Dependency stuff ----- */
    @Override
    public List<IMigrationAction> getDependencies() {
        return this.deps;
    }

    @Override
    public IMigrationAction addDependency(IMigrationAction dep) {
        this.deps.add(dep);
        return this;
    }

    /**
     *  {@inheritDoc}
     */
    @Override
    public int dependsOn(IMigrationAction other) throws CircularDependencyException {

        Set<IMigrationAction> visited = new HashSet();
        visited.add(this);
        visited.add(other);

        return dependsOn(other, visited);
    }

    /**
     *  {@inheritDoc}
     */
    private int dependsOn(IMigrationAction other, Set<IMigrationAction> visited)
            throws CircularDependencyException {

        if (this.getDependencies().isEmpty())
            return -1;
        if (this.equals(other))
            return 0;
        if (this.getDependencies().contains(other))
            return 1;

        int minDist = Integer.MAX_VALUE;
        for (IMigrationAction dep : this.getDependencies()) {
            if (visited.contains(dep))
                throw new CircularDependencyException(this, dep);
            int dist = dep.dependsOn(other);
            if (dist > 0)
                minDist = Math.min(dist, minDist);
        }
        if (minDist == Integer.MAX_VALUE)
            return -1;
        else
            return minDist + 1;
    }

    public static class CircularDependencyException extends MigrationException {
        public CircularDependencyException(IMigrationAction a, IMigrationAction b) {
            super("Circular dependency of actions - somewhere between these:\n\n" + a.toDescription() + "\n\n"
                    + b.toDescription());
        }
    }

    /**
     *  Sorting nodes of one action's dependency tree.
     */
    public static List<IMigrationAction> sortNodes_LeavesFirst(IMigrationAction action) {
        List<IMigrationAction> retNodes = new LinkedList();
        sortNodes_LeavesFirst(action, retNodes);
        return retNodes;
    }

    private static void sortNodes_LeavesFirst(IMigrationAction action, List<IMigrationAction> retNodes) {
        for (IMigrationAction dep : action.getDependencies()) {
            sortNodes_LeavesFirst(dep, retNodes);
        }
        retNodes.add(action);
    }

}// class