| /* |
| * 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.Properties; |
| |
| import org.apache.uima.ducc.transport.event.SubmitServiceDuccEvent; |
| import org.apache.uima.ducc.transport.event.SubmitServiceReplyDuccEvent; |
| import org.apache.uima.ducc.transport.event.cli.ServiceRequestProperties; |
| import org.apache.uima.ducc.transport.event.sm.IService.ServiceType; |
| |
| |
| /** |
| * Submit a DUCC service. This is usually called by the DUCC Service Manager but is |
| * made public to enable developer-driven management and testing of services without |
| * formal service registration. |
| */ |
| |
| public class DuccServiceSubmit |
| extends CliBase |
| { |
| |
| //private String jvmarg_string = null; |
| //private Properties jvmargs = null; |
| ServiceRequestProperties requestProperties = new ServiceRequestProperties(); |
| |
| UiOption[] opts = { |
| UiOption.Help, |
| UiOption.Debug, |
| UiOption.Description, |
| UiOption.Administrators, // ( not used directly here, but is allowed in registration ) |
| |
| UiOption.SchedulingClass, |
| UiOption.LogDirectory, |
| UiOption.WorkingDirectory, |
| UiOption.Jvm, |
| UiOption.ProcessJvmArgs, |
| UiOption.Classpath, |
| UiOption.Environment, |
| UiOption.ProcessMemorySize, |
| UiOption.ProcessDD, |
| UiOption.ProcessExecutable, |
| UiOption.ProcessExecutableArgs, |
| UiOption.ProcessInitializationTimeMax, |
| |
| UiOption.ProcessDebug, |
| |
| UiOption.InstanceFailureLimit, |
| UiOption.Specification, |
| UiOption.ServiceDependency, |
| UiOption.ServiceRequestEndpoint, |
| UiOption.ServiceLinger, |
| |
| UiOption.ServicePingArguments, |
| UiOption.ServiceId, |
| UiOption.ServicePingClass, |
| UiOption.ServicePingClasspath, |
| UiOption.ServicePingJvmArgs, |
| UiOption.ServicePingTimeout, |
| UiOption.ServicePingDoLog, |
| |
| UiOption.InstanceFailureWindow, |
| UiOption.InstanceFailureLimit, |
| UiOption.InstanceInitFailureLimit, |
| }; |
| |
| /** |
| * @param args Array of string arguments as described in the |
| * <a href="/doc/duccbook.html#DUCC_CLI_SERVICE_SUBMIT">DUCC CLI reference.</a> |
| */ |
| public DuccServiceSubmit(String[] args) |
| throws Exception |
| { |
| init(this.getClass().getName(), opts, args, requestProperties, null); |
| } |
| |
| /** |
| * @param args List of string arguments as described in the |
| * <a href="/doc/duccbook.html#DUCC_CLI_SERVICE_SUBMIT">DUCC CLI reference.</a> |
| */ |
| public DuccServiceSubmit(ArrayList<String> args) |
| throws Exception |
| { |
| this(args.toArray(new String[args.size()])); |
| } |
| |
| /** |
| * @param props Properties file of arguments, as described in the |
| * <a href="/doc/duccbook.html#DUCC_CLI_SERVICE_SUBMIT">DUCC CLI reference.</a> |
| */ |
| public DuccServiceSubmit(Properties props) |
| throws Exception |
| { |
| init (this.getClass().getName(), opts, props, requestProperties, null); |
| } |
| |
| protected void enrich_parameters_for_debug(Properties props) |
| throws Exception |
| { |
| try { |
| int debug_port = -1; |
| String debug_host = null; |
| |
| // we allow both jd and jp to debug, but the ports have to differ |
| String do_debug = UiOption.ProcessDebug.pname(); |
| if ( props.containsKey(do_debug) ) { |
| String deb = props.getProperty(do_debug); |
| if ( deb == null ) { |
| throw new IllegalArgumentException("Missing port for " + do_debug); |
| } |
| |
| if ( deb.equals("off") ) { |
| System.out.println("Note: Ignoring process_debug = off"); |
| return; |
| } |
| |
| String[] parts = deb.split(":"); |
| if ( parts.length != 2 ) { |
| System.out.println("Warning: process_debug must be of the form host: port. Found '" + deb + "'. Ignoring debug."); |
| return; |
| } |
| |
| debug_host = parts[0]; |
| debug_port = Integer.parseInt(parts[1]); |
| |
| String debug_jvmargs = "-Xdebug -Xrunjdwp:transport=dt_socket,address=" + debug_host + ":" + debug_port; |
| String jvmargs = props.getProperty(UiOption.ProcessJvmArgs.pname()); |
| if (jvmargs == null) { |
| jvmargs = debug_jvmargs; |
| } else { |
| jvmargs += " " + debug_jvmargs; |
| } |
| props.put(UiOption.ProcessJvmArgs.pname(), jvmargs); |
| |
| // For debugging, if the JP is being debugged, insure these are conservative |
| props.setProperty(UiOption.ProcessDeploymentsMax.pname(), "1"); |
| props.setProperty(UiOption.ProcessFailuresLimit.pname(), "1"); |
| } |
| |
| } catch (NumberFormatException e) { |
| throw new IllegalArgumentException("Invalid debug port (not numeric)"); |
| } |
| } |
| |
| /** |
| * Execute collects the service parameters, does basic error and correctness checking, and sends |
| * the job properties to the DUCC orchestrator for execution. |
| * |
| * @return True if the orchestrator accepts the service; false otherwise. |
| */ |
| public boolean execute() |
| throws Exception |
| { |
| // |
| // Need to 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); |
| |
| String endpoint = requestProperties.getProperty(UiOption.ServiceRequestEndpoint.pname()); |
| |
| boolean isUimaAs = true; |
| |
| if (endpoint == null || endpoint.startsWith(ServiceType.UimaAs.decode())) { |
| requestProperties.put(UiOption.ServiceTypeUima.pname(), ""); |
| if (uimaDD == null) { |
| message("ERROR: Must specify --process_DD for UIMA-AS services"); |
| return false; |
| } |
| if (customCmd != null) { |
| message("WARN: --process_executable is ignored for UIMA-AS services"); |
| } |
| |
| // This should have already been done when registered, but perhaps not in old services. |
| String key_cp = UiOption.Classpath.pname(); |
| if (!cli_props.containsKey(key_cp)) { |
| cli_props.setProperty(key_cp, System.getProperty("java.class.path")); |
| } |
| |
| // |
| // Always extract the endpoint from the DD since when it is explicitly specified it must match. |
| // |
| try { |
| String dd = (String) requestProperties.get(UiOption.ProcessDD.pname()); |
| String wd = (String) requestProperties.get(UiOption.WorkingDirectory.pname()); |
| String jvmarg_string = requestProperties.getProperty(UiOption.ProcessJvmArgs.pname()); |
| String inferred_endpoint = DuccUiUtilities.getEndpoint(wd, dd, jvmarg_string); |
| if (endpoint == null) { |
| endpoint = inferred_endpoint; |
| requestProperties.put(UiOption.ServiceRequestEndpoint.pname(), endpoint); |
| } else if (!inferred_endpoint.equals(endpoint)) { |
| message("ERROR: Endpoint from --service_request_endpoint does not match endpoint ectracted from UIMA DD" |
| + "\n--service_request_endpoint: " |
| + endpoint |
| + "\nextracted: : " + inferred_endpoint); |
| return false; |
| } |
| if (debug) { |
| System.out.println("service_endpoint: " + endpoint); |
| } |
| } catch (IllegalArgumentException e) { |
| message("ERROR: Cannot read/process DD descriptor for endpoint:", e.getMessage()); |
| return false; |
| } |
| |
| } else if (endpoint.startsWith(ServiceType.Custom.decode())) { |
| isUimaAs = false; |
| if (uimaDD != null) { |
| message("WARN: --process_DD is ignored for CUSTOM endpoints"); |
| } |
| requestProperties.put(UiOption.ServiceTypeCustom.pname(), ""); |
| |
| } else { |
| return false; |
| } |
| |
| if ( ! check_service_dependencies(endpoint) ) { |
| return false; |
| } |
| |
| if ( debug ) { |
| requestProperties.dump(); |
| } |
| |
| if ( isUimaAs ) { |
| enrich_parameters_for_debug(requestProperties); |
| } |
| |
| requestProperties.put(UiOption.ProcessPipelineCount.pname(), "1"); // enforce this - OR will complain if it's missing |
| |
| SubmitServiceDuccEvent ev = new SubmitServiceDuccEvent(requestProperties, CliVersion.getVersion()); |
| SubmitServiceReplyDuccEvent reply = null; |
| |
| try { |
| reply = (SubmitServiceReplyDuccEvent) dispatcher.dispatchAndWaitForDuccReply(ev); |
| } catch (Exception e) { |
| message("Service instance not submitted:", e.getMessage()); |
| return false; |
| } finally { |
| dispatcher.close(); |
| } |
| |
| /* |
| * process reply |
| */ |
| boolean rc = extractReply(reply); |
| |
| if ( rc ) { |
| saveSpec(DuccUiConstants.service_specification_properties, requestProperties); |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * Main method, as used by the executable jar or direct java invocation. |
| * @param args arguments as described in the <a href="/doc/duccbook.html#DUCC_CLI_SERVICE_SUBMIT">DUCC CLI reference.</a> |
| */ |
| public static void main(String[] args) { |
| try { |
| // Instantiate the object with args similar to the CLI, or a pre-built properties file |
| DuccServiceSubmit ds = new DuccServiceSubmit(args); |
| |
| // Run the API. If process_attach_console was specified in the args, a console listener is |
| // started but this call does NOT block on it. |
| boolean rc = ds.execute(); |
| |
| // If the return is 'true' then as best the API can tell, the submit worked |
| if ( rc ) { |
| |
| // Fetch the Ducc ID |
| System.out.println("Service instance " + ds.getDuccId() + " submitted"); |
| System.exit(0); |
| } else { |
| System.out.println("Could not submit Service"); |
| System.exit(1); |
| } |
| } catch (Exception e) { |
| System.out.println("Cannot initialize: " + e.getMessage()); |
| System.exit(1); |
| } |
| |
| } |
| |
| } |