You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eagle.apache.org by ha...@apache.org on 2016/07/21 12:28:54 UTC
[10/11] incubator-eagle git commit: [EAGLE-382][EAGLE-385] Monitoring
Application Framework Core
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderLoader.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderLoader.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderLoader.java
new file mode 100644
index 0000000..8b98404
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderLoader.java
@@ -0,0 +1,74 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.service;
+
+import com.typesafe.config.Config;
+import org.apache.eagle.app.service.loader.ApplicationProviderConfigLoader;
+import org.apache.eagle.app.service.loader.ApplicationProviderSPILoader;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class ApplicationProviderLoader {
+ private final Config config;
+ private final Map<String,ApplicationProvider> providers;
+ private final static Logger LOG = LoggerFactory.getLogger(ApplicationProviderLoader.class);
+
+ public ApplicationProviderLoader(Config config) {
+ this.config = config;
+ this.providers = new HashMap<>();
+ }
+
+ public abstract void load();
+
+ protected Config getConfig() {
+ return config;
+ }
+
+ protected void registerProvider(ApplicationProvider provider){
+ if(providers.containsKey(provider.getApplicationDesc().getType())){
+ throw new RuntimeException("Duplicated APPLICATION_TYPE: "+provider.getApplicationDesc().getType()+", was already registered by provider: "+providers.get(provider.getApplicationDesc().getType()));
+ }
+ providers.put(provider.getApplicationDesc().getType(),provider);
+ LOG.info("Initialized application provider: {}",provider);
+ }
+
+ public Collection<ApplicationProvider> getProviders(){
+ return providers.values();
+ }
+
+ public ApplicationProvider<?> getApplicationProviderByType(String type) {
+ return providers.get(type);
+ }
+
+ public void reset(){
+ providers.clear();
+ }
+
+ public static String getDefaultAppProviderLoader(){
+ if(ApplicationProviderConfigLoader
+ .appProviderConfExists(ApplicationProviderConfigLoader.DEFAULT_APPLICATIONS_CONFIG_FILE)){
+ return ApplicationProviderConfigLoader.class.getCanonicalName();
+ } else {
+ return ApplicationProviderSPILoader.class.getCanonicalName();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderService.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderService.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderService.java
new file mode 100644
index 0000000..52dfb5c
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderService.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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.service;
+
+import org.apache.eagle.app.Application;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.metadata.service.ApplicationDescService;
+
+import java.util.Collection;
+
+public interface ApplicationProviderService extends ApplicationDescService {
+ void reload();
+ Collection<ApplicationProvider> getProviders();
+ <T extends Application> ApplicationProvider<T> getApplicationProviderByType(String type);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderServiceImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderServiceImpl.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderServiceImpl.java
new file mode 100644
index 0000000..6ce463f
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/ApplicationProviderServiceImpl.java
@@ -0,0 +1,87 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.service;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.typesafe.config.Config;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.apache.eagle.metadata.model.ApplicationDesc;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Support to load application provider from application.provider.config = "providers.xml" configuration file
+ * or application.provider.dir = "lib/apps" with SPI Class loader
+ *
+ * TODO: hot-manage application provider loading
+ */
+@Singleton
+public class ApplicationProviderServiceImpl implements ApplicationProviderService {
+ private final Config config;
+ private final static Logger LOG = LoggerFactory.getLogger(ApplicationProviderServiceImpl.class);
+ private final ApplicationProviderLoader appProviderLoader;
+ public final static String APP_PROVIDER_LOADER_CLASS_KEY = "application.provider.loader";
+
+ @Inject
+ public ApplicationProviderServiceImpl(Config config){
+ LOG.info("Initializing {}",this.getClass().getCanonicalName());
+ this.config = config;
+ String appProviderLoaderClass = this.config.hasPath(APP_PROVIDER_LOADER_CLASS_KEY)?
+ this.config.getString(APP_PROVIDER_LOADER_CLASS_KEY):ApplicationProviderLoader.getDefaultAppProviderLoader();
+ LOG.info("Initializing {} = {}",APP_PROVIDER_LOADER_CLASS_KEY,appProviderLoaderClass);
+ appProviderLoader = initializeAppProviderLoader(appProviderLoaderClass);
+ LOG.info("Initialized {}",appProviderLoader);
+ reload();
+ }
+
+ private ApplicationProviderLoader initializeAppProviderLoader(String appProviderLoaderClass){
+ try {
+ return (ApplicationProviderLoader) Class.forName(appProviderLoaderClass).getConstructor(Config.class).newInstance(this.config);
+ } catch (Throwable e) {
+ LOG.error("Failed to initialize ApplicationProviderLoader: "+appProviderLoaderClass,e);
+ throw new IllegalStateException("Failed to initialize ApplicationProviderLoader: "+appProviderLoaderClass,e);
+ }
+ }
+
+ public synchronized void reload(){
+ appProviderLoader.reset();
+ LOG.info("Loading application providers ...");
+ appProviderLoader.load();
+ LOG.info("Loaded {} application providers",appProviderLoader.getProviders().size());
+ }
+
+ public Collection<ApplicationProvider> getProviders(){
+ return appProviderLoader.getProviders();
+ }
+
+ public Collection<ApplicationDesc> getApplicationDescs(){
+ return getProviders().stream().map(ApplicationProvider::getApplicationDesc).collect(Collectors.toList());
+ }
+
+ public ApplicationProvider<?> getApplicationProviderByType(String type) {
+ return appProviderLoader.getApplicationProviderByType(type);
+ }
+
+ @Deprecated
+ public ApplicationDesc getApplicationDescByType(String appType) {
+ return appProviderLoader.getApplicationProviderByType(appType).getApplicationDesc();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderConfigLoader.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderConfigLoader.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderConfigLoader.java
new file mode 100644
index 0000000..11d9905
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderConfigLoader.java
@@ -0,0 +1,128 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.service.loader;
+
+import com.google.common.base.Preconditions;
+import com.typesafe.config.Config;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.app.config.ApplicationProvidersConfig;
+import org.apache.eagle.app.service.ApplicationProviderLoader;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+public class ApplicationProviderConfigLoader extends ApplicationProviderLoader {
+ public final static String DEFAULT_APPLICATIONS_CONFIG_FILE = "providers.xml";
+ private final static String APPLICATIONS_CONFIG_PROPS_KEY = "application.provider.config";
+ private final static Logger LOG = LoggerFactory.getLogger(ApplicationProviderConfigLoader.class);
+ public ApplicationProviderConfigLoader(Config config) {
+ super(config);
+ }
+
+ @Override
+ public void load() {
+ List<ApplicationProviderConfig> applicationProviderConfigs = loadProvidersFromProvidersConf();
+ int totalCount = applicationProviderConfigs.size();
+ int loadedCount = 0,failedCount = 0;
+ for(ApplicationProviderConfig providerConfig: applicationProviderConfigs){
+ try {
+ initializeProvider(providerConfig);
+ loadedCount ++;
+ }catch (Throwable ex){
+ LOG.warn("Failed to initialized {}, ignored",providerConfig,ex);
+ failedCount ++;
+ }
+ }
+ LOG.info("Loaded {} app providers (total: {}, failed: {})",loadedCount,totalCount,failedCount);
+ }
+
+ public static boolean appProviderConfExists(String applicationConfFile){
+ InputStream is = ApplicationProviderConfigLoader.class.getResourceAsStream(applicationConfFile);
+ if(is == null){
+ is = ApplicationProviderConfigLoader.class.getResourceAsStream("/"+applicationConfFile);
+ }
+
+ if(is != null){
+ try {
+ return true;
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOG.debug(e.getMessage());
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private void initializeProvider(ApplicationProviderConfig providerConfig) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+ LOG.info("Loading application provider {} from {}",providerConfig.getClassName(),providerConfig.getJarPath());
+ String providerClassName = providerConfig.getClassName();
+ if(providerClassName == null) throw new RuntimeException("provider.classname is null: "+providerConfig);
+ if(providerConfig.getJarPath() == null) throw new RuntimeException("provider.jarpath is null: "+providerConfig);
+
+ Class<?> providerClass = Class.forName(providerClassName);
+
+ if(!ApplicationProvider.class.isAssignableFrom(providerClass)){
+ throw new RuntimeException("providerClassName is not implementation of "+ApplicationProvider.class.getCanonicalName());
+ }
+ ApplicationProvider provider = (ApplicationProvider) providerClass.newInstance();
+ provider.prepare(providerConfig,this.getConfig());
+ Preconditions.checkNotNull(provider.getApplicationDesc(),"appDesc is null");
+ Preconditions.checkNotNull(provider.getApplicationDesc().getType(),"type is null");
+ registerProvider(provider);
+ }
+
+ private List<ApplicationProviderConfig> loadProvidersFromProvidersConf() {
+ String providerConfigFile = DEFAULT_APPLICATIONS_CONFIG_FILE;
+ if(getConfig().hasPath(APPLICATIONS_CONFIG_PROPS_KEY)){
+ providerConfigFile = getConfig().getString(APPLICATIONS_CONFIG_PROPS_KEY);
+ LOG.info("Set {} = {}",APPLICATIONS_CONFIG_PROPS_KEY,providerConfigFile);
+ }
+ InputStream is = null;
+ try {
+ JAXBContext jc = JAXBContext.newInstance(ApplicationProvidersConfig.class);
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ is = ApplicationProviderConfigLoader.class.getResourceAsStream(providerConfigFile);
+ if(is == null){
+ is = ApplicationProviderConfigLoader.class.getResourceAsStream("/"+providerConfigFile);
+ }
+ if(is == null){
+ LOG.error("Application provider configuration {} is not found",providerConfigFile);
+ }
+ Preconditions.checkNotNull(is,providerConfigFile+" is not found");
+ return ((ApplicationProvidersConfig) unmarshaller.unmarshal(is)).getProviders();
+ }catch (Exception ex){
+ LOG.error("Failed to load application provider configuration: {}",providerConfigFile,ex);
+ throw new RuntimeException("Failed to load application provider configuration: "+providerConfigFile,ex);
+ } finally {
+ if(is != null) try {
+ is.close();
+ } catch (IOException e) {
+ LOG.error(e.getMessage(),e);
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderSPILoader.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderSPILoader.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderSPILoader.java
new file mode 100644
index 0000000..33cb401
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/service/loader/ApplicationProviderSPILoader.java
@@ -0,0 +1,89 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.service.loader;
+
+import com.typesafe.config.Config;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.app.service.ApplicationProviderLoader;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.apache.eagle.app.tools.DynamicJarPathFinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ServiceLoader;
+import java.util.function.Function;
+
+public class ApplicationProviderSPILoader extends ApplicationProviderLoader{
+ private final String appProviderExtDir;
+ private final static Logger LOG = LoggerFactory.getLogger(ApplicationProviderSPILoader.class);
+ private final static String APPLICATIONS_DIR_PROPS_KEY = "application.provider.dir";
+
+ public ApplicationProviderSPILoader(Config config) {
+ super(config);
+ if(config.hasPath(APPLICATIONS_DIR_PROPS_KEY)) {
+ this.appProviderExtDir = config.getString(APPLICATIONS_DIR_PROPS_KEY);
+ }else{
+ this.appProviderExtDir = null;
+ }
+
+ LOG.info("Using {}: {}",APPLICATIONS_DIR_PROPS_KEY,this.appProviderExtDir);
+
+ }
+
+ @Override
+ public void load() {
+ if(appProviderExtDir != null) {
+ LOG.info("Loading application providers from class loader of jars in {}", appProviderExtDir);
+ File loc = new File(appProviderExtDir);
+ File[] jarFiles = loc.listFiles(file -> file.getPath().toLowerCase().endsWith(".jar"));
+ if (jarFiles != null) {
+ for (File jarFile : jarFiles) {
+ try {
+ URL jarFileUrl = jarFile.toURI().toURL();
+ LOG.debug("Loading ApplicationProvider from jar: {}", jarFileUrl.toString());
+ URLClassLoader jarFileClassLoader = new URLClassLoader(new URL[]{jarFileUrl});
+ loadProviderFromClassLoader(jarFileClassLoader, (applicationProviderConfig) -> jarFileUrl.getPath());
+ } catch (Exception e) {
+ LOG.warn("Failed to load application provider from jar {}", jarFile,e);
+ }
+ }
+ }
+ } else {
+ LOG.info("Loading application providers from context class loader");
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ loadProviderFromClassLoader(classLoader,(applicationProviderConfig) -> DynamicJarPathFinder.findPath(applicationProviderConfig.getClass()));
+ }
+ }
+
+ private void loadProviderFromClassLoader(ClassLoader jarFileClassLoader, Function<ApplicationProviderConfig,String> jarFileSupplier){
+ ServiceLoader<ApplicationProvider> serviceLoader = ServiceLoader.load(ApplicationProvider.class, jarFileClassLoader);
+ for (ApplicationProvider applicationProvider : serviceLoader) {
+ try {
+ ApplicationProviderConfig providerConfig = new ApplicationProviderConfig();
+ providerConfig.setClassName(applicationProvider.getClass().getCanonicalName());
+ providerConfig.setJarPath(jarFileSupplier.apply(providerConfig));
+ applicationProvider.prepare(providerConfig, getConfig());
+ registerProvider(applicationProvider);
+ }catch (Throwable ex){
+ LOG.warn("Failed to register application provider {}",applicationProvider,ex);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/KafkaStreamSink.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/KafkaStreamSink.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/KafkaStreamSink.java
new file mode 100644
index 0000000..eee2a70
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/KafkaStreamSink.java
@@ -0,0 +1,65 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.sink;
+
+import backtype.storm.task.TopologyContext;
+import org.apache.eagle.alert.engine.coordinator.StreamDefinition;
+import org.apache.eagle.alert.engine.model.StreamEvent;
+import org.apache.eagle.app.ApplicationContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class KafkaStreamSink extends StreamSink {
+ private final static Logger LOGGER = LoggerFactory.getLogger(KafkaStreamSink.class);
+ private final String topicId;
+
+ public KafkaStreamSink(StreamDefinition streamDefinition, ApplicationContext applicationContext) {
+ super(streamDefinition, applicationContext);
+ this.topicId = String.format("EAGLE_%s_%s_%s",
+ applicationContext.getAppEntity().getSite().getSiteId(),
+ applicationContext.getAppEntity().getDescriptor().getType(),
+ streamDefinition.getStreamId());
+ }
+
+ @Override
+ public void prepare(Map stormConf, TopologyContext context) {
+ super.prepare(stormConf, context);
+ ensureTopic();
+ // TODO: Create KafkaProducer
+ }
+
+ private void ensureTopic(){
+ LOGGER.info("TODO: ensure kafka topic {} created",this.topicId);
+ }
+
+ @Override
+ protected void onEvent(StreamEvent streamEvent) {
+ LOGGER.info("TODO: producing {}",streamEvent);
+ }
+
+ @Override
+ public Map<String, Object> getSinkContext() {
+ return new HashMap<String,Object>(){
+ {
+ put("kafka.topic",KafkaStreamSink.this.topicId);
+ }
+ };
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/LoggingStreamSink.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/LoggingStreamSink.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/LoggingStreamSink.java
new file mode 100644
index 0000000..ca201a8
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/LoggingStreamSink.java
@@ -0,0 +1,43 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.sink;
+
+import org.apache.eagle.alert.engine.coordinator.StreamDefinition;
+import org.apache.eagle.alert.engine.model.StreamEvent;
+import org.apache.eagle.app.ApplicationContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LoggingStreamSink extends StreamSink {
+ private final static Logger LOGGER = LoggerFactory.getLogger(KafkaStreamSink.class);
+ public LoggingStreamSink(StreamDefinition streamDefinition, ApplicationContext applicationContext) {
+ super(streamDefinition, applicationContext);
+ }
+
+ @Override
+ protected void onEvent(StreamEvent streamEvent) {
+ LOGGER.info("Receiving {}",streamEvent);
+ }
+
+ @Override
+ public Map<String, Object> getSinkContext() {
+ return new HashMap<>();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/StreamSink.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/StreamSink.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/StreamSink.java
new file mode 100644
index 0000000..e0f2db2
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/sink/StreamSink.java
@@ -0,0 +1,82 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.sink;
+
+import backtype.storm.task.TopologyContext;
+import backtype.storm.topology.BasicOutputCollector;
+import backtype.storm.topology.OutputFieldsDeclarer;
+import backtype.storm.topology.base.BaseBasicBolt;
+import backtype.storm.tuple.Fields;
+import backtype.storm.tuple.Tuple;
+import org.apache.eagle.alert.engine.coordinator.StreamDefinition;
+import org.apache.eagle.alert.engine.model.StreamEvent;
+import org.apache.eagle.app.ApplicationContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+public abstract class StreamSink extends BaseBasicBolt {
+ private final static Logger LOG = LoggerFactory.getLogger(StreamSink.class);
+ public final static String KEY_FIELD = "KEY";
+ public final static String VALUE_FIELD = "VALUE";
+
+ public StreamSink(StreamDefinition streamDefinition,ApplicationContext applicationContext){
+
+ }
+
+ @Override
+ public void prepare(Map stormConf, TopologyContext context) {
+ super.prepare(stormConf, context);
+ }
+
+ @Override
+ public void execute(Tuple input, BasicOutputCollector collector) {
+ List<Object> values = input.getValues();
+ Object inputValue;
+ if(values.size() == 1){
+ inputValue = values.get(0);
+ } else if(values.size() == 2){
+ inputValue = values.get(1);
+ } else{
+ collector.reportError(new IllegalStateException("Expect tuple in size of 1: <StreamEvent> or 2: <Object,StreamEvent>, but got "+values.size()+": "+values));
+ return;
+ }
+
+ if(inputValue instanceof StreamEvent){
+ try {
+ onEvent((StreamEvent) inputValue);
+ }catch (Exception e){
+ LOG.error("Failed to execute event {}",inputValue);
+ collector.reportError(e);
+ }
+ } else {
+ LOG.error("{} is not StreamEvent",inputValue);
+ collector.reportError(new IllegalStateException("Input tuple "+input+"is not type of StreamEvent"));
+ }
+ }
+
+ protected abstract void onEvent(StreamEvent streamEvent);
+
+ public abstract Map<String,Object> getSinkContext();
+
+ @Override
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
+ declarer.declare(new Fields(KEY_FIELD,VALUE_FIELD));
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/AbstractApplicationProvider.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/AbstractApplicationProvider.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/AbstractApplicationProvider.java
new file mode 100644
index 0000000..37311eb
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/AbstractApplicationProvider.java
@@ -0,0 +1,144 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.spi;
+
+import com.typesafe.config.Config;
+import org.apache.eagle.alert.engine.coordinator.StreamDefinition;
+import org.apache.eagle.app.Application;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.app.config.ApplicationProviderDescConfig;
+import org.apache.eagle.app.sink.KafkaStreamSink;
+import org.apache.eagle.app.sink.StreamSink;
+import org.apache.eagle.metadata.model.ApplicationDesc;
+import org.apache.eagle.metadata.model.ApplicationDocs;
+import org.apache.eagle.metadata.model.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBException;
+import java.util.List;
+
+public abstract class AbstractApplicationProvider<T extends Application> implements ApplicationProvider<T> {
+ private final static Logger LOG = LoggerFactory.getLogger(AbstractApplicationProvider.class);
+ private final static String APPLICATIONS_SINK_TYPE_PROPS_KEY = "application.sink.type";
+ private final static String DEFAULT_APPLICATIONS_SINK_TYPE = KafkaStreamSink.class.getCanonicalName();
+ private final ApplicationDesc applicationDesc;
+
+ public AbstractApplicationProvider(){
+ applicationDesc = new ApplicationDesc();
+ applicationDesc.setProviderClass(this.getClass());
+ configure();
+ }
+
+ protected void configure (){
+ // do nothing by default
+ }
+
+ protected AbstractApplicationProvider(String applicationDescConfig) {
+ this();
+ ApplicationProviderDescConfig descWrapperConfig = ApplicationProviderDescConfig.loadFromXML(applicationDescConfig);
+ setType(descWrapperConfig.getType());
+ setVersion(descWrapperConfig.getVersion());
+ setName(descWrapperConfig.getName());
+ setDocs(descWrapperConfig.getDocs());
+ try {
+ if (descWrapperConfig.getAppClass() != null) {
+ setAppClass((Class<T>) Class.forName(descWrapperConfig.getAppClass()));
+ if (!Application.class.isAssignableFrom(applicationDesc.getAppClass())) {
+ throw new IllegalStateException(descWrapperConfig.getAppClass() + " is not sub-class of " + Application.class.getCanonicalName());
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ LOG.error(e.getMessage(), e);
+ throw new RuntimeException(e.getMessage(), e.getCause());
+ }
+ setViewPath(descWrapperConfig.getViewPath());
+ setConfiguration(descWrapperConfig.getConfiguration());
+ setStreams(descWrapperConfig.getStreams());
+ }
+
+ @Override
+ public void prepare(ApplicationProviderConfig providerConfig, Config envConfig) {
+ this.applicationDesc.setJarPath(providerConfig.getJarPath());
+ String sinkClassName = envConfig.hasPath(APPLICATIONS_SINK_TYPE_PROPS_KEY) ?
+ envConfig.getString(APPLICATIONS_SINK_TYPE_PROPS_KEY) : DEFAULT_APPLICATIONS_SINK_TYPE;
+ try {
+ Class<?> sinkClass = Class.forName(sinkClassName);
+ if(!StreamSink.class.isAssignableFrom(sinkClass)){
+ throw new IllegalStateException(sinkClassName+ "is not assignable from "+StreamSink.class.getCanonicalName());
+ }
+ applicationDesc.setSinkClass(sinkClass);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(),e.getCause());
+ }
+ }
+
+ protected void setVersion(String version) {
+ applicationDesc.setVersion(version);
+ }
+
+ protected void setName(String name) {
+ applicationDesc.setName(name);
+ }
+
+ protected void setAppClass(Class<T> appClass) {
+ applicationDesc.setAppClass(appClass);
+ }
+
+ protected void setViewPath(String viewPath) {
+ applicationDesc.setViewPath(viewPath);
+ }
+
+ protected void setConfiguration(Configuration configuration) {
+ applicationDesc.setConfiguration(configuration);
+ }
+
+ protected void setAppConfig(String resourceName) {
+ try {
+ applicationDesc.setConfiguration(Configuration.fromResource(resourceName));
+ } catch (JAXBException e) {
+ LOG.error("Failed to load configuration template from "+resourceName,e);
+ throw new RuntimeException("Failed to load configuration template from "+resourceName,e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "%s[name=%s, type=%s, version=%s, viewPath=%s, appClass=%s, configuration= %s properties]", getClass().getSimpleName(),
+ applicationDesc.getName(),applicationDesc.getType(),applicationDesc.getVersion(), applicationDesc.getViewPath(), applicationDesc.getAppClass(), applicationDesc.getConfiguration().size()
+ );
+ }
+
+ protected void setStreams(List<StreamDefinition> streams) {
+ applicationDesc.setStreams(streams);
+ }
+
+
+ protected void setDocs(ApplicationDocs docs) {
+ applicationDesc.setDocs(docs);
+ }
+
+ public void setType(String type) {
+ applicationDesc.setType(type);
+ }
+
+ @Override
+ public ApplicationDesc getApplicationDesc() {
+ return applicationDesc;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/ApplicationProvider.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/ApplicationProvider.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/ApplicationProvider.java
new file mode 100644
index 0000000..ace0c45
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/spi/ApplicationProvider.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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.spi;
+
+import com.typesafe.config.Config;
+import org.apache.eagle.app.Application;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.metadata.model.ApplicationDesc;
+
+public interface ApplicationProvider<T extends Application> {
+
+ void prepare(ApplicationProviderConfig providerConfig,Config envConfig);
+
+ /**
+ * @return application descriptor
+ */
+ ApplicationDesc getApplicationDesc();
+
+ /**
+ * @return application instance
+ */
+ T getApplication();
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulator.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulator.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulator.java
new file mode 100644
index 0000000..a3ef0ee
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulator.java
@@ -0,0 +1,51 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.test;
+
+import org.apache.eagle.app.spi.ApplicationProvider;
+
+import java.util.Map;
+
+/**
+ * Application test simulator for developer to quickly run application without diving into application lifecycle
+ */
+public interface AppSimulator {
+ /**
+ *
+ * @param appType
+ */
+ void submit(String appType);
+ /**
+ *
+ * @param appType
+ * @param appConfig
+ */
+ void submit(String appType, Map<String,Object> appConfig);
+
+ /**
+ *
+ * @param appProviderClass
+ */
+ void submit(Class<? extends ApplicationProvider> appProviderClass);
+
+ /**
+ *
+ * @param appProviderClass
+ * @param appConfig
+ */
+ void submit(Class<? extends ApplicationProvider> appProviderClass, Map<String,Object> appConfig) throws Exception;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulatorImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulatorImpl.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulatorImpl.java
new file mode 100644
index 0000000..f5af815
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppSimulatorImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.test;
+
+import com.google.inject.Inject;
+import com.typesafe.config.Config;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.app.resource.ApplicationResource;
+import org.apache.eagle.app.service.AppOperations;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.apache.eagle.app.tools.DynamicJarPathFinder;
+import org.apache.eagle.metadata.model.ApplicationEntity;
+import org.apache.eagle.metadata.model.SiteEntity;
+import org.apache.eagle.metadata.resource.SiteResource;
+import org.junit.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AppSimulatorImpl implements AppSimulator {
+ private final Config config;
+ private final SiteResource siteResource;
+ private final ApplicationResource applicationResource;
+
+ @Inject
+ public AppSimulatorImpl(Config config, SiteResource siteResource,ApplicationResource applicationResource){
+ this.config = config;
+ this.siteResource = siteResource;
+ this.applicationResource = applicationResource;
+ }
+
+ @Override
+ public void submit(String appType) {
+ submit(appType, new HashMap<>());
+ }
+
+ @Override
+ public void submit(String appType, Map<String, Object> appConfig) {
+ SiteEntity siteEntity = getUniqueSite();
+ siteResource.createSite(siteEntity);
+ Assert.assertNotNull(siteEntity.getUuid());
+ // Install application
+ ApplicationEntity applicationEntity = applicationResource.installApplication(new AppOperations.InstallOperation(siteEntity.getSiteId(),appType, ApplicationEntity.Mode.LOCAL));
+ // Start application
+ applicationResource.startApplication(new AppOperations.StartOperation(applicationEntity.getUuid()));
+ }
+
+ private final static AtomicInteger incr = new AtomicInteger();
+
+ private SiteEntity getUniqueSite(){
+ // Create local site
+ SiteEntity siteEntity = new SiteEntity();
+ siteEntity.setSiteId("SIMULATED_SITE_"+incr.incrementAndGet());
+ siteEntity.setSiteName(siteEntity.getSiteId());
+ siteEntity.setDescription("Automatically generated unique simulation site "+siteEntity.getSiteId()+" (simulator: "+this+")");
+ return siteEntity;
+ }
+
+ @Override
+ public void submit(Class<? extends ApplicationProvider> appProviderClass) {
+ submit(appProviderClass, new HashMap<>());
+ }
+
+ @Override
+ public void submit(Class<? extends ApplicationProvider> appProviderClass, Map<String, Object> appConfig) {
+ try {
+ ApplicationProvider applicationProvider = appProviderClass.newInstance();
+ applicationProvider.prepare(new ApplicationProviderConfig(DynamicJarPathFinder.findPath(appProviderClass),appProviderClass),config);
+ submit(applicationProvider.getApplicationDesc().getType(),appConfig);
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException(e.getMessage(),e);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppTestGuiceModule.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppTestGuiceModule.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppTestGuiceModule.java
new file mode 100644
index 0000000..b2b0194
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppTestGuiceModule.java
@@ -0,0 +1,33 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Singleton;
+import org.apache.eagle.app.ApplicationGuiceModule;
+import org.apache.eagle.common.module.CommonGuiceModule;
+import org.apache.eagle.metadata.persistence.MemoryMetadataStore;
+
+public class AppTestGuiceModule extends AbstractModule{
+ @Override
+ protected void configure() {
+ install(new CommonGuiceModule());
+ install(new ApplicationGuiceModule());
+ install(new MemoryMetadataStore());
+ bind(AppSimulator.class).to(AppSimulatorImpl.class).in(Singleton.class);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppUnitTestRunner.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppUnitTestRunner.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppUnitTestRunner.java
new file mode 100644
index 0000000..2dda4d6
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/AppUnitTestRunner.java
@@ -0,0 +1,84 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.InitializationError;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class AppUnitTestRunner extends BlockJUnit4ClassRunner {
+ private final Injector injector;
+ public AppUnitTestRunner(Class<?> klass) throws InitializationError {
+ super(klass);
+ injector = createInjectorFor(getModulesFor(klass));
+ }
+
+ @Override
+ protected Object createTest() throws Exception {
+ final Object obj = super.createTest();
+ injector.injectMembers(this);
+ this.injector.injectMembers(obj);
+ return obj;
+ }
+
+ /**
+ * Create a Guice Injector for the class under test.
+ * @param classes Guice Modules
+ * @return A Guice Injector instance.
+ * @throws InitializationError If couldn't instantiate a module.
+ */
+ private Injector createInjectorFor(final Class<?>[] classes)
+ throws InitializationError {
+ final List<Module> modules = new ArrayList<>();
+
+ // Add default modules
+ modules.add(new AppTestGuiceModule());
+
+ if(classes!= null) {
+ for (final Class<?> module : Arrays.asList(classes)) {
+ try {
+ modules.add((Module) module.newInstance());
+ } catch (final ReflectiveOperationException exception) {
+ throw new InitializationError(exception);
+ }
+ }
+ }
+ return Guice.createInjector(modules);
+ }
+
+ /**
+ * Get the list of Guice Modules request by GuiceModules annotation in the
+ * class under test.
+ * @param klass Class under test.
+ * @return A Class Array of Guice Modules required by this class.
+ * @throws InitializationError If the annotation is not present.
+ */
+ private Class<?>[] getModulesFor(final Class<?> klass)
+ throws InitializationError {
+ final Modules annotation = klass.getAnnotation(Modules.class);
+ if (annotation == null) {
+ return null;
+ }
+ return annotation.value();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/Modules.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/Modules.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/Modules.java
new file mode 100644
index 0000000..71a6b79
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/Modules.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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.test;
+
+import com.google.inject.AbstractModule;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines the Guice Modules in use in the test class.
+ *
+ * @version $Id$
+ */
+@Inherited
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Modules {
+ /**
+ * The Guice Modules classes needed by the class under test.
+ */
+ Class<? extends AbstractModule>[] value();
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/package-info.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/package-info.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/package-info.java
new file mode 100644
index 0000000..aaaf157
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/test/package-info.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ *
+ *
+ *
+ */
+
+/**
+ * <h1>How to test application ?</h1>
+ *
+ * <h2>Option 1: Test with AppTestRunner</h2>
+ * <pre>
+ * @RunWith(AppTestRunner.class)
+ * public class ExampleApplicationTest {
+ * @Inject
+ * private ApplicationResource applicationResource;
+ * }
+ * </pre>
+ *
+ * <h2>Option 2: Manually create injector</h2>
+ * <pre>
+ * public class ExampleApplicationTest {
+ * @Inject
+ * private ApplicationResource applicationResource;
+ *
+ * @Before
+ * public void setUp(){
+ * Guice.createInjector(new AppTestModule()).injector.injectMembers(this);
+ * }
+ * }
+ * </pre>
+ */
+package org.apache.eagle.app.test;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/tools/DynamicJarPathFinder.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/tools/DynamicJarPathFinder.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/tools/DynamicJarPathFinder.java
new file mode 100644
index 0000000..fce72fe
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/tools/DynamicJarPathFinder.java
@@ -0,0 +1,121 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app.tools;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+
+/**
+ * http://stackoverflow.com/questions/1983839/determine-which-jar-file-a-class-is-from
+ * https://github.com/rzwitserloot/lombok.patcher/blob/master/src/lombok/patcher/inject/LiveInjector.java
+ */
+public class DynamicJarPathFinder {
+ private final static Logger LOG = LoggerFactory.getLogger(DynamicJarPathFinder.class);
+ /**
+ * If the provided class has been loaded from a jar file that is on the local file system, will find the absolute path to that jar file.
+ *
+ * @param context The jar file that contained the class file that represents this class will be found. Specify {@code null} to let {@code LiveInjector}
+ * find its own jar.
+ * @throws IllegalStateException If the specified class was loaded from a directory or in some other way (such as via HTTP, from a database, or some
+ * other custom classloading device).
+ */
+ public static String findPathJar(Class<?> context) throws IllegalStateException {
+ if (context == null) context = DynamicJarPathFinder.class;
+ String rawName = context.getName();
+ String classFileName;
+ /* rawName is something like package.name.ContainingClass$ClassName. We need to turn this into ContainingClass$ClassName.class. */ {
+ int idx = rawName.lastIndexOf('.');
+ classFileName = (idx == -1 ? rawName : rawName.substring(idx+1)) + ".class";
+ }
+
+ String uri = context.getResource(classFileName).toString();
+ if (uri.startsWith("file:")) {
+ throw new IllegalStateException("This class has been loaded from a directory and not from a jar file.");
+ }
+ if (!uri.startsWith("jar:file:")) {
+ int idx = uri.indexOf(':');
+ String protocol = idx == -1 ? "(unknown)" : uri.substring(0, idx);
+ throw new IllegalStateException("This class has been loaded remotely via the " + protocol +
+ " protocol. Only loading from a jar on the local file system is supported.");
+ }
+
+ int idx = uri.indexOf('!');
+ //As far as I know, the if statement below can't ever trigger, so it's more of a sanity check thing.
+ if (idx == -1) throw new IllegalStateException("You appear to have loaded this class from a local jar file, but I can't make sense of the URL!");
+
+ try {
+ String fileName = URLDecoder.decode(uri.substring("jar:file:".length(), idx), Charset.defaultCharset().name());
+ return new File(fileName).getAbsolutePath();
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError("default charset doesn't exist. Your VM is borked.");
+ }
+ }
+
+ /**
+ * Similar to JarPathFinder, but not make sure the path must valid jar.
+ *
+ * @see DynamicJarPathFinder#findPathJar(Class)
+ * @return the class path contains the context class
+ */
+ public static String findPath(Class<?> context) throws IllegalStateException {
+ if (context == null) context = DynamicJarPathFinder.class;
+ String rawName = context.getName();
+ String classFileName;
+ /* rawName is something like package.name.ContainingClass$ClassName. We need to turn this into ContainingClass$ClassName.class. */ {
+ int idx = rawName.lastIndexOf('.');
+ classFileName = (idx == -1 ? rawName : rawName.substring(idx+1)) + ".class";
+ }
+
+ String uri = context.getResource(classFileName).toString();
+ if (uri.startsWith("file:")) {
+ LOG.warn("This class has been loaded from a directory and not from a jar file: {}",uri);
+ String fileName = null;
+ try {
+ fileName = URLDecoder.decode(uri.substring("file:".length(), uri.length()), Charset.defaultCharset().name());
+ return new File(fileName).getAbsolutePath();
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError("default charset doesn't exist. Your VM is borked.");
+ }
+ }
+
+ if (!uri.startsWith("jar:file:")) {
+ int idx = uri.indexOf(':');
+ String protocol = idx == -1 ? "(unknown)" : uri.substring(0, idx);
+ throw new IllegalStateException("This class has been loaded remotely via the " + protocol +
+ " protocol. Only loading from a jar on the local file system is supported.");
+ }
+
+ int idx = uri.indexOf('!');
+ //As far as I know, the if statement below can't ever trigger, so it's more of a sanity check thing.
+ if (idx == -1) {
+ throw new IllegalStateException("You appear to have loaded this class from a local jar file, but I can't make sense of the URL!");
+ }
+
+ try {
+ String fileName = URLDecoder.decode(uri.substring("jar:file:".length(), idx), Charset.defaultCharset().name());
+ return new File(fileName).getAbsolutePath();
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError("default charset doesn't exist. Your VM is borked.");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/resources/applications.xml
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/resources/applications.xml b/eagle-core/eagle-app/eagle-app-base/src/main/resources/applications.xml
new file mode 100644
index 0000000..5f67807
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/resources/applications.xml
@@ -0,0 +1,42 @@
+<?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.
+ -->
+
+<applications>
+ <application>
+ <type>EXAMPLE_APP</type>
+ <version>0.2.0</version>
+ <name>Example Application</name>
+ <jarPath>target/apache-eagle-example-app.jar</jarPath>
+ <className>org.apache.eagle.app.base.example.ExampleApplication</className>
+ <!-- 'view' provides UI elements like portal/widget/dashboard, etc. -->
+ <viewPath>webapp/app/example</viewPath>
+ <configuration>
+ <property>
+ <name>kafka.topic</name>
+ <value>hdfs_audit</value>
+ <description>Kafka Topic</description>
+ </property>
+ <property>
+ <name>zookeeper.server</name>
+ <displayName>Zookeeper Server</displayName>
+ <value>localhost:2181</value>
+ <description>Zookeeper Server address</description>
+ </property>
+ </configuration>
+ </application>
+</applications>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/resources/log4j.properties b/eagle-core/eagle-app/eagle-app-base/src/main/resources/log4j.properties
new file mode 100644
index 0000000..fb13ad5
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/main/resources/log4j.properties
@@ -0,0 +1,21 @@
+# 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.
+
+log4j.rootLogger=DEBUG, stdout
+
+# standard output
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %p [%t] %c{2}[%L]: %m%n
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderDescConfigTest.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderDescConfigTest.java b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderDescConfigTest.java
new file mode 100644
index 0000000..5a6980c
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderDescConfigTest.java
@@ -0,0 +1,53 @@
+package org.apache.eagle.app;
+
+import org.apache.eagle.app.config.ApplicationProviderDescConfig;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+import java.io.InputStream;
+
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ */
+public class ApplicationProviderDescConfigTest {
+ @Test
+ public void testApplicationDescWrapperConfigLoadFromXML(){
+ ApplicationProviderDescConfig config = ApplicationProviderDescConfig.loadFromXML("TestApplicationMetadata.xml");
+ Assert.assertNotNull(config);
+ }
+
+ @Test
+ public void testStreamDefinitionLoadFromXML(){
+ String configXmlFile = "TestStreamDefinitionConf.xml";
+ try {
+ JAXBContext jc = JAXBContext.newInstance(StreamDefinitions.class);
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ InputStream is = ApplicationProviderDescConfigTest.class.getResourceAsStream(configXmlFile);
+ if(is == null){
+ is = ApplicationProviderDescConfigTest.class.getResourceAsStream("/"+configXmlFile);
+ }
+ if(is == null){
+ throw new IllegalStateException("Stream Definition configuration "+configXmlFile+" is not found");
+ }
+ StreamDefinitions streamDefinitions = (StreamDefinitions) unmarshaller.unmarshal(is);
+ Assert.assertNotNull(streamDefinitions);
+ } catch (Exception ex){
+ throw new RuntimeException("Failed to load application descriptor configuration: "+configXmlFile,ex);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderServiceTest.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderServiceTest.java b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderServiceTest.java
new file mode 100644
index 0000000..61f7542
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ApplicationProviderServiceTest.java
@@ -0,0 +1,47 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app;
+
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.eagle.app.config.ApplicationProviderConfig;
+import org.apache.eagle.app.service.ApplicationProviderService;
+import org.apache.eagle.app.spi.ApplicationProvider;
+import org.apache.eagle.common.module.CommonGuiceModule;
+import org.apache.eagle.metadata.model.ApplicationDesc;
+import org.apache.eagle.metadata.persistence.MemoryMetadataStore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+
+public class ApplicationProviderServiceTest {
+ private final static Logger LOGGER = LoggerFactory.getLogger(ApplicationProviderServiceTest.class);
+ private Injector injector = Guice.createInjector(new CommonGuiceModule(),new ApplicationGuiceModule(), MemoryMetadataStore.getInstance());
+
+ @Test
+ public void testApplicationProviderManagerInit(){
+ ApplicationProviderService providerManager = injector.getInstance(ApplicationProviderService.class);
+ Collection<ApplicationDesc> applicationDescs = providerManager.getApplicationDescs();
+ Collection<ApplicationProvider> applicationProviders = providerManager.getProviders();
+
+ applicationDescs.forEach((d)-> LOGGER.debug(d.toString()));
+ applicationProviders.forEach((d)-> LOGGER.debug(d.toString()));
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ConfigurationHelperTest.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ConfigurationHelperTest.java b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ConfigurationHelperTest.java
new file mode 100644
index 0000000..ebdae61
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/ConfigurationHelperTest.java
@@ -0,0 +1,33 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app;
+
+import org.apache.eagle.metadata.model.Configuration;
+import org.apache.eagle.metadata.utils.ConfigTemplateHelper;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.xml.bind.JAXBException;
+
+public class ConfigurationHelperTest {
+ @Test
+ public void testConfigTemplateUnmarshall() throws JAXBException {
+ Configuration configuration = ConfigTemplateHelper.unmarshallFromResource("/config_template.xml");
+ Assert.assertNotNull(configuration);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/StreamDefinitions.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/StreamDefinitions.java b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/StreamDefinitions.java
new file mode 100644
index 0000000..7ca0176
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/StreamDefinitions.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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app;
+
+import org.apache.eagle.alert.engine.coordinator.StreamDefinition;
+
+import javax.xml.bind.annotation.*;
+import java.util.List;
+
+@XmlRootElement(name = "streams")
+public class StreamDefinitions{
+
+ private List<StreamDefinition> streams;
+
+ @XmlElement(name = "stream")
+ public List<StreamDefinition> getStreams() {
+ return streams;
+ }
+
+ public void setStreams(List<StreamDefinition> streams) {
+ this.streams = streams;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationImpl.java b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationImpl.java
new file mode 100644
index 0000000..90a2b90
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.eagle.app;
+
+import backtype.storm.spout.SpoutOutputCollector;
+import backtype.storm.task.TopologyContext;
+import backtype.storm.topology.OutputFieldsDeclarer;
+import backtype.storm.topology.TopologyBuilder;
+import backtype.storm.topology.base.BaseRichSpout;
+import backtype.storm.tuple.Fields;
+import backtype.storm.tuple.Values;
+import org.apache.eagle.app.spi.AbstractApplicationProvider;
+import org.junit.Ignore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+@Ignore
+public class TestApplicationImpl extends AbstractApplication {
+ private final static Logger LOG = LoggerFactory.getLogger(TestApplicationImpl.class);
+ public class RandomEventSpout extends BaseRichSpout {
+ SpoutOutputCollector _collector;
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
+ _collector = collector;
+ }
+
+ @Override
+ public void nextTuple() {
+
+ }
+
+ @Override
+ public void ack(Object id) {
+ //Ignored
+ }
+
+ @Override
+ public void fail(Object id) {
+ _collector.emit(new Values(id), id);
+ }
+
+ @Override
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
+ declarer.declare(new Fields("key","event"));
+ }
+ }
+
+ protected void buildTopology(TopologyBuilder builder, ApplicationContext context) {
+ builder.setSpout("mockMetricSpout", new RandomEventSpout(), 4);
+ builder.setBolt("sink_1",context.getStreamSink("TEST_STREAM_1")).fieldsGrouping("mockMetricSpout",new Fields("key"));
+ builder.setBolt("sink_2",context.getStreamSink("TEST_STREAM_2")).fieldsGrouping("mockMetricSpout",new Fields("key"));
+ }
+
+ public static class Provider extends AbstractApplicationProvider<TestApplicationImpl> {
+ public Provider(){
+ super("TestApplicationMetadata.xml");
+ }
+
+ @Override
+ public TestApplicationImpl getApplication() {
+ return new TestApplicationImpl();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationTestSuite.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationTestSuite.java b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationTestSuite.java
new file mode 100644
index 0000000..f86f903
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/java/org/apache/eagle/app/TestApplicationTestSuite.java
@@ -0,0 +1,82 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.eagle.app;
+
+import com.google.inject.Inject;
+import org.apache.eagle.app.resource.ApplicationResource;
+import org.apache.eagle.app.service.AppOperations;
+import org.apache.eagle.app.test.AppSimulator;
+import org.apache.eagle.app.test.AppUnitTestRunner;
+import org.apache.eagle.metadata.model.ApplicationDesc;
+import org.apache.eagle.metadata.model.ApplicationEntity;
+import org.apache.eagle.metadata.model.SiteEntity;
+import org.apache.eagle.metadata.resource.SiteResource;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collection;
+
+@RunWith(AppUnitTestRunner.class)
+public class TestApplicationTestSuite {
+ @Inject private SiteResource siteResource;
+ @Inject private ApplicationResource applicationResource;
+ @Inject private AppSimulator simulator;
+
+ @Test
+ public void testApplicationProviderLoading(){
+ Collection<ApplicationDesc> applicationDescs = applicationResource.getApplicationDescs();
+ Assert.assertNotNull(applicationDescs);
+ Assert.assertEquals(1,applicationDescs.size());
+ }
+
+ @Test
+ public void testApplicationLifecycle() throws InterruptedException {
+ // Create local site
+ SiteEntity siteEntity = new SiteEntity();
+ siteEntity.setSiteId("test_site");
+ siteEntity.setSiteName("Test Site");
+ siteEntity.setDescription("Test Site for ExampleApplicationTest");
+ siteResource.createSite(siteEntity);
+ Assert.assertNotNull(siteEntity.getUuid());
+
+ // Install application
+ ApplicationEntity applicationEntity = applicationResource.installApplication(new AppOperations.InstallOperation("test_site","TEST_APPLICATION", ApplicationEntity.Mode.LOCAL));
+ // Start application
+ applicationResource.startApplication(new AppOperations.StartOperation(applicationEntity.getUuid()));
+ // Stop application
+ applicationResource.stopApplication(new AppOperations.StopOperation(applicationEntity.getUuid()));
+ // Uninstall application
+ applicationResource.uninstallApplication(new AppOperations.UninstallOperation(applicationEntity.getUuid()));
+ try {
+ applicationResource.getApplicationEntityByUUID(applicationEntity.getUuid());
+ Assert.fail("Application instance (UUID: "+applicationEntity.getUuid()+") should have been uninstalled");
+ } catch (Exception ex){
+ // Expected exception
+ }
+ }
+
+ @Test
+ public void testApplicationQuickRunWithAppType(){
+ simulator.submit("TEST_APPLICATION");
+ }
+
+ @Test
+ public void testApplicationQuickRunWithAppProvider(){
+ simulator.submit(TestApplicationImpl.Provider.class);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-app/eagle-app-base/src/test/resources/ExampleApplicationConf.xml
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-app/eagle-app-base/src/test/resources/ExampleApplicationConf.xml b/eagle-core/eagle-app/eagle-app-base/src/test/resources/ExampleApplicationConf.xml
new file mode 100644
index 0000000..4a31c5e
--- /dev/null
+++ b/eagle-core/eagle-app/eagle-app-base/src/test/resources/ExampleApplicationConf.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+<configuration>
+ <property>
+ <name>kafka.topic</name>
+ <displayName>Kafka Topic</displayName>
+ <value>hdfs_audit</value>
+ <description>Kafka Topic</description>
+ </property>
+ <property>
+ <name>zookeeper.server</name>
+ <displayName>Zookeeper Server</displayName>
+ <value>localhost:2181</value>
+ <description>Zookeeper Server address</description>
+ </property>
+</configuration>
\ No newline at end of file