Package org.apache.isis.applib.fixturescripts

Source Code of org.apache.isis.applib.fixturescripts.FixtureScripts

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you 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.apache.isis.applib.fixturescripts;

import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import org.apache.isis.applib.AbstractService;
import org.apache.isis.applib.ViewModel;
import org.apache.isis.applib.annotation.*;
import org.apache.isis.applib.services.bookmark.BookmarkService;
import org.apache.isis.applib.services.classdiscovery.ClassDiscoveryService;
import org.apache.isis.applib.services.classdiscovery.ClassDiscoveryService2;
import org.apache.isis.applib.services.memento.MementoService;
import org.apache.isis.applib.services.memento.MementoService.Memento;
import org.apache.isis.applib.util.ObjectContracts;

public abstract class FixtureScripts extends AbstractService {


    //region > nonPersistedObjectsStrategy, multipleExecutionStrategy enums

    /**
     * How to handle objects that are to be
     * {@link FixtureScripts#newFixtureResult(FixtureScript, String, Object, boolean) added}
     * into a {@link org.apache.isis.applib.fixturescripts.FixtureResult} but which are not yet persisted.
     */
    public enum NonPersistedObjectsStrategy {
        PERSIST,
        IGNORE
    }

    /**
     * How to handle fixture scripts that are submitted to be executed more than once.
     */
    public enum MultipleExecutionStrategy {
        /**
         * Any given fixture script (or more precisely, any fixture script instance for a particular fixture script
         * class) can only be run once.
         *
         * <p>
         *     This strategy represents the original design of fixture scripts service.  Specifically,it allows an
         *     arbitrary graph of fixture scripts (eg A -> B -> C, A -> B -> D, A -> C -> D) to be created each
         *     specifying its dependencies, and without having to worry or co-ordinate whether those prerequisite
         *     fixture scripts have already been run.
         * </p>
         * <p>
         *     The most obvious example is a global teardown script; every fixture script can require this to be
         *     called, but it will only be run once.
         * </p>
         * <p>
         *     Note that this strategy treats fixture scripts as combining both the 'how' (which business action(s) to
         *     call) and the also the 'what' (what the arguments are to those actions).
         * </p>
         */
        IGNORE,
        /**
         * Allow fixture scripts to run as requested, even if that another instance of the fixture script's class
         * has already been invoked.
         *
         * <p>
         *     This strategy is conceptually the simplest; all fixtures are run as requested.  However, it is then
         *     the responsibility of the programmer to ensure that fixtures do not interfere with each other.  For
         *     example, if fixture A calls fixture B which calls teardown, and fixture A also calls fixture C that
         *     itself calls teardown, then fixture B's setup will get removed.
         * </p>
         * <p>
         *     The workaround to the teardown issue is of course to call the teardown fixture only once in the test
         *     itself; however even then this strategy cannot cope with arbitrary graphs of fixtures.  The solution
         *     is for the fixture list to be flat, one level high.
         * </p>
         */
        EXECUTE;

        public boolean isIgnore() {
            return this == IGNORE;
        }
        public boolean isExecute() {
            return this == EXECUTE;
        }
    }

    //endregion

    //region > constructors

    /**
     * Defaults to {@link org.apache.isis.applib.fixturescripts.FixtureScripts.NonPersistedObjectsStrategy#PERSIST persist}
     * strategy (if non-persisted objects are {@link #newFixtureResult(FixtureScript, String, Object, boolean) added} to a {@link org.apache.isis.applib.fixturescripts.FixtureResultList}),
     * defaults {@link #getMultipleExecutionStrategy()} to {@link org.apache.isis.applib.fixturescripts.FixtureScripts.MultipleExecutionStrategy#IGNORE ignore}
     * if multiple instances of the same fixture script class are encountered.
     *
     * @param packagePrefix - to search for fixture script implementations, eg "com.mycompany"
     */
    public FixtureScripts(final String packagePrefix) {
        this(packagePrefix, NonPersistedObjectsStrategy.PERSIST, MultipleExecutionStrategy.IGNORE);
    }

    /**
     * Defaults to {@link org.apache.isis.applib.fixturescripts.FixtureScripts.NonPersistedObjectsStrategy#PERSIST persist}
     * strategy (if non-persisted objects are {@link #newFixtureResult(FixtureScript, String, Object, boolean) added} to a {@link org.apache.isis.applib.fixturescripts.FixtureResultList}).
     *
     * @param packagePrefix - to search for fixture script implementations, eg "com.mycompany"
     * @param multipleExecutionStrategy - whether more than one instance of the same fixture script class can be run multiple times
     */
    public FixtureScripts(
            final String packagePrefix,
            final MultipleExecutionStrategy multipleExecutionStrategy) {
        this(packagePrefix, NonPersistedObjectsStrategy.PERSIST, multipleExecutionStrategy);
    }

    /**
     * Defaults {@link #getMultipleExecutionStrategy()} to {@link org.apache.isis.applib.fixturescripts.FixtureScripts.MultipleExecutionStrategy#IGNORE ignore}
     * if multiple instances of the same fixture script class are encountered.
     *
     * @param packagePrefix  - to search for fixture script implementations, eg "com.mycompany"
     * @param nonPersistedObjectsStrategy - how to handle any non-persisted objects that are {@link #newFixtureResult(FixtureScript, String, Object, boolean) added} to a {@link org.apache.isis.applib.fixturescripts.FixtureResultList}.
     */
    public FixtureScripts(
            final String packagePrefix, NonPersistedObjectsStrategy nonPersistedObjectsStrategy) {
        this(packagePrefix, nonPersistedObjectsStrategy, MultipleExecutionStrategy.IGNORE);
    }

    /**
     * @param packagePrefix  - to search for fixture script implementations, eg "com.mycompany"
     * @param nonPersistedObjectsStrategy - how to handle any non-persisted objects that are {@link #newFixtureResult(FixtureScript, String, Object, boolean) added} to a {@link org.apache.isis.applib.fixturescripts.FixtureResultList}.
     * @param multipleExecutionStrategy - whether more than one instance of the same fixture script class can be run multiple times
     */
    public FixtureScripts(
            final String packagePrefix,
            final NonPersistedObjectsStrategy nonPersistedObjectsStrategy,
            final MultipleExecutionStrategy multipleExecutionStrategy) {
        this.packagePrefix = packagePrefix;
        this.nonPersistedObjectsStrategy = nonPersistedObjectsStrategy;
        this.multipleExecutionStrategy = multipleExecutionStrategy;
    }

    //endregion

    //region > packagePrefix, nonPersistedObjectsStrategy, multipleExecutionStrategy

    private final String packagePrefix;

    @Programmatic
    public String getPackagePrefix() {
        return packagePrefix;
    }

    private final NonPersistedObjectsStrategy nonPersistedObjectsStrategy;
    private final FixtureScripts.MultipleExecutionStrategy multipleExecutionStrategy;

    @Programmatic
    public NonPersistedObjectsStrategy getNonPersistedObjectsStrategy() {
        return nonPersistedObjectsStrategy;
    }

    @Programmatic
    public MultipleExecutionStrategy getMultipleExecutionStrategy() {
        return multipleExecutionStrategy;
    }

    //endregion

    //region > init

    private final List<FixtureScript> fixtureScriptList = Lists.newArrayList();
   
    @PostConstruct
    public void init() {
        findAndInstantiateFixtureScripts(fixtureScriptList);
    }

    private void findAndInstantiateFixtureScripts(List<FixtureScript> fixtureScriptList) {
        final Set<Class<? extends FixtureScript>> classes =
                findSubTypesOfClasses();
        for (final Class<? extends FixtureScript> fixtureScriptCls : classes) {
            final String packageName = fixtureScriptCls.getPackage().getName();
            if(!packageName.startsWith(packagePrefix)) {
                continue;
            }
            final FixtureScript fs = newFixtureScript(fixtureScriptCls);
            if(fs != null) {
                fixtureScriptList.add(fs);
            }
        }
        Collections.sort(fixtureScriptList, new Comparator<FixtureScript>() {
            @Override
            public int compare(FixtureScript o1, FixtureScript o2) {
                return ObjectContracts.compare(o1, o2, "friendlyName,qualifiedName");
            }
        });
    }

    private Set<Class<? extends FixtureScript>> findSubTypesOfClasses() {
        if(classDiscoveryService instanceof ClassDiscoveryService2) {
            final ClassDiscoveryService2 classDiscoveryService2 = (ClassDiscoveryService2) classDiscoveryService;
            return classDiscoveryService2.findSubTypesOfClasses(FixtureScript.class, packagePrefix);
        } else {
            return classDiscoveryService.findSubTypesOfClasses(FixtureScript.class);
        }
    }

    private FixtureScript newFixtureScript(Class<? extends FixtureScript> fixtureScriptCls) {
        try {
            final Constructor<? extends FixtureScript> constructor = fixtureScriptCls.getConstructor();
            final FixtureScript template = constructor.newInstance();
            if(!template.isDiscoverable()) {
                return null;
            }
            return getContainer().newViewModelInstance(fixtureScriptCls, mementoFor(template));
        } catch(Exception ex) {
            // ignore if does not have a no-arg constructor or cannot be instantiated
            return null;
        }
    }

    //endregion

    //region > runFixtureScript (prototype action)

    /**
     * To make this action usable in the UI, override either {@link #choices0RunFixtureScript()} or
     * {@link #autoComplete0RunFixtureScript(String)} with <tt>public</tt> visibility</tt>.
     */
    @Prototype
    @MemberOrder(sequence="10")
    public List<FixtureResult> runFixtureScript(
            final FixtureScript fixtureScript,
            @Named("Parameters")
            @DescribedAs("Script-specific parameters (if any).  The format depends on the script implementation (eg key=value, CSV, JSON, XML etc)")
            @MultiLine(numberOfLines=10)
            @Optional
            final String parameters) {

        // if this method is called programmatically, the caller may have simply new'd up the fixture script
        // (rather than use container.newTransientInstance(...).  To allow this use case, we need to ensure that
        // domain services are injected into the fixture script.
        getContainer().injectServicesInto(fixtureScript);

        return fixtureScript.run(parameters);
    }
    public FixtureScript default0RunFixtureScript() {
        return fixtureScriptList.isEmpty() ? null: fixtureScriptList.get(0);
    }
    protected List<FixtureScript> choices0RunFixtureScript() {
        return fixtureScriptList;
    }
    protected List<FixtureScript> autoComplete0RunFixtureScript(final @MinLength(1) String arg) {
        return Lists.newArrayList(
                Collections2.filter(fixtureScriptList, new Predicate<FixtureScript>() {
                    @Override
                    public boolean apply(FixtureScript input) {
                        return contains(input.getFriendlyName()) || contains(input.getLocalName());
                    }
                    private boolean contains(String str) {
                        return str != null && str.contains(arg);
                    }
                }));
    }
    public String disableRunFixtureScript(final FixtureScript fixtureScript, final String parameters) {
        return fixtureScriptList.isEmpty()? "No fixture scripts found under package '" + packagePrefix + "'": null;
    }
    public String validateRunFixtureScript(final FixtureScript fixtureScript, final String parameters) {
        return fixtureScript.validateRun(parameters);
    }

    //endregion

    //region > hooks

    /**
     * Optional hook.
     */
    protected FixtureScript findFixtureScriptFor(String qualifiedName) {
        List<FixtureScript> fixtureScripts = fixtureScriptList;
        for (FixtureScript fs : fixtureScripts) {
            if(fs.getQualifiedName().contains(qualifiedName)) {
                return fs;
            }
        }
        return null;
    }
    /**
     * Optional hook.
     */
    protected FixtureScript findFixtureScriptFor(Class<? extends FixtureScript> fixtureScriptClass) {
        List<FixtureScript> fixtureScripts = fixtureScriptList;
        for (FixtureScript fs : fixtureScripts) {
            if(fixtureScriptClass.isAssignableFrom(fs.getClass())) {
                return fs;
            }
        }
        return null;
    }

    /**
     * Optional hook.
     */
    protected FixtureScript.ExecutionContext newExecutionContext(String parameters) {
        return new FixtureScript.ExecutionContext(parameters, this);
    }

    //endregion

    //region > memento support for FixtureScript


    String mementoFor(final FixtureScript fs) {
        return mementoService.create()
                .set("path", fs.getParentPath())
                .asString();
    }
    void initOf(final String mementoStr, final FixtureScript fs) {
        Memento memento = mementoService.parse(mementoStr);
        fs.setParentPath(memento.get("path", String.class));
    }

    //endregion

    //region > helpers (package level)

    @Programmatic
    FixtureResult newFixtureResult(FixtureScript script, String subkey, Object object, boolean firstTime) {
        if(object == null) {
            return null;
        }
        if (object instanceof ViewModel || getContainer().isPersistent(object)) {
            // continue
        } else {
            switch(nonPersistedObjectsStrategy) {
                case PERSIST:
                    getContainer().flush();
                    break;
                case IGNORE:
                    return null;
            }
        }
        final FixtureResult fixtureResult = new FixtureResult();
        fixtureResult.setFixtureScriptClassName(firstTime ? script.getClass().getName() : null);
        fixtureResult.setFixtureScriptQualifiedName(script.getQualifiedName());
        fixtureResult.setKey(script.pathWith(subkey));
        fixtureResult.setObject(object);
        return fixtureResult;
    }

    @Programmatic
    String titleOf(final FixtureResult fixtureResult) {
        final Object object = fixtureResult.getObject();
        return object != null? getContainer().titleOf(object): "(null)";
    }

    //endregion

    //region > injected services

    @javax.inject.Inject
    private MementoService mementoService;
   
    @javax.inject.Inject
    private BookmarkService bookmarkService;

    @javax.inject.Inject
    private ClassDiscoveryService classDiscoveryService;

    //endregion

}
TOP

Related Classes of org.apache.isis.applib.fixturescripts.FixtureScripts

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.