helix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zzh...@apache.org
Subject git commit: HELIX-45: Standalone helix agent
Date Mon, 08 Apr 2013 07:14:49 GMT
Updated Branches:
  refs/heads/master 1b686456c -> 36e3b2c18


HELIX-45: Standalone helix agent


Project: http://git-wip-us.apache.org/repos/asf/incubator-helix/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-helix/commit/36e3b2c1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-helix/tree/36e3b2c1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-helix/diff/36e3b2c1

Branch: refs/heads/master
Commit: 36e3b2c186e8be9ed3259a62b74c977467a7fcfe
Parents: 1b68645
Author: zzhang <zzhang5@uci.edu>
Authored: Mon Apr 8 00:14:32 2013 -0700
Committer: zzhang <zzhang5@uci.edu>
Committed: Mon Apr 8 00:14:32 2013 -0700

----------------------------------------------------------------------
 helix-agent/pom.xml                                |   68 ++++++
 helix-agent/src/main/config/log4j.properties       |   31 +++
 .../org/apache/helix/agent/AgentStateModel.java    |  176 +++++++++++++++
 .../apache/helix/agent/AgentStateModelFactory.java |   32 +++
 .../org/apache/helix/agent/CommandAttribute.java   |   53 +++++
 .../java/org/apache/helix/agent/CommandConfig.java |  102 +++++++++
 .../org/apache/helix/agent/HelixAgentMain.java     |  157 +++++++++++++
 .../java/org/apache/helix/agent/SystemUtil.java    |  113 +++++++++
 pom.xml                                            |    1 +
 9 files changed, 733 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/pom.xml
----------------------------------------------------------------------
diff --git a/helix-agent/pom.xml b/helix-agent/pom.xml
new file mode 100644
index 0000000..d3fe572
--- /dev/null
+++ b/helix-agent/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.helix</groupId>
+    <artifactId>helix</artifactId>
+    <version>0.6.1-incubating-SNAPSHOT</version>
+  </parent>
+  <artifactId>helix-agent</artifactId>
+  <name>Apache Helix :: HelixAgent</name>
+
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>helix-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.noelios.restlet</groupId>
+      <artifactId>com.noelios.restlet</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>appassembler-maven-plugin</artifactId>
+        <configuration>
+          <programs>
+            <program>
+              <mainClass>org.apache.helix.agent.HelixAgentMain</mainClass>
+              <name>start-helix-agent</name>
+            </program>
+          </programs>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/config/log4j.properties
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/config/log4j.properties b/helix-agent/src/main/config/log4j.properties
new file mode 100644
index 0000000..91fac03
--- /dev/null
+++ b/helix-agent/src/main/config/log4j.properties
@@ -0,0 +1,31 @@
+#
+# 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.
+##
+
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=DEBUG,A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache=ERROR

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModel.java
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModel.java b/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModel.java
new file mode 100644
index 0000000..aeda1db
--- /dev/null
+++ b/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModel.java
@@ -0,0 +1,176 @@
+package org.apache.helix.agent;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.helix.ExternalCommand;
+import org.apache.helix.HelixManager;
+import org.apache.helix.NotificationContext;
+import org.apache.helix.ZNRecord;
+import org.apache.helix.agent.SystemUtil.ProcessStateCode;
+import org.apache.helix.model.ConfigScope;
+import org.apache.helix.model.HelixConfigScope;
+import org.apache.helix.model.Message;
+import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
+import org.apache.helix.model.builder.ConfigScopeBuilder;
+import org.apache.helix.model.builder.HelixConfigScopeBuilder;
+import org.apache.helix.participant.statemachine.StateModel;
+import org.apache.helix.participant.statemachine.StateModelInfo;
+import org.apache.helix.participant.statemachine.Transition;
+import org.apache.log4j.Logger;
+
+@StateModelInfo(initialState = "OFFLINE", states = {})
+public class AgentStateModel extends StateModel
+{
+  private static final Logger _logger = Logger.getLogger(AgentStateModel.class);
+  private static final int MONITOR_PERIOD_BASE = 1000;  // 1 second
+  private static Pattern pattern = Pattern.compile("(\\{.+?\\})");
+
+  private static String buildKey(String fromState, String toState, CommandAttribute attribute)
{
+    return fromState + "-" + toState + "." + attribute.getName();
+  }
+  
+  private static String instantiateByMessage(String string, Message message) {
+    Matcher matcher = pattern.matcher(string);
+    String result = string;
+    while (matcher.find()) {
+      String var = matcher.group();
+      result = result.replace(var, message.getAttribute(Message.Attributes.valueOf(var.substring(1,
var.length() - 1))));
+    }
+
+    return result;
+  }
+    
+  @Transition(to = "*", from = "*")
+  public void genericStateTransitionHandler(Message message,
+      NotificationContext context) throws Exception
+  {
+    // first try get command from message
+    String cmd = message.getRecord().getSimpleField(CommandAttribute.COMMAND.getName());
+    String workingDir = message.getRecord().getSimpleField(CommandAttribute.WORKING_DIR.getName());
+    String timeout = message.getRecord().getSimpleField(CommandAttribute.TIMEOUT.getName());
+    String pidFile = message.getRecord().getSimpleField(CommandAttribute.PID_FILE.getName());
+    
+    HelixManager manager = context.getManager();
+    String clusterName = manager.getClusterName();
+    String fromState = message.getFromState();
+    String toState = message.getToState();
+    
+    // construct keys for command-config
+    String cmdKey = buildKey(fromState, toState, CommandAttribute.COMMAND);
+    String workingDirKey = buildKey(fromState, toState, CommandAttribute.WORKING_DIR);
+    String timeoutKey = buildKey(fromState, toState, CommandAttribute.TIMEOUT);
+    String pidFileKey = buildKey(fromState, toState, CommandAttribute.PID_FILE);
+    List<String> cmdConfigKeys = Arrays.asList(cmdKey, workingDirKey, timeoutKey, pidFileKey);
+    
+    // read command from resource-scope configures
+    if (cmd == null) {
+      HelixConfigScope resourceScope = new HelixConfigScopeBuilder(ConfigScopeProperty.RESOURCE)
+                                          .forCluster(clusterName)
+                                          .forResource(message.getResourceName())
+                                          .build();
+      Map<String, String> cmdKeyValueMap = manager.getConfigAccessor().get(resourceScope,
cmdConfigKeys);
+      cmd = cmdKeyValueMap.get(cmdKey);
+      workingDir = cmdKeyValueMap.get(workingDirKey);
+      timeout = cmdKeyValueMap.get(timeoutKey);
+      pidFile = cmdKeyValueMap.get(pidFileKey);
+    }
+    
+    // if resource-scope doesn't contain command, fall back to cluster-scope configures
+    if (cmd == null) {
+      HelixConfigScope clusterScope = new HelixConfigScopeBuilder(ConfigScopeProperty.CLUSTER)
+                                          .forCluster(clusterName)
+                                          .build();
+      Map<String, String> cmdKeyValueMap = manager.getConfigAccessor().get(clusterScope,
cmdConfigKeys);
+      cmd = cmdKeyValueMap.get(cmdKey);
+      workingDir = cmdKeyValueMap.get(workingDirKey);
+      timeout = cmdKeyValueMap.get(timeoutKey);
+      pidFile = cmdKeyValueMap.get(pidFileKey);
+    }
+
+    if (cmd == null)
+    {
+      throw new Exception("Unable to find command for transition from:"
+          + message.getFromState() + " to:" + message.getToState());
+    }
+    _logger.info("Executing command: " + cmd + ", using workingDir: " + workingDir
+        + ", timeout: " + timeout + ", on " + manager.getInstanceName());
+    
+    // skip nop command
+    if (cmd.equals(CommandAttribute.NOP.getName())) {
+      return;
+    }
+    
+    // split the cmd to actual cmd and args[]
+    String cmdSplits[] = cmd.trim().split("\\s+");
+    String cmdValue = cmdSplits[0];
+    String args[] = Arrays.copyOfRange(cmdSplits, 1, cmdSplits.length);
+    
+    // get the command-execution timeout
+    long timeoutValue = 0; // 0 means wait for ever
+    if (timeout != null) {
+      try {
+        timeoutValue = Long.parseLong(timeout);
+      } catch (NumberFormatException e) {
+        // OK to use 0
+      }
+    }
+    ExternalCommand externalCmd = ExternalCommand.executeWithTimeout(new File(workingDir),

+        cmdValue, timeoutValue, args);
+    
+    // debug
+    // System.out.println("command output:\n" + externalCmd.getStringOutput());
+    
+    int exitValue = externalCmd.exitValue();
+    _logger.info("Executed command: " + cmd + ", exitValue: " + exitValue);
+    
+    // if exit-value != 0, transition fails
+    if (exitValue != 0) {
+      throw new Exception("fail to execute command: " + cmd + ", exitValue: " + exitValue

+          + ", error: " + externalCmd.getStringError());
+    }
+    
+    // monitor pid if pidFile exists
+    if (pidFile == null) {
+      // no pid to monitor
+      return;
+    }
+    
+    String pidFileValue = instantiateByMessage(pidFile, message);
+    String pid = SystemUtil.getPidFromFile(new File(pidFileValue));
+    
+    // monitor pid
+    ProcessStateCode processState = SystemUtil.getProcessState(pid);
+    while (processState != null) {
+      if (processState == ProcessStateCode.Z) {
+        throw new Exception("process: " + pid + " is in zombie state");
+      }
+      Thread.sleep(new Random().nextInt(MONITOR_PERIOD_BASE) + MONITOR_PERIOD_BASE);
+      processState = SystemUtil.getProcessState(pid);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModelFactory.java
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModelFactory.java
b/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModelFactory.java
new file mode 100644
index 0000000..99ff884
--- /dev/null
+++ b/helix-agent/src/main/java/org/apache/helix/agent/AgentStateModelFactory.java
@@ -0,0 +1,32 @@
+package org.apache.helix.agent;
+
+/*
+ * 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.
+ */
+
+import org.apache.helix.participant.statemachine.StateModelFactory;
+
+public class AgentStateModelFactory extends StateModelFactory<AgentStateModel>{
+  
+  @Override
+  public AgentStateModel createNewStateModel(String partitionKey)
+  {
+    AgentStateModel model = new AgentStateModel();
+    return model;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/java/org/apache/helix/agent/CommandAttribute.java
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/java/org/apache/helix/agent/CommandAttribute.java b/helix-agent/src/main/java/org/apache/helix/agent/CommandAttribute.java
new file mode 100644
index 0000000..3f09123
--- /dev/null
+++ b/helix-agent/src/main/java/org/apache/helix/agent/CommandAttribute.java
@@ -0,0 +1,53 @@
+package org.apache.helix.agent;
+
+/*
+ * 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.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum CommandAttribute {
+  COMMAND ("command"),
+  WORKING_DIR ("command.workingDir"),
+  TIMEOUT ("command.timeout"),
+  PID_FILE("command.pidFile"),
+  NOP ("nop");
+  
+  // map from name to value
+  private static final Map<String, CommandAttribute> map = new HashMap<String, CommandAttribute>();
+  static {
+    for (CommandAttribute attr : CommandAttribute.values()) {
+      map.put(attr.getName(), attr);
+    }
+  }
+  
+  private final String _name;
+  
+  private CommandAttribute(String name) {
+    _name = name;
+  }
+
+  public String getName() {
+    return _name;
+  }
+  
+  public static CommandAttribute getCommandAttributeByName(String name) {
+    return map.get(name);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/java/org/apache/helix/agent/CommandConfig.java
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/java/org/apache/helix/agent/CommandConfig.java b/helix-agent/src/main/java/org/apache/helix/agent/CommandConfig.java
new file mode 100644
index 0000000..59d5016
--- /dev/null
+++ b/helix-agent/src/main/java/org/apache/helix/agent/CommandConfig.java
@@ -0,0 +1,102 @@
+package org.apache.helix.agent;
+
+/*
+ * 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.
+ */
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class CommandConfig {
+  private final String _fromState;
+  private final String _toState;
+  private final String _command;
+  private final String _workingDir;
+  private final String _timeout;
+  private final String _pidFile;
+
+  public CommandConfig(String fromState, String toState, String command,
+      String workingDir, String timeout, String pidFile) {
+    _fromState = fromState;
+    _toState = toState;
+    _command = command;
+    _workingDir = workingDir;
+    _timeout = timeout;
+    _pidFile = pidFile;
+
+  }
+  
+  private String buildKey(String fromState, String toState, CommandAttribute attribute) {
+    return fromState + "-" + toState + "." + attribute.getName();
+  }
+
+  public Map<String, String> toKeyValueMap() {
+    Map<String, String> map = new TreeMap<String, String>();
+    map.put(buildKey(_fromState, _toState, CommandAttribute.COMMAND), _command);
+    map.put(buildKey(_fromState, _toState, CommandAttribute.WORKING_DIR), _workingDir);
+    map.put(buildKey(_fromState, _toState, CommandAttribute.TIMEOUT), _timeout);
+    map.put(buildKey(_fromState, _toState, CommandAttribute.PID_FILE), _pidFile);
+    
+    return map;
+  }
+  
+  /**
+   * builder for command-config
+   *
+   */
+  public static class Builder {
+    private String _fromState;
+    private String _toState;
+    private String _command;
+    private String _workingDir;
+    private String _timeout;
+    private String _pidFile;
+    
+    public Builder setTransition(String fromState, String toState) {
+      _fromState = fromState;
+      _toState = toState;
+      return this;
+    }
+    
+    public Builder setCommand(String command) {
+      _command = command;
+      return this;
+    }
+    
+    public Builder setCommandWorkingDir(String workingDir) {
+      _workingDir = workingDir;
+      return this;
+    }
+    
+    public Builder setCommandTimeout(String timeout) {
+      _timeout = timeout;
+      return this;
+    }
+    
+    public Builder setPidFile(String pidFile) {
+      _pidFile = pidFile;
+      return this;
+    }
+    
+    public CommandConfig build() {
+      return new CommandConfig(_fromState, _toState, _command, _workingDir, 
+          _timeout, _pidFile);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/java/org/apache/helix/agent/HelixAgentMain.java
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/java/org/apache/helix/agent/HelixAgentMain.java b/helix-agent/src/main/java/org/apache/helix/agent/HelixAgentMain.java
new file mode 100644
index 0000000..6568d73
--- /dev/null
+++ b/helix-agent/src/main/java/org/apache/helix/agent/HelixAgentMain.java
@@ -0,0 +1,157 @@
+package org.apache.helix.agent;
+
+/*
+ * 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.
+ */
+
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.helix.HelixManager;
+import org.apache.helix.InstanceType;
+import org.apache.helix.manager.zk.ZKHelixManager;
+import org.apache.helix.participant.StateMachineEngine;
+import org.apache.log4j.Logger;
+
+public class HelixAgentMain {
+  private static Logger LOG = Logger.getLogger(HelixAgentMain.class);
+
+  public static final String zkAddr = "zkSvr";
+  public static final String cluster = "cluster";
+  public static final String help = "help";
+  public static final String instanceName = "instanceName";
+  public static final String stateModel = "stateModel";
+
+  // hack: OptionalBuilder is not thread safe
+  @SuppressWarnings("static-access")
+  synchronized private static Options constructCommandLineOptions()
+  {
+    Option helpOption = OptionBuilder.withLongOpt(help)
+        .withDescription("Prints command-line options info").create();
+
+    Option zkAddrOption = OptionBuilder.withLongOpt(zkAddr)
+        .hasArgs(1)
+        .isRequired(true)
+        .withArgName("ZookeeperServerAddress(Required)")
+        .withDescription("Provide zookeeper address").create();
+
+    Option clusterOption = OptionBuilder.withLongOpt(cluster)
+        .hasArgs(1)
+        .isRequired(true)
+        .withArgName("Cluster name (Required)")
+        .withDescription("Provide cluster name").create();
+
+    Option instanceNameOption = OptionBuilder.withLongOpt(instanceName)
+        .hasArgs(1)
+        .isRequired(true)
+        .withArgName("Helix agent name (Required)")
+        .withDescription("Provide Helix agent name").create();
+
+    Option stateModelOption = OptionBuilder.withLongOpt(stateModel)
+        .hasArgs(1)
+        .isRequired(true)
+        .withArgName("State model name (Required)")
+        .withDescription("Provide state model name").create();
+
+    Options options = new Options();
+    options.addOption(helpOption);
+    options.addOption(zkAddrOption);
+    options.addOption(clusterOption);
+    options.addOption(instanceNameOption);
+    options.addOption(stateModelOption);
+
+    return options;
+  }
+
+  public static void printUsage(Options cliOptions)
+  {
+    HelpFormatter helpFormatter = new HelpFormatter();
+    helpFormatter.setWidth(1000);
+    helpFormatter.printHelp("java " + HelixAgentMain.class.getName(), cliOptions);
+  }
+
+  public static CommandLine processCommandLineArgs(String[] cliArgs) throws Exception
+  {
+    CommandLineParser cliParser = new GnuParser();
+    Options cliOptions = constructCommandLineOptions();
+
+    try
+    {
+      return cliParser.parse(cliOptions, cliArgs);
+    } catch (ParseException pe)
+    {
+      LOG.error("fail to parse command-line options. cliArgs: " + Arrays.toString(cliArgs),
pe);
+      printUsage(cliOptions);
+      System.exit(1);
+    }
+    return null;
+  }
+
+  // NOT working for kill -9, working for kill -2/-15
+  static class HelixAgentShutdownHook extends Thread
+  {
+    final HelixManager _manager;
+
+    HelixAgentShutdownHook(HelixManager manager)
+    {
+      _manager = manager;
+    }
+
+    @Override
+    public void run()
+    {
+      LOG.info("HelixAgentShutdownHook invoked. agent: " + _manager.getInstanceName());
+      if (_manager != null && _manager.isConnected())
+      _manager.disconnect();
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    CommandLine cmd = processCommandLineArgs(args);
+    String zkAddress = cmd.getOptionValue(zkAddr);
+    String clusterName = cmd.getOptionValue(cluster);
+    String instance = cmd.getOptionValue(instanceName);
+    String stateModelName = cmd.getOptionValue(stateModel);
+
+    HelixManager manager = new ZKHelixManager(clusterName, 
+        instance, InstanceType.PARTICIPANT, zkAddress);
+    
+    StateMachineEngine stateMach = manager.getStateMachineEngine();
+    stateMach.registerStateModelFactory(stateModelName, new AgentStateModelFactory());
+
+    Runtime.getRuntime().addShutdownHook(new HelixAgentShutdownHook(manager));
+
+    try {
+      manager.connect();
+      Thread.currentThread().join();
+    } catch (Exception e) {
+      LOG.error(e);
+    } finally {
+      if (manager != null && manager.isConnected()) {
+        manager.disconnect();
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/helix-agent/src/main/java/org/apache/helix/agent/SystemUtil.java
----------------------------------------------------------------------
diff --git a/helix-agent/src/main/java/org/apache/helix/agent/SystemUtil.java b/helix-agent/src/main/java/org/apache/helix/agent/SystemUtil.java
new file mode 100644
index 0000000..b256581
--- /dev/null
+++ b/helix-agent/src/main/java/org/apache/helix/agent/SystemUtil.java
@@ -0,0 +1,113 @@
+package org.apache.helix.agent;
+
+/*
+ * 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.helix.ExternalCommand;
+import org.apache.log4j.Logger;
+
+public class SystemUtil {
+  public static final String OS_NAME = System.getProperty("os.name");
+  private static Logger LOG = Logger.getLogger(SystemUtil.class);
+
+  /**
+   * PROCESS STATE CODES
+   */
+  public static enum ProcessStateCode {
+    // Here are the different values that the s, stat and state output specifiers (header
"STAT" or "S") 
+    //   will display to describe the state of a process.
+    D ("Uninterruptible sleep (usually IO)"),
+    R ("Running or runnable (on run queue)"),
+    S ("Interruptible sleep (waiting for an event to complete)"),
+    T ("Stopped, either by a job control signal or because it is being traced."),
+    W ("paging (not valid since the 2.6.xx kernel)"),
+    X ("dead (should never be seen)"),
+    Z ("Defunct (\"zombie\") process, terminated but not reaped by its parent.");
+
+    private final String _description;
+        
+    private ProcessStateCode(String description) {
+      _description = description;
+    }
+    
+    public String getDescription() {
+      return _description;
+    }
+  }
+  
+  public static ProcessStateCode getProcessState(String processId) throws Exception {
+    if (OS_NAME.equals("Mac OS X") || OS_NAME.equals("Linux")) {
+      ExternalCommand cmd = ExternalCommand.start("ps", processId);
+      cmd.waitFor();
+      
+      // split by new lines
+      // should get 2 lines for existing process, 1 line for non-existing process
+      String lines[] = cmd.getStringOutput().split("[\\r\\n]+");
+      if (lines.length != 2) {
+        LOG.info("process: " + processId + " not exist");
+        return null;
+      }
+      
+      // split by whitespace, 1st line is attributes, 2nd line is actual values
+      // should be parallel arrays
+      String attributes[] = lines[0].trim().split("\\s+");
+      String values[] = lines[1].trim().split("\\s+");
+      
+      Character processStateCodeChar = null;
+      for (int i = 0; i < attributes.length; i++) {
+        String attribute = attributes[i];
+        // header "STAT" or "S"
+        if ("STAT".equals(attribute) || "S".equals(attribute)) {
+          // first character should be major process state code
+          processStateCodeChar = values[i].charAt(0);
+          break;
+        }
+      }
+      
+      return ProcessStateCode.valueOf(Character.toString(processStateCodeChar));
+    } else {
+      throw new UnsupportedOperationException("Not supported OS: " + OS_NAME);
+    }
+  }
+  
+  public static String getPidFromFile(File file) {
+    BufferedReader br = null;
+    try {
+      br = new BufferedReader(new FileReader(file));
+      String line = br.readLine();
+      return line;
+    } catch (IOException e) {
+      LOG.error("fail to read pid from pidFile: " + file, e);
+    } finally {
+      if (br != null) {
+        try {
+          br.close();
+        } catch (IOException e) {
+          LOG.error("fail to close file: " + file, e);
+        }
+      }
+    }
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/36e3b2c1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3396928..4440b3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -116,6 +116,7 @@ under the License.
     <module>helix-core</module>
     <module>helix-admin-webapp</module>
     <module>mockservice</module>
+    <module>helix-agent</module>
     <module>recipes</module>
   </modules>
 


Mime
View raw message