Package org.apache.uima.ducc.cli

Source Code of org.apache.uima.ducc.cli.DuccServiceApi

/*
* 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.uima.ducc.cli;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.uima.ducc.common.Pair;
import org.apache.uima.ducc.common.utils.DuccProperties;
import org.apache.uima.ducc.common.utils.DuccPropertiesResolver;
import org.apache.uima.ducc.common.utils.DuccSchedulerClasses;
import org.apache.uima.ducc.transport.event.ServiceModifyEvent;
import org.apache.uima.ducc.transport.event.ServiceQueryEvent;
import org.apache.uima.ducc.transport.event.ServiceRegisterEvent;
import org.apache.uima.ducc.transport.event.ServiceStartEvent;
import org.apache.uima.ducc.transport.event.ServiceStopEvent;
import org.apache.uima.ducc.transport.event.ServiceUnregisterEvent;
import org.apache.uima.ducc.transport.event.sm.IService.ServiceType;
import org.apache.uima.ducc.transport.event.sm.IService.Trinary;
import org.apache.uima.ducc.transport.event.sm.IServiceReply;


/**
* Handle registered services. This class is also the implementation of the
* DUCC service CLI which internally uses its API to implement itself.  Details on the
* DUCC service CLI are found in the<a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
*/
public class DuccServiceApi
    extends CliBase
{

    String endpoint = null;
    IDuccCallback callback = null;

    UiOption[] registration_options = {
        UiOption.Help,
        UiOption.Debug,
        UiOption.Description,
        UiOption.SchedulingClass,
        UiOption.LogDirectory,
        UiOption.SuppressConsoleLog,
        UiOption.WorkingDirectory,
        UiOption.Jvm,
        UiOption.ProcessJvmArgs,
        UiOption.Classpath,
        UiOption.Environment,
        UiOption.ProcessMemorySize,
        UiOption.ProcessDD,
        UiOption.ProcessExecutable,
        UiOption.ProcessExecutableArgs,
        UiOption.InstanceFailuresLimit,
        UiOption.ClasspathOrder,
        // UiOption.Specification          // not used for registration
        UiOption.ServiceDependency,
        UiOption.ServiceRequestEndpoint,
        UiOption.ServiceLinger,
        UiOption.ServicePingArguments,
        UiOption.ServicePingClass,
        UiOption.ServicePingClasspath,
        UiOption.ServicePingJvmArgs,
        UiOption.ServicePingTimeout,
        UiOption.ServicePingDoLog,

        UiOption.Register,
        UiOption.Autostart,
        UiOption.Instances,
    };
  
    UiOption[] unregister_options = {
        UiOption.Help,
        UiOption.Debug,
        UiOption.Unregister,
    };


    /**
     * Service start arguments.
     */
    UiOption[] start_options = {
        UiOption.Help,
        UiOption.Debug,
        UiOption.Start,
        UiOption.Instances,
        UiOption.Update,
    };

    UiOption[] stop_options = {
        UiOption.Help,
        UiOption.Debug,
        UiOption.Stop,
        UiOption.Instances,
        UiOption.Update,
    };

    UiOption[] modify_options = {
        UiOption.Help,
        UiOption.Debug,
        UiOption.Modify,
        UiOption.Instances,
        UiOption.Autostart,
        UiOption.Activate,
    };

    UiOption[] query_options = {
        UiOption.Help,
        UiOption.Debug,
        UiOption.Query,
    };

    public DuccServiceApi(IDuccCallback cb)
    {
        this.callback = cb;
    }

    private Pair<Integer, String> getId(UiOption opt)
    {

        String sid = cli_props.getProperty(opt.pname());

        if ( sid == null ) {
            throw new IllegalArgumentException("Missing service id: --id <id or endpoint>");
        }
       
        int id = -1;
        try {
            id = Integer.parseInt(sid);
            return new Pair<Integer, String>(id, null);
        } catch ( NumberFormatException e ) {
            // nothing
        }
        if ( sid.startsWith(ServiceType.UimaAs.decode()) || sid.startsWith(ServiceType.Custom.decode()) ) {
            return new Pair<Integer, String>(-1, sid);
        }
        throw new IllegalArgumentException("Invalid id; must be numeric or start with " + ServiceType.UimaAs.decode() + " or " + ServiceType.Custom.decode() + ".");
    }

    private Trinary getAutostart()
    {
        String auto = cli_props.getStringProperty(UiOption.Autostart.pname(), null);
        if ( auto == null ) {
            return Trinary.Unset;
        }
        boolean val = Boolean.parseBoolean(auto);

        Trinary answer = Trinary.encode(val ? "true" : "false");

        if ( answer == Trinary.Unset ) {
            throw new IllegalArgumentException("--" + UiOption.Autostart.pname()  + " " + auto + " is not 'true' or 'false'");
        }

        return answer;
    }

    private int getInstances(int dflt)
    {
        String inst = cli_props.getProperty(UiOption.Instances.pname());
        if ( inst == null ) return dflt;

        int instances;
        try {
            instances = Integer.parseInt(inst);
        } catch ( NumberFormatException e ) {
            throw new IllegalArgumentException(UiOption.Instances.pname() + " " + inst + " is not numeric.");
        }

        if ( instances <= 0 ) {
            throw new IllegalArgumentException(UiOption.Instances.pname() + " " + inst + " must be > 0");
        }

        return instances;
    }

    private boolean getActivate()
    {
        return cli_props.containsKey(UiOption.Activate.pname());
    }

    private boolean getUpdate()
    {
        return cli_props.containsKey(UiOption.Update.pname());
    }

    private void setLinger()
    {
        String default_linger = DuccPropertiesResolver.get("ducc.sm.default.linger", "5000");
        String linger         = cli_props.getStringProperty(UiOption.ServiceLinger.pname(), default_linger);
        try {            
      Long.parseLong(linger); // make sure it's a long, don't care about the value
        } catch ( NumberFormatException e ) {
            throw new IllegalArgumentException(UiOption.ServiceLinger.pname() + " is not numeric: " + linger);
        }
    }

    String extractEndpoint(String jvmargs)
    {
        String dd = cli_props.getStringProperty(UiOption.ProcessDD.pname()); // will throw if can't find the prop
        String working_dir = cli_props.getStringProperty(UiOption.WorkingDirectory.pname());
        endpoint = DuccUiUtilities.getEndpoint(working_dir, dd, jvmargs);
        if ( debug ) {
            System.out.println("Service endpoint resolves to " + endpoint);
        }
        return endpoint;
    }

    /**
     * The register API is used to register a service with the service manager.
     *
     * @param args String rray of arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     * @return {@link IServiceReply IServiceReply} object with register status.
     */
    public IServiceReply register(String[] args)
        throws Exception
    {
        DuccProperties dp = new DuccProperties();
        init (this.getClass().getName(), registration_options, args, null, dp, callback, "sm");

        // Note: dp & cli_props are identical ... use only the class variable here for consistency
       
        setLinger();

        //
        // Check if the mutually exclusive UIMA-AS DD and the Custom executable are specified
        //
        String uimaDD = cli_props.getStringProperty(UiOption.ProcessDD.pname(), null);
        String customCmd = cli_props.getStringProperty(UiOption.ProcessExecutable.pname(), null);
       
        //
        // Establish my endpoint
        //
        String  endpoint = cli_props.getStringProperty(UiOption.ServiceRequestEndpoint.pname(), null);
        if ( endpoint == null || endpoint.startsWith(ServiceType.UimaAs.decode()) ) {

            if (uimaDD == null) {
                throw new IllegalArgumentException("Must specify --process_DD for UIMA-AS services");
            }
            if (customCmd != null) {
                message("WARN", "--process_executable is ignored for UIMA-AS services");
            }
            // Infer the classpath (DuccProperties will return the default if the value isn't found.)
            // Note: only used for UIMA-AS services
            if (fixupClasspath(UiOption.Classpath.pname()) == null) {
                throw new IllegalArgumentException("Specified classpath contains only DUCC jars");
            }

            // Given ep must match inferred ep. Use case: an application is wrapping DuccServiceApi and has to construct
            // the endpoint as well.  The app passes it in and we insure that the constructed endpoint matches the one
            // we extract from the DD - the job will fail otherwise, so we catch this early.
            //
            String jvmarg_string = cli_props.getProperty(UiOption.ProcessJvmArgs.pname());
            String inferred_endpoint = extractEndpoint(jvmarg_string);
            if (endpoint == null) {
                endpoint = inferred_endpoint;
            } else if ( !inferred_endpoint.equals(endpoint) ) {
                throw new IllegalArgumentException("Specified endpoint does not match endpoint extracted from UIMA DD"
                                                   + "\n --service_request_endpoint: " + endpoint
                                                   + "\n                  extracted: " + inferred_endpoint );
            }
           
        } else if (endpoint.startsWith(ServiceType.Custom.decode())) {

            if (uimaDD != null) {
                message("WARN", "--process_DD is ignored for CUSTOM endpoints");
            }
            // Custom services must have a pinger, but the process_executable (& args)
            // options may be omitted for a ping-only service.
            // When omitted other options such as autostart are irrelevant.
            if ( ! cli_props.containsKey(UiOption.ServicePingClass.pname()) ) {
                throw new IllegalArgumentException("Custom service is missing ping class name.");
            }
            fixupClasspath(UiOption.ServicePingClasspath.pname());
       
        } else {
            throw new IllegalArgumentException("Invalid service endpoint: " + endpoint);
        }

        // Check if falsely using a fair-share class
        String scheduling_class = cli_props.getProperty(UiOption.SchedulingClass.pname());
        if (scheduling_class != null) {
            DuccSchedulerClasses duccSchedulerClasses = DuccSchedulerClasses.getInstance();
            if (duccSchedulerClasses.isPreemptable(scheduling_class)) {
                throw new IllegalArgumentException("Invalid pre-emptable scheduling class: " + scheduling_class);
            }
        }
       
        // work out stuff I'm dependent upon
        if ( !check_service_dependencies(endpoint) ) {
            throw new IllegalArgumentException("Invalid service dependencies");
        }
        int instances = cli_props.getIntProperty(UiOption.Instances.pname(), 1);
        Trinary autostart = getAutostart();
        String user = (String) cli_props.remove(UiOption.User.pname());
        byte[] auth_block = (byte[]) cli_props.remove(UiOption.Signature.pname());
       
        // A few spurious properties are set as an artifact of parsing the overly-complex command line, and need removal
        cli_props.remove(UiOption.SubmitPid.pname());
        cli_props.remove(UiOption.Register.pname());
        cli_props.remove(UiOption.Instances.pname());
        cli_props.remove(UiOption.Autostart.pname());

        ServiceRegisterEvent ev = new ServiceRegisterEvent(user, instances, autostart, endpoint, cli_props, auth_block);

        try {
            return (IServiceReply) dispatcher.dispatchAndWaitForDuccReply(ev);
        } finally {
            dispatcher.close();
        }
  }

    /**
     * The unregister API is used to unregister a service.  The service manager will stop all instances and
     * remove the service registration.
     *
     * @param args String array of arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     * @return {@link IServiceReply IServiceReply} object with unregister reply status.
     */
    public IServiceReply unregister(String[] args)
        throws Exception
    {
        DuccProperties dp = new DuccProperties();
        init(this.getClass().getName(), unregister_options, args, null, dp, callback, "sm");
       

        Pair<Integer, String> id = getId(UiOption.Unregister);
        String user = dp.getProperty(UiOption.User.pname());
        byte[] auth_block = (byte[]) dp.get(UiOption.Signature.pname());

        ServiceUnregisterEvent ev = new ServiceUnregisterEvent(user, id.first(), id.second(), auth_block);
       
        try {
            return (IServiceReply) dispatcher.dispatchAndWaitForDuccReply(ev);
        } finally {
            dispatcher.close();
        }

  }

    /**
     * The start API is used to start one or more instances of a registered service.
     *
     * @param args String array of arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     * @return {@link IServiceReply IServiceReply} object with start reply status.
     */
    public IServiceReply start(String[] args)
        throws Exception
    {
        DuccProperties dp = new DuccProperties();
        init(this.getClass().getName(), start_options, args, null, dp, callback, "sm");

        Pair<Integer, String> id = getId(UiOption.Start);
        String user = dp.getProperty(UiOption.User.pname());
        byte[] auth_block = (byte[]) dp.get(UiOption.Signature.pname());

        ServiceStartEvent ev = new ServiceStartEvent(user, id.first(), id.second(), auth_block);

        int instances = getInstances(-1);
        boolean update = getUpdate();

        ev.setInstances(instances);
        ev.setUpdate(update);

        try {
            return (IServiceReply) dispatcher.dispatchAndWaitForDuccReply(ev);
        } finally {
            dispatcher.close();
        }
    }


    /**
     * The stop API is used to stop one or more service instances.
     *
     * @param args String array of arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     * @return {@link IServiceReply IServiceReply} object with stop status.
     */
    public IServiceReply stop(String[] args)
        throws Exception
    {
        DuccProperties dp = new DuccProperties();
        init(this.getClass().getName(), stop_options, args, null, dp, callback, "sm");

        Pair<Integer, String> id = getId(UiOption.Stop);
        String user = dp.getProperty(UiOption.User.pname());
        byte[] auth_block = (byte[]) dp.get(UiOption.Signature.pname());

        ServiceStopEvent ev = new ServiceStopEvent(user, id.first(), id.second(), auth_block);

        int instances = getInstances(-1);
        boolean update = getUpdate();

        ev.setInstances(instances);
        ev.setUpdate(update);

        try {
            return (IServiceReply) dispatcher.dispatchAndWaitForDuccReply(ev);
        } finally {
            dispatcher.close();
        }       
    }

    /**
     * The service 'modify' command is used to change various aspects of a registered service
     * without the need to reregister it.
     *
     * @param args String array of arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     * @return {@link IServiceReply IServiceReply} object with modify status.
     */
    public IServiceReply modify(String[] args)
        throws Exception
    {
        DuccProperties dp = new DuccProperties();
        init (this.getClass().getName(), modify_options, args, null, dp, callback, "sm");

        Pair<Integer, String> id = getId(UiOption.Modify);
        String user = dp.getProperty(UiOption.User.pname());
        byte[] auth_block = (byte[]) dp.get(UiOption.Signature.pname());

        ServiceModifyEvent ev = new ServiceModifyEvent(user, id.first(), id.second(), auth_block);
        int instances = getInstances(-1);
        Trinary autostart = getAutostart();
        boolean activate = getActivate();

        ev.setInstances(instances);
        ev.setAutostart(autostart);
        ev.setActivate(activate);
       
        try {
            return (IServiceReply) dispatcher.dispatchAndWaitForDuccReply(ev);
        } finally {
            dispatcher.close();
        }
    }

    /**
     * The query API is used to query the status of services known to the service manager.
     *
     * @param args String array of arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     * @return {@link IServiceReply IServiceReply} object with query results status.
     */
    public IServiceReply query(String[] args)
        throws Exception
    {
        DuccProperties dp = new DuccProperties();
        init(this.getClass().getName(), query_options, args, null, dp, callback, "sm");

        Pair<Integer, String> id = null;
        String sid = cli_props.getProperty(UiOption.Query.pname()).trim();
        if ( sid == null || sid.equals("") ) {
            id = new Pair<Integer, String>(-1, null);
        } else {       
            id = getId(UiOption.Query);
        }

        String user = dp.getProperty(UiOption.User.pname());
        byte[] auth_block = (byte[]) dp.get(UiOption.Signature.pname());

        ServiceQueryEvent ev = new ServiceQueryEvent(user, id.first(), id.second(), auth_block);

        try {
            return (IServiceReply) dispatcher.dispatchAndWaitForDuccReply(ev);
        } finally {
            dispatcher.close();
        }
    }

    void help()
    {
        HelpFormatter formatter = new HelpFormatter();
        Options options;

        formatter.setWidth(IUiOptions.help_width);

        System.out.println("------------- Register Options ------------------");
        options = makeOptions(registration_options);
        formatter.printHelp(this.getClass().getName(), options);

        System.out.println("\n\n------------- Unregister Options ------------------");
        options = makeOptions(unregister_options);
        formatter.printHelp(this.getClass().getName(), options);

        System.out.println("\n\n------------- Start Options ------------------");
        options = makeOptions(start_options);
        formatter.printHelp(this.getClass().getName(), options);

        System.out.println("\n\n------------- Stop Options ------------------");
        options = makeOptions(stop_options);
        formatter.printHelp(this.getClass().getName(), options);

        System.out.println("\n\n------------- Modify Options ------------------");
        options = makeOptions(modify_options);
        formatter.printHelp(this.getClass().getName(), options);

        System.out.println("\n\n------------- Query Options ------------------");
        options = makeOptions(query_options);
        formatter.printHelp(this.getClass().getName(), options);

        System.exit(1);
    }

    public boolean execute() { return false; }

    static boolean format_reply(UiOption verb, IServiceReply reply)
    {
        // Note
        String ep = reply.getEndpoint()!=null ? reply.getEndpoint() : "";
        String id = reply.getId()!=-1 ? " ID["+String.valueOf(reply.getId())+"]" : "";
        String result = (reply.getReturnCode()) ? " succeeded - " : " failed - ";
        String msg = "Service " + verb + result + reply.getMessage() + " - " + ep + id;
        switch ( verb ) {
           case Register:
           case Unregister:
           case Start:
           case Stop:
           case Modify:
               System.out.println(msg);
               break;
           case Query:
               if (reply.getReturnCode()) {
                   System.out.println(reply.toString());
               } else {
                   System.out.println(msg);
               }
               break;
        }

        return reply.getReturnCode();
    }

    static boolean Register(String[] args)
      throws Exception
    {
        DuccServiceApi api = new DuccServiceApi(null);
        IServiceReply reply = api.register(args);
        return format_reply(UiOption.Register, reply);
    }

    static boolean Unregister(String[] args)
      throws Exception
    {
        DuccServiceApi api = new DuccServiceApi(null);
        IServiceReply reply = api.unregister(args);
        return format_reply(UiOption.Unregister, reply);
    }

    static boolean Start(String[] args)
        throws Exception
    {
        DuccServiceApi api = new DuccServiceApi(null);
        IServiceReply reply = api.start(args);
        return format_reply(UiOption.Start, reply);
    }

    static boolean Stop(String[] args)
        throws Exception
    {
        DuccServiceApi api = new DuccServiceApi(null);
        IServiceReply reply = api.stop(args);
        return format_reply(UiOption.Stop, reply);
    }

    static boolean Modify(String[] args)
        throws Exception
    {
        DuccServiceApi api = new DuccServiceApi(null);
        IServiceReply reply = api.modify(args);
        return format_reply(UiOption.Modify, reply);
    }

    static boolean Query(String[] args)
        throws Exception
    {
        DuccServiceApi api = new DuccServiceApi(null);
        IServiceReply reply = api.query(args);
        return format_reply(UiOption.Query, reply);
    }

    static void Help()
    {
        DuccServiceApi api = new DuccServiceApi(null);
        api.help();
        System.exit(1);
    }

    static UiOption getVerb(String[] args)
    {
        // need to scan args for the verb, and insure only one verb
        UiOption[] verbs = {
            UiOption.Register,
            UiOption.Modify,
            UiOption.Start,
            UiOption.Stop,
            UiOption.Query,
            UiOption.Unregister
        };       
        List<UiOption> check = new ArrayList<UiOption>();
        UiOption reply = UiOption.Help;

        for ( String s : args ) {
            if ( ! s.startsWith("--") ) continue;
            s = s.substring(2);

            if ( s.equals("help") ) Help();

            for ( UiOption v : verbs ) {
                if ( s.equals(v.pname() ) ) {
                    reply = v;
                    check.add(v);
                }
            }
        }

        if ( check.size() > 1 ) {
            String msg = "";
            for ( UiOption o : check ) {
                msg = msg + " " + o;
            }
            throw new IllegalArgumentException("Duplicate service actions: " + msg );
        }

        return reply;
    }


    /*
     * DuccServiceApi <options>
     * Where options:
     *    -r --register <properties>
     *    -u --unregister <id>
     *    -q --query <id>
     *    --start <id>
     *    --stop <id>
     *    -m --modify <properties>
     *    -f --fetch <id>
     *
     *  Properties file:
     *  type = UIMA-AS | Custom
     *  endpoint = <amq endoint>      # if UIMA-AS
     *  broker   = <broker url>       # if UIMA-AS
     *  user     = userid
     *
     *  Service is identified as:
     *  type:endpoint:broker
     *  e.g.  UIMA-AS@FixedSleepAE@tcp://node1
     */

    /**
     * This is the main entrypoint, used by the executable jars and callable directly from the command line.
     *
     * If the invocation is successful, the process exits with return code 0.  Otherwise, it exit
     * with return code 1.
     *
     * @param args arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICES">DUCC CLI reference.</a>
     */
  public static void main(String[] args)
    {       
        boolean rc = false;
        try {
            switch ( getVerb(args) ) {
                case Register:
                    rc = Register(args);
                    break;
                case Unregister:
                    rc = Unregister(args);
                    break;
                case Start:
                    rc = Start(args);
                    break;
                case Stop:
                    rc = Stop(args);
                    break;
                case Modify:
                    rc = Modify(args);
                    break;
                case Query:
                    rc = Query(args);
                    break;
                default:
                    System.out.println("Missing service action (register, unregister, start, stop, modify, or query)");
                    System.exit(1);
            }
        } catch (Exception e) {
            System.out.println("Service call failed: " + e);
            for (String arg : args) {
                if (arg.equals("--debug")) {
                    e.printStackTrace();
                    break;
                }
            }
            System.exit(1);           
        }
        System.exit(rc ? 0 : 1);
  }
 
}
TOP

Related Classes of org.apache.uima.ducc.cli.DuccServiceApi

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.