You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2017/03/02 20:51:51 UTC

[05/10] incubator-metron git commit: METRON-503: Metron REST API this closes apache/incubator-metron#316

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/GrokConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/GrokConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/GrokConfig.java
new file mode 100644
index 0000000..1166e9c
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/GrokConfig.java
@@ -0,0 +1,36 @@
+/**
+ * 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.metron.rest.config;
+
+import oi.thekraken.grok.api.Grok;
+import oi.thekraken.grok.api.exception.GrokException;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.io.InputStreamReader;
+
+@Configuration
+public class GrokConfig {
+
+    @Bean
+    public Grok commonGrok() throws GrokException {
+        Grok grok = new Grok();
+        grok.addPatternFromReader(new InputStreamReader(getClass().getResourceAsStream("/patterns/common")));
+        return grok;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/HadoopConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/HadoopConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/HadoopConfig.java
new file mode 100644
index 0000000..f4b8bdd
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/HadoopConfig.java
@@ -0,0 +1,38 @@
+/**
+ * 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.metron.rest.config;
+
+import org.apache.metron.rest.MetronRestConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+@Configuration
+public class HadoopConfig {
+
+    @Autowired
+    private Environment environment;
+
+    @Bean
+    public org.apache.hadoop.conf.Configuration configuration() {
+        org.apache.hadoop.conf.Configuration configuration = new org.apache.hadoop.conf.Configuration();
+        configuration.set("fs.defaultFS", environment.getProperty(MetronRestConstants.HDFS_URL_SPRING_PROPERTY, MetronRestConstants.DEFAULT_HDFS_URL));
+        return configuration;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/KafkaConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/KafkaConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/KafkaConfig.java
new file mode 100644
index 0000000..8044405
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/KafkaConfig.java
@@ -0,0 +1,61 @@
+/**
+ * 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.metron.rest.config;
+
+import kafka.utils.ZkUtils;
+import org.I0Itec.zkclient.ZkClient;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.metron.rest.MetronRestConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+
+import java.util.Properties;
+
+import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+
+@Configuration
+@Profile("!" + TEST_PROFILE)
+public class KafkaConfig {
+
+  @Autowired
+  private Environment environment;
+
+  @Autowired
+  private ZkClient zkClient;
+
+  @Bean
+  public ZkUtils zkUtils() {
+    return ZkUtils.apply(zkClient, false);
+  }
+
+  @Bean(destroyMethod="close")
+  public KafkaConsumer<String, String> kafkaConsumer() {
+    Properties props = new Properties();
+    props.put("bootstrap.servers", environment.getProperty(MetronRestConstants.KAFKA_BROKER_URL_SPRING_PROPERTY));
+    props.put("group.id", "metron-config");
+    props.put("enable.auto.commit", "false");
+    props.put("auto.commit.interval.ms", "1000");
+    props.put("session.timeout.ms", "30000");
+    props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
+    props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
+    return new KafkaConsumer<>(props);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/MvcConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/MvcConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/MvcConfig.java
new file mode 100644
index 0000000..ed82164
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/MvcConfig.java
@@ -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.
+ */
+package org.apache.metron.rest.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class MvcConfig extends WebMvcConfigurerAdapter {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**");
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/RestTemplateConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/RestTemplateConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/RestTemplateConfig.java
new file mode 100644
index 0000000..8fea90c
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/RestTemplateConfig.java
@@ -0,0 +1,36 @@
+/**
+ * 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.metron.rest.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.web.client.RestTemplate;
+
+import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+
+@Configuration
+@Profile("!" + TEST_PROFILE)
+public class RestTemplateConfig {
+
+    @Bean
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/StormConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/StormConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/StormConfig.java
new file mode 100644
index 0000000..d6d0cff
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/StormConfig.java
@@ -0,0 +1,48 @@
+/**
+ * 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.metron.rest.config;
+
+import org.apache.metron.rest.service.impl.DockerStormCLIWrapper;
+import org.apache.metron.rest.service.impl.StormCLIWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+
+import java.util.Arrays;
+
+import static org.apache.metron.rest.MetronRestConstants.DOCKER_PROFILE;
+import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+
+@Configuration
+@Profile("!" + TEST_PROFILE)
+public class StormConfig {
+
+  @Autowired
+  private Environment environment;
+
+  @Bean
+  public StormCLIWrapper stormCLIClientWrapper() {
+    if (Arrays.asList(environment.getActiveProfiles()).contains(DOCKER_PROFILE)) {
+      return new DockerStormCLIWrapper();
+    } else {
+      return new StormCLIWrapper();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/SwaggerConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/SwaggerConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/SwaggerConfig.java
new file mode 100644
index 0000000..9e82196
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/SwaggerConfig.java
@@ -0,0 +1,39 @@
+/**
+ * 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.metron.rest.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+  @Bean
+  public Docket api() {
+    return new Docket(DocumentationType.SWAGGER_2)
+            .select()
+            .apis(RequestHandlerSelectors.any())
+            .paths(PathSelectors.any())
+            .build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java
new file mode 100644
index 0000000..789098a
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java
@@ -0,0 +1,93 @@
+/**
+ * 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.metron.rest.config;
+
+import org.apache.metron.rest.MetronRestConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.sql.DataSource;
+import java.util.Arrays;
+import java.util.List;
+
+@Configuration
+@EnableWebSecurity
+@Controller
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Autowired
+    private Environment environment;
+
+    @RequestMapping({"/login", "/logout", "/sensors", "/sensors*/**"})
+    public String handleNGRequests() {
+        return "forward:/index.html";
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .authorizeRequests()
+                .antMatchers("/", "/home", "/login").permitAll()
+                .antMatchers("/app/**").permitAll()
+                .antMatchers("/vendor/**").permitAll()
+                .antMatchers("/fonts/**").permitAll()
+                .antMatchers("/assets/images/**").permitAll()
+                .antMatchers("/*.js").permitAll()
+                .antMatchers("/*.ttf").permitAll()
+                .antMatchers("/*.woff2").permitAll()
+                .anyRequest().authenticated()
+                .and().httpBasic()
+                .and()
+                .logout()
+                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
+                .invalidateHttpSession(true)
+                .deleteCookies("JSESSIONID");
+        if (Arrays.asList(environment.getActiveProfiles()).contains(MetronRestConstants.CSRF_ENABLE_PROFILE)) {
+            http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
+        } else {
+            http.csrf().disable();
+        }
+    }
+
+    @Autowired
+    private DataSource dataSource;
+
+    @Autowired
+    public void configureJdbc(AuthenticationManagerBuilder auth) throws Exception {
+        List<String> activeProfiles = Arrays.asList(environment.getActiveProfiles());
+        if (activeProfiles.contains(MetronRestConstants.DEV_PROFILE) ||
+                activeProfiles.contains(MetronRestConstants.TEST_PROFILE)) {
+            auth.jdbcAuthentication().dataSource(dataSource)
+                    .withUser("user").password("password").roles("USER").and()
+                    .withUser("user1").password("password").roles("USER").and()
+                    .withUser("user2").password("password").roles("USER").and()
+                    .withUser("admin").password("password").roles("USER", "ADMIN");
+        } else {
+            auth.jdbcAuthentication().dataSource(dataSource);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/ZookeeperConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/ZookeeperConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/ZookeeperConfig.java
new file mode 100644
index 0000000..5f58193
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/ZookeeperConfig.java
@@ -0,0 +1,52 @@
+/**
+ * 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.metron.rest.config;
+
+import kafka.utils.ZKStringSerializer$;
+import org.I0Itec.zkclient.ZkClient;
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.metron.rest.MetronRestConstants;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+
+import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+
+@Configuration
+@Profile("!" + TEST_PROFILE)
+public class ZookeeperConfig {
+
+  @Bean(initMethod = "start", destroyMethod="close")
+  public CuratorFramework client(Environment environment) {
+    int sleepTime = Integer.parseInt(environment.getProperty(MetronRestConstants.CURATOR_SLEEP_TIME));
+    int maxRetries = Integer.parseInt(environment.getProperty(MetronRestConstants.CURATOR_MAX_RETRIES));
+    RetryPolicy retryPolicy = new ExponentialBackoffRetry(sleepTime, maxRetries);
+    return CuratorFrameworkFactory.newClient(environment.getProperty(MetronRestConstants.ZK_URL_SPRING_PROPERTY), retryPolicy);
+  }
+
+  @Bean(destroyMethod="close")
+  public ZkClient zkClient(Environment environment) {
+    int sessionTimeout = Integer.parseInt(environment.getProperty(MetronRestConstants.ZK_CLIENT_SESSION_TIMEOUT));
+    int connectionTimeout = Integer.parseInt(environment.getProperty(MetronRestConstants.ZK_CLIENT_CONNECTION_TIMEOUT));
+    return new ZkClient(environment.getProperty(MetronRestConstants.ZK_URL_SPRING_PROPERTY), sessionTimeout, connectionTimeout, ZKStringSerializer$.MODULE$);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GlobalConfigController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GlobalConfigController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GlobalConfigController.java
new file mode 100644
index 0000000..739d68b
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GlobalConfigController.java
@@ -0,0 +1,79 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.service.GlobalConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/global/config")
+public class GlobalConfigController {
+
+    @Autowired
+    private GlobalConfigService globalConfigService;
+
+    @ApiOperation(value = "Creates or updates the Global Config in Zookeeper")
+    @ApiResponses(value = { @ApiResponse(message = "Global Config updated. Returns saved Global Config JSON", code = 200),
+            @ApiResponse(message = "Global Config created. Returns saved Global Config JSON", code = 201) })
+    @RequestMapping(method = RequestMethod.POST)
+    ResponseEntity<Map<String, Object>> save(@ApiParam(name="globalConfig", value="The Global Config JSON to be saved", required=true)@RequestBody Map<String, Object> globalConfig) throws RestException {
+        if (globalConfigService.get() == null) {
+            return new ResponseEntity<>(globalConfigService.save(globalConfig), HttpStatus.CREATED);
+        } else {
+            return new ResponseEntity<>(globalConfigService.save(globalConfig), HttpStatus.OK);
+        }
+    }
+
+    @ApiOperation(value = "Retrieves the current Global Config from Zookeeper")
+    @ApiResponses(value = { @ApiResponse(message = "Returns current Global Config JSON in Zookeeper", code = 200),
+            @ApiResponse(message = "Global Config JSON was not found in Zookeeper", code = 404) })
+    @RequestMapping(method = RequestMethod.GET)
+    ResponseEntity<Map<String, Object>> get() throws RestException {
+        Map<String, Object> globalConfig = globalConfigService.get();
+        if (globalConfig != null) {
+            return new ResponseEntity<>(globalConfig, HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+
+    @ApiOperation(value = "Deletes the current Global Config from Zookeeper")
+    @ApiResponses(value = { @ApiResponse(message = "Global Config JSON was deleted", code = 200),
+            @ApiResponse(message = "Global Config JSON was not found in Zookeeper", code = 404) })
+    @RequestMapping(method = RequestMethod.DELETE)
+    ResponseEntity<Void> delete() throws RestException {
+        if (globalConfigService.delete()) {
+            return new ResponseEntity<>(HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GrokController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GrokController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GrokController.java
new file mode 100644
index 0000000..2b155b1
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/GrokController.java
@@ -0,0 +1,56 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.GrokValidation;
+import org.apache.metron.rest.service.GrokService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/grok")
+public class GrokController {
+
+    @Autowired
+    private GrokService grokService;
+
+    @ApiOperation(value = "Applies a Grok statement to a sample message")
+    @ApiResponse(message = "JSON results", code = 200)
+    @RequestMapping(value = "/validate", method = RequestMethod.POST)
+    ResponseEntity<GrokValidation> post(@ApiParam(name="grokValidation", value="Object containing Grok statment and sample message", required=true)@RequestBody GrokValidation grokValidation) throws RestException {
+        return new ResponseEntity<>(grokService.validateGrokStatement(grokValidation), HttpStatus.OK);
+    }
+
+    @ApiOperation(value = "Lists the common Grok statements available in Metron")
+    @ApiResponse(message = "JSON object containing pattern label/Grok statements key value pairs", code = 200)
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    ResponseEntity<Map<String, String>> list() throws RestException {
+        return new ResponseEntity<>(grokService.getCommonGrokPatterns(), HttpStatus.OK);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/KafkaController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/KafkaController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/KafkaController.java
new file mode 100644
index 0000000..876558b
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/KafkaController.java
@@ -0,0 +1,96 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.KafkaTopic;
+import org.apache.metron.rest.service.KafkaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Set;
+
+@RestController
+@RequestMapping("/api/v1/kafka")
+public class KafkaController {
+
+    @Autowired
+    private KafkaService kafkaService;
+
+    @ApiOperation(value = "Creates a new Kafka topic")
+    @ApiResponse(message = "Returns saved Kafka topic", code = 200)
+    @RequestMapping(value = "/topic", method = RequestMethod.POST)
+    ResponseEntity<KafkaTopic> save(@ApiParam(name="topic", value="Kafka topic", required=true)@RequestBody KafkaTopic topic) throws RestException {
+        return new ResponseEntity<>(kafkaService.createTopic(topic), HttpStatus.CREATED);
+    }
+
+    @ApiOperation(value = "Retrieves a Kafka topic")
+    @ApiResponses(value = { @ApiResponse(message = "Returns Kafka topic", code = 200),
+            @ApiResponse(message = "Kafka topic is missing", code = 404) })
+    @RequestMapping(value = "/topic/{name}", method = RequestMethod.GET)
+    ResponseEntity<KafkaTopic> get(@ApiParam(name="name", value="Kafka topic name", required=true)@PathVariable String name) throws RestException {
+        KafkaTopic kafkaTopic = kafkaService.getTopic(name);
+        if (kafkaTopic != null) {
+            return new ResponseEntity<>(kafkaTopic, HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+
+    @ApiOperation(value = "Retrieves all Kafka topics")
+    @ApiResponse(message = "Returns a list of all Kafka topics", code = 200)
+    @RequestMapping(value = "/topic", method = RequestMethod.GET)
+    ResponseEntity<Set<String>> list() throws Exception {
+        return new ResponseEntity<>(kafkaService.listTopics(), HttpStatus.OK);
+    }
+
+    @ApiOperation(value = "Delets a Kafka topic")
+    @ApiResponses(value = { @ApiResponse(message = "Kafka topic was deleted", code = 200),
+            @ApiResponse(message = "Kafka topic is missing", code = 404) })
+    @RequestMapping(value = "/topic/{name}", method = RequestMethod.DELETE)
+    ResponseEntity<Void> delete(@ApiParam(name="name", value="Kafka topic name", required=true)@PathVariable String name) throws RestException {
+        if (kafkaService.deleteTopic(name)) {
+            return new ResponseEntity<>(HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+
+    @ApiOperation(value = "Retrieves a sample message from a Kafka topic using the most recent offset")
+    @ApiResponses(value = { @ApiResponse(message = "Returns sample message", code = 200),
+            @ApiResponse(message = "Either Kafka topic is missing or contains no messages", code = 404) })
+    @RequestMapping(value = "/topic/{name}/sample", method = RequestMethod.GET)
+    ResponseEntity<String> getSample(@ApiParam(name="name", value="Kafka topic name", required=true)@PathVariable String name) throws RestException {
+        String sampleMessage = kafkaService.getSampleMessage(name);
+        if (sampleMessage != null) {
+            return new ResponseEntity<>(sampleMessage, HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/RestExceptionHandler.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/RestExceptionHandler.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/RestExceptionHandler.java
new file mode 100644
index 0000000..fa73dab
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/RestExceptionHandler.java
@@ -0,0 +1,48 @@
+/**
+ * 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.metron.rest.controller;
+
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.RestError;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+import javax.servlet.http.HttpServletRequest;
+
+@ControllerAdvice(basePackages = "org.apache.metron.rest.controller")
+public class RestExceptionHandler extends ResponseEntityExceptionHandler {
+
+  @ExceptionHandler(RestException.class)
+  @ResponseBody
+  ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
+    HttpStatus status = getStatus(request);
+    return new ResponseEntity<>(new RestError(status.value(), ex.getMessage(), ex.getMessage()), status);
+  }
+
+  private HttpStatus getStatus(HttpServletRequest request) {
+    Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+    if (statusCode == null) {
+      return HttpStatus.INTERNAL_SERVER_ERROR;
+    }
+    return HttpStatus.valueOf(statusCode);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorEnrichmentConfigController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorEnrichmentConfigController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorEnrichmentConfigController.java
new file mode 100644
index 0000000..546384a
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorEnrichmentConfigController.java
@@ -0,0 +1,97 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.service.SensorEnrichmentConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/sensor/enrichment/config")
+public class SensorEnrichmentConfigController {
+
+  @Autowired
+  private SensorEnrichmentConfigService sensorEnrichmentConfigService;
+
+  @ApiOperation(value = "Updates or creates a SensorEnrichmentConfig in Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "SensorEnrichmentConfig updated. Returns saved SensorEnrichmentConfig", code = 200),
+          @ApiResponse(message = "SensorEnrichmentConfig created. Returns saved SensorEnrichmentConfig", code = 201) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.POST)
+  ResponseEntity<SensorEnrichmentConfig> save(@ApiParam(name="name", value="SensorEnrichmentConfig name", required=true)@PathVariable String name,
+                                              @ApiParam(name="sensorEnrichmentConfig", value="SensorEnrichmentConfig", required=true)@RequestBody SensorEnrichmentConfig sensorEnrichmentConfig) throws RestException {
+    if (sensorEnrichmentConfigService.findOne(name) == null) {
+      return new ResponseEntity<>(sensorEnrichmentConfigService.save(name, sensorEnrichmentConfig), HttpStatus.CREATED);
+    } else {
+      return new ResponseEntity<>(sensorEnrichmentConfigService.save(name, sensorEnrichmentConfig), HttpStatus.OK);
+    }
+  }
+
+  @ApiOperation(value = "Retrieves a SensorEnrichmentConfig from Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "Returns SensorEnrichmentConfig", code = 200),
+          @ApiResponse(message = "SensorEnrichmentConfig is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.GET)
+  ResponseEntity<SensorEnrichmentConfig> findOne(@ApiParam(name="name", value="SensorEnrichmentConfig name", required=true)@PathVariable String name) throws RestException {
+    SensorEnrichmentConfig sensorEnrichmentConfig = sensorEnrichmentConfigService.findOne(name);
+    if (sensorEnrichmentConfig != null) {
+      return new ResponseEntity<>(sensorEnrichmentConfig, HttpStatus.OK);
+    }
+
+    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+  }
+
+  @ApiOperation(value = "Retrieves all SensorEnrichmentConfigs from Zookeeper")
+  @ApiResponse(message = "Returns all SensorEnrichmentConfigs", code = 200)
+  @RequestMapping(method = RequestMethod.GET)
+  ResponseEntity<Map<String, SensorEnrichmentConfig>> getAll() throws Exception {
+    return new ResponseEntity<>(sensorEnrichmentConfigService.getAll(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Deletes a SensorEnrichmentConfig from Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "SensorEnrichmentConfig was deleted", code = 200),
+          @ApiResponse(message = "SensorEnrichmentConfig is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.DELETE)
+  ResponseEntity<Void> delete(@ApiParam(name="name", value="SensorEnrichmentConfig name", required=true)@PathVariable String name) throws RestException {
+    if (sensorEnrichmentConfigService.delete(name)) {
+      return new ResponseEntity<>(HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+
+  @ApiOperation(value = "Lists the available enrichments")
+  @ApiResponse(message = "Returns a list of available enrichments", code = 200)
+  @RequestMapping(value = "/list/available", method = RequestMethod.GET)
+  ResponseEntity<List<String>> getAvailable() throws RestException {
+    return new ResponseEntity<>(sensorEnrichmentConfigService.getAvailableEnrichments(), HttpStatus.OK);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorIndexingConfigController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorIndexingConfigController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorIndexingConfigController.java
new file mode 100644
index 0000000..ffd80fb
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorIndexingConfigController.java
@@ -0,0 +1,88 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.service.SensorIndexingConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/sensor/indexing/config")
+public class SensorIndexingConfigController {
+
+  @Autowired
+  private SensorIndexingConfigService sensorIndexingConfigService;
+
+  @ApiOperation(value = "Updates or creates a SensorIndexingConfig in Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "SensorIndexingConfig updated. Returns saved SensorIndexingConfig", code = 200),
+          @ApiResponse(message = "SensorIndexingConfig created. Returns saved SensorIndexingConfig", code = 201) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.POST)
+  ResponseEntity<Map<String, Object>> save(@ApiParam(name="name", value="SensorIndexingConfig name", required=true)@PathVariable String name,
+                                           @ApiParam(name="sensorIndexingConfig", value="SensorIndexingConfig", required=true)@RequestBody Map<String, Object> sensorIndexingConfig) throws RestException {
+    if (sensorIndexingConfigService.findOne(name) == null) {
+      return new ResponseEntity<>(sensorIndexingConfigService.save(name, sensorIndexingConfig), HttpStatus.CREATED);
+    } else {
+      return new ResponseEntity<>(sensorIndexingConfigService.save(name, sensorIndexingConfig), HttpStatus.OK);
+    }
+  }
+
+  @ApiOperation(value = "Retrieves a SensorIndexingConfig from Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "Returns SensorIndexingConfig", code = 200),
+          @ApiResponse(message = "SensorIndexingConfig is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.GET)
+  ResponseEntity<Map<String, Object>> findOne(@ApiParam(name="name", value="SensorIndexingConfig name", required=true)@PathVariable String name) throws RestException {
+    Map<String, Object> sensorIndexingConfig = sensorIndexingConfigService.findOne(name);
+    if (sensorIndexingConfig != null) {
+      return new ResponseEntity<>(sensorIndexingConfig, HttpStatus.OK);
+    }
+
+    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+  }
+
+  @ApiOperation(value = "Retrieves all SensorIndexingConfigs from Zookeeper")
+  @ApiResponse(message = "Returns all SensorIndexingConfigs", code = 200)
+  @RequestMapping(method = RequestMethod.GET)
+  ResponseEntity<Map<String, Map<String, Object>>> getAll() throws Exception {
+    return new ResponseEntity<>(sensorIndexingConfigService.getAll(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Deletes a SensorIndexingConfig from Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "SensorIndexingConfig was deleted", code = 200),
+          @ApiResponse(message = "SensorIndexingConfig is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.DELETE)
+  ResponseEntity<Void> delete(@ApiParam(name="name", value="SensorIndexingConfig name", required=true)@PathVariable String name) throws RestException {
+    if (sensorIndexingConfigService.delete(name)) {
+      return new ResponseEntity<>(HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorParserConfigController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorParserConfigController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorParserConfigController.java
new file mode 100644
index 0000000..189e2af
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/SensorParserConfigController.java
@@ -0,0 +1,112 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.ParseMessageRequest;
+import org.apache.metron.rest.service.SensorParserConfigService;
+import org.json.simple.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/sensor/parser/config")
+public class SensorParserConfigController {
+
+  @Autowired
+  private SensorParserConfigService sensorParserConfigService;
+
+  @ApiOperation(value = "Updates or creates a SensorParserConfig in Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "SensorParserConfig updated. Returns saved SensorParserConfig", code = 200),
+          @ApiResponse(message = "SensorParserConfig created. Returns saved SensorParserConfig", code = 201) })
+  @RequestMapping(method = RequestMethod.POST)
+  ResponseEntity<SensorParserConfig> save(@ApiParam(name="sensorParserConfig", value="SensorParserConfig", required=true)@RequestBody SensorParserConfig sensorParserConfig) throws RestException {
+    String name = sensorParserConfig.getSensorTopic();
+    if (sensorParserConfigService.findOne(name) == null) {
+      return new ResponseEntity<>(sensorParserConfigService.save(sensorParserConfig), HttpStatus.CREATED);
+    } else {
+      return new ResponseEntity<>(sensorParserConfigService.save(sensorParserConfig), HttpStatus.OK);
+    }
+  }
+
+  @ApiOperation(value = "Retrieves a SensorParserConfig from Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "Returns SensorParserConfig", code = 200),
+          @ApiResponse(message = "SensorParserConfig is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.GET)
+  ResponseEntity<SensorParserConfig> findOne(@ApiParam(name="name", value="SensorParserConfig name", required=true)@PathVariable String name) throws RestException {
+    SensorParserConfig sensorParserConfig = sensorParserConfigService.findOne(name);
+    if (sensorParserConfig != null) {
+      return new ResponseEntity<>(sensorParserConfig, HttpStatus.OK);
+    }
+
+    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+  }
+
+  @ApiOperation(value = "Retrieves all SensorParserConfigs from Zookeeper")
+  @ApiResponse(message = "Returns all SensorParserConfigs", code = 200)
+  @RequestMapping(method = RequestMethod.GET)
+  ResponseEntity<Iterable<SensorParserConfig>> findAll() throws RestException {
+    return new ResponseEntity<>(sensorParserConfigService.getAll(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Deletes a SensorParserConfig from Zookeeper")
+  @ApiResponses(value = { @ApiResponse(message = "SensorParserConfig was deleted", code = 200),
+          @ApiResponse(message = "SensorParserConfig is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.DELETE)
+  ResponseEntity<Void> delete(@ApiParam(name="name", value="SensorParserConfig name", required=true)@PathVariable String name) throws RestException {
+    if (sensorParserConfigService.delete(name)) {
+      return new ResponseEntity<>(HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+
+  @ApiOperation(value = "Lists the available parser classes that can be found on the classpath")
+  @ApiResponse(message = "Returns a list of available parser classes", code = 200)
+  @RequestMapping(value = "/list/available", method = RequestMethod.GET)
+  ResponseEntity<Map<String, String>> getAvailable() throws RestException {
+    return new ResponseEntity<>(sensorParserConfigService.getAvailableParsers(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Scans the classpath for available parser classes and reloads the cached parser class list")
+  @ApiResponse(message = "Returns a list of available parser classes", code = 200)
+  @RequestMapping(value = "/reload/available", method = RequestMethod.GET)
+  ResponseEntity<Map<String, String>> reloadAvailable() throws RestException {
+    return new ResponseEntity<>(sensorParserConfigService.reloadAvailableParsers(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Parses a sample message given a SensorParserConfig")
+  @ApiResponse(message = "Returns parsed message", code = 200)
+  @RequestMapping(value = "/parseMessage", method = RequestMethod.POST)
+  ResponseEntity<JSONObject> parseMessage(@ApiParam(name="parseMessageRequest", value="Object containing a sample message and SensorParserConfig", required=true) @RequestBody ParseMessageRequest parseMessageRequest) throws RestException {
+    return new ResponseEntity<>(sensorParserConfigService.parseMessage(parseMessageRequest), HttpStatus.OK);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/StormController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/StormController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/StormController.java
new file mode 100644
index 0000000..915e304
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/StormController.java
@@ -0,0 +1,190 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.apache.metron.rest.MetronRestConstants;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.TopologyResponse;
+import org.apache.metron.rest.model.TopologyStatus;
+import org.apache.metron.rest.service.StormAdminService;
+import org.apache.metron.rest.service.StormStatusService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/storm")
+public class StormController {
+
+  @Autowired
+  private StormStatusService stormStatusService;
+
+  @Autowired
+  private StormAdminService stormAdminService;
+
+  @ApiOperation(value = "Retrieves the status of all Storm topologies")
+  @ApiResponse(message = "Returns a list of topologies with status information", code = 200)
+  @RequestMapping(method = RequestMethod.GET)
+  ResponseEntity<List<TopologyStatus>> getAll() throws RestException {
+    return new ResponseEntity<>(stormStatusService.getAllTopologyStatus(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Retrieves the status of a Storm topology")
+  @ApiResponses(value = { @ApiResponse(message = "Returns topology status information", code = 200),
+          @ApiResponse(message = "Topology is missing", code = 404) })
+  @RequestMapping(value = "/{name}", method = RequestMethod.GET)
+  ResponseEntity<TopologyStatus> get(@ApiParam(name="name", value="Topology name", required=true)@PathVariable String name) throws RestException {
+    TopologyStatus topologyStatus = stormStatusService.getTopologyStatus(name);
+    if (topologyStatus != null) {
+      return new ResponseEntity<>(topologyStatus, HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+
+  @ApiOperation(value = "Starts a Storm parser topology")
+  @ApiResponse(message = "Returns start response message", code = 200)
+  @RequestMapping(value = "/parser/start/{name}", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> start(@ApiParam(name="name", value="Parser name", required=true)@PathVariable String name) throws RestException {
+    return new ResponseEntity<>(stormAdminService.startParserTopology(name), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Stops a Storm parser topology")
+  @ApiResponse(message = "Returns stop response message", code = 200)
+  @RequestMapping(value = "/parser/stop/{name}", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> stop(@ApiParam(name="name", value="Parser name", required=true)@PathVariable String name,
+                                        @ApiParam(name="stopNow", value="Stop the topology immediately")@RequestParam(required = false, defaultValue = "false") boolean stopNow) throws RestException {
+    return new ResponseEntity<>(stormAdminService.stopParserTopology(name, stopNow), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Activates a Storm parser topology")
+  @ApiResponse(message = "Returns activate response message", code = 200)
+  @RequestMapping(value = "/parser/activate/{name}", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> activate(@ApiParam(name="name", value="Parser name", required=true)@PathVariable String name) throws RestException {
+    return new ResponseEntity<>(stormStatusService.activateTopology(name), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Deactivates a Storm parser topology")
+  @ApiResponse(message = "Returns deactivate response message", code = 200)
+  @RequestMapping(value = "/parser/deactivate/{name}", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> deactivate(@ApiParam(name="name", value="Parser name", required=true)@PathVariable String name) throws RestException {
+    return new ResponseEntity<>(stormStatusService.deactivateTopology(name), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Retrieves the status of the Storm enrichment topology")
+  @ApiResponses(value = { @ApiResponse(message = "Returns topology status information", code = 200),
+          @ApiResponse(message = "Topology is missing", code = 404) })
+  @RequestMapping(value = "/enrichment", method = RequestMethod.GET)
+  ResponseEntity<TopologyStatus> getEnrichment() throws RestException {
+    TopologyStatus sensorParserStatus = stormStatusService.getTopologyStatus(MetronRestConstants.ENRICHMENT_TOPOLOGY_NAME);
+    if (sensorParserStatus != null) {
+      return new ResponseEntity<>(sensorParserStatus, HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+
+  @ApiOperation(value = "Starts a Storm enrichment topology")
+  @ApiResponse(message = "Returns start response message", code = 200)
+  @RequestMapping(value = "/enrichment/start", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> startEnrichment() throws RestException {
+    return new ResponseEntity<>(stormAdminService.startEnrichmentTopology(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Stops a Storm enrichment topology")
+  @ApiResponse(message = "Returns stop response message", code = 200)
+  @RequestMapping(value = "/enrichment/stop", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> stopEnrichment(@ApiParam(name="stopNow", value="Stop the topology immediately")@RequestParam(required = false, defaultValue = "false") boolean stopNow) throws RestException {
+    return new ResponseEntity<>(stormAdminService.stopEnrichmentTopology(stopNow), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Activates a Storm enrichment topology")
+  @ApiResponse(message = "Returns activate response message", code = 200)
+  @RequestMapping(value = "/enrichment/activate", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> activateEnrichment() throws RestException {
+    return new ResponseEntity<>(stormStatusService.activateTopology(MetronRestConstants.ENRICHMENT_TOPOLOGY_NAME), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Deactivates a Storm enrichment topology")
+  @ApiResponse(message = "Returns deactivate response message", code = 200)
+  @RequestMapping(value = "/enrichment/deactivate", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> deactivateEnrichment() throws RestException {
+    return new ResponseEntity<>(stormStatusService.deactivateTopology(MetronRestConstants.ENRICHMENT_TOPOLOGY_NAME), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Retrieves the status of the Storm indexing topology")
+  @ApiResponses(value = { @ApiResponse(message = "Returns topology status information", code = 200),
+          @ApiResponse(message = "Topology is missing", code = 404) })
+  @RequestMapping(value = "/indexing", method = RequestMethod.GET)
+  ResponseEntity<TopologyStatus> getIndexing() throws RestException {
+    TopologyStatus topologyStatus = stormStatusService.getTopologyStatus(MetronRestConstants.INDEXING_TOPOLOGY_NAME);
+    if (topologyStatus != null) {
+      return new ResponseEntity<>(topologyStatus, HttpStatus.OK);
+    } else {
+      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+  }
+
+  @ApiOperation(value = "Starts a Storm indexing topology")
+  @ApiResponse(message = "Returns start response message", code = 200)
+  @RequestMapping(value = "/indexing/start", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> startIndexing() throws RestException {
+    return new ResponseEntity<>(stormAdminService.startIndexingTopology(), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Stops a Storm enrichment topology")
+  @ApiResponse(message = "Returns stop response message", code = 200)
+  @RequestMapping(value = "/indexing/stop", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> stopIndexing(@ApiParam(name="stopNow", value="Stop the topology immediately")@RequestParam(required = false, defaultValue = "false") boolean stopNow) throws RestException {
+    return new ResponseEntity<>(stormAdminService.stopIndexingTopology(stopNow), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Activates a Storm indexing topology")
+  @ApiResponse(message = "Returns activate response message", code = 200)
+  @RequestMapping(value = "/indexing/activate", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> activateIndexing() throws RestException {
+    return new ResponseEntity<>(stormStatusService.activateTopology(MetronRestConstants.INDEXING_TOPOLOGY_NAME), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Deactivates a Storm indexing topology")
+  @ApiResponse(message = "Returns deactivate response message", code = 200)
+  @RequestMapping(value = "/indexing/deactivate", method = RequestMethod.GET)
+  ResponseEntity<TopologyResponse> deactivateIndexing() throws RestException {
+    return new ResponseEntity<>(stormStatusService.deactivateTopology(MetronRestConstants.INDEXING_TOPOLOGY_NAME), HttpStatus.OK);
+  }
+
+  @ApiOperation(value = "Retrieves information about the Storm command line client")
+  @ApiResponse(message = "Returns storm command line client information", code = 200)
+  @RequestMapping(value = "/client/status", method = RequestMethod.GET)
+  ResponseEntity<Map<String, String>> clientStatus() throws RestException {
+    return new ResponseEntity<>(stormAdminService.getStormClientStatus(), HttpStatus.OK);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/TransformationController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/TransformationController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/TransformationController.java
new file mode 100644
index 0000000..bc8b201
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/TransformationController.java
@@ -0,0 +1,80 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import org.apache.metron.common.field.transformation.FieldTransformations;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.StellarFunctionDescription;
+import org.apache.metron.rest.model.TransformationValidation;
+import org.apache.metron.rest.service.TransformationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/transformation")
+public class TransformationController {
+
+    @Autowired
+    private TransformationService transformationService;
+
+  @ApiOperation(value = "Tests Stellar statements to ensure they are well-formed")
+  @ApiResponse(message = "Returns validation results", code = 200)
+    @RequestMapping(value = "/validate/rules", method = RequestMethod.POST)
+    ResponseEntity<Map<String, Boolean>> validateRule(@ApiParam(name="statements", value="List of statements to validate", required=true)@RequestBody List<String> statements) throws RestException {
+        return new ResponseEntity<>(transformationService.validateRules(statements), HttpStatus.OK);
+    }
+
+  @ApiOperation(value = "Executes transformations against a sample message")
+  @ApiResponse(message = "Returns transformation results", code = 200)
+    @RequestMapping(value = "/validate", method = RequestMethod.POST)
+    ResponseEntity<Map<String, Object>> validateTransformation(@ApiParam(name="transformationValidation", value="Object containing SensorParserConfig and sample message", required=true)@RequestBody TransformationValidation transformationValidation) throws RestException {
+        return new ResponseEntity<>(transformationService.validateTransformation(transformationValidation), HttpStatus.OK);
+    }
+
+  @ApiOperation(value = "Retrieves field transformations")
+  @ApiResponse(message = "Returns a list field transformations", code = 200)
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    ResponseEntity<FieldTransformations[]> list() throws RestException {
+        return new ResponseEntity<>(transformationService.getTransformations(), HttpStatus.OK);
+    }
+
+  @ApiOperation(value = "Lists the Stellar functions that can be found on the classpath")
+  @ApiResponse(message = "Returns a list of Stellar functions", code = 200)
+    @RequestMapping(value = "/list/functions", method = RequestMethod.GET)
+    ResponseEntity<List<StellarFunctionDescription>> listFunctions() throws RestException {
+        return new ResponseEntity<>(transformationService.getStellarFunctions(), HttpStatus.OK);
+    }
+
+  @ApiOperation(value = "Lists the simple Stellar functions (functions with only 1 input) that can be found on the classpath")
+  @ApiResponse(message = "Returns a list of simple Stellar functions", code = 200)
+    @RequestMapping(value = "/list/simple/functions", method = RequestMethod.GET)
+    ResponseEntity<List<StellarFunctionDescription>> listSimpleFunctions() throws RestException {
+        return new ResponseEntity<>(transformationService.getSimpleStellarFunctions(), HttpStatus.OK);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UserController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UserController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UserController.java
new file mode 100644
index 0000000..594b51f
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UserController.java
@@ -0,0 +1,36 @@
+/**
+ * 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.metron.rest.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.security.Principal;
+
+@RestController
+public class UserController {
+
+  @ApiOperation(value = "Retrieves the current user")
+  @ApiResponse(message = "Current user", code = 200)
+    @RequestMapping("/api/v1/user")
+    public String user(Principal user) {
+        return user.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GlobalConfigService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GlobalConfigService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GlobalConfigService.java
new file mode 100644
index 0000000..7b8cf45
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GlobalConfigService.java
@@ -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.
+ */
+package org.apache.metron.rest.service;
+
+import org.apache.metron.rest.RestException;
+
+import java.util.Map;
+
+public interface GlobalConfigService {
+
+    Map<String, Object> save(Map<String, Object> globalConfig) throws RestException;
+
+    Map<String, Object> get() throws RestException;
+
+    boolean delete() throws RestException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GrokService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GrokService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GrokService.java
new file mode 100644
index 0000000..95ce1ba
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/GrokService.java
@@ -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.
+ */
+package org.apache.metron.rest.service;
+
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.GrokValidation;
+
+import java.util.Map;
+
+public interface GrokService {
+
+    Map<String, String> getCommonGrokPatterns();
+
+    GrokValidation validateGrokStatement(GrokValidation grokValidation) throws RestException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/HdfsService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/HdfsService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/HdfsService.java
new file mode 100644
index 0000000..97888ff
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/HdfsService.java
@@ -0,0 +1,35 @@
+/**
+ * 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.metron.rest.service;
+
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.Path;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+
+public interface HdfsService {
+
+    byte[] read(Path path) throws IOException;
+
+    void write(Path path, byte[] contents) throws IOException;
+
+    FileStatus[] list(Path path) throws IOException;
+
+    boolean delete(Path path, boolean recursive) throws IOException;
+ }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/KafkaService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/KafkaService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/KafkaService.java
new file mode 100644
index 0000000..e89b165
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/KafkaService.java
@@ -0,0 +1,37 @@
+/**
+ * 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.metron.rest.service;
+
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.KafkaTopic;
+
+import java.util.Set;
+
+public interface KafkaService {
+
+    KafkaTopic createTopic(KafkaTopic topic) throws RestException;
+
+    boolean deleteTopic(String name);
+
+    KafkaTopic getTopic(String name);
+
+    Set<String> listTopics();
+
+    String getSampleMessage(String topic);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorEnrichmentConfigService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorEnrichmentConfigService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorEnrichmentConfigService.java
new file mode 100644
index 0000000..fe84802
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorEnrichmentConfigService.java
@@ -0,0 +1,40 @@
+/**
+ * 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.metron.rest.service;
+
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.rest.RestException;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SensorEnrichmentConfigService {
+
+    SensorEnrichmentConfig save(String name, SensorEnrichmentConfig sensorEnrichmentConfig) throws RestException;
+
+    SensorEnrichmentConfig findOne(String name) throws RestException;
+
+    Map<String, SensorEnrichmentConfig> getAll() throws RestException;
+
+    List<String> getAllTypes() throws RestException;
+
+    boolean delete(String name) throws RestException;
+
+    List<String> getAvailableEnrichments();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/55b3e7ea/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorIndexingConfigService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorIndexingConfigService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorIndexingConfigService.java
new file mode 100644
index 0000000..0699431
--- /dev/null
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/SensorIndexingConfigService.java
@@ -0,0 +1,37 @@
+/**
+ * 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.metron.rest.service;
+
+import org.apache.metron.rest.RestException;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SensorIndexingConfigService {
+
+  Map<String, Object> save(String name, Map<String, Object> sensorIndexingConfig) throws RestException;
+
+  Map<String, Object> findOne(String name) throws RestException;
+
+  Map<String, Map<String, Object>> getAll() throws RestException;
+
+  List<String> getAllTypes() throws RestException;
+
+  boolean delete(String name) throws RestException;
+
+}