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:49 UTC

[05/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-common/src/main/java/org/apache/eagle/common/service/LdapService.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/LdapService.java b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/LdapService.java
new file mode 100644
index 0000000..31f1d01
--- /dev/null
+++ b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/LdapService.java
@@ -0,0 +1,259 @@
+/*
+ * 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.eagle.common.service;
+
+
+import org.apache.eagle.common.config.EagleConfig;
+import org.apache.eagle.common.config.EagleConfigFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.*;
+import java.util.*;
+
+/**
+ * @since : 7/11/14,2014
+ */
+public class LdapService {
+    private final static Logger LOG = LoggerFactory.getLogger(LdapService.class);
+
+    private final List<String> ldapSrvs;
+    private String ldapCerts;
+    private final String securityPrincipal;
+    private final String securityCredentials;
+
+    public final static String SECURITY_PRINCIPAL_CONFIG_NAME = "eagle.ldap.security-principal";
+    public final static String SECURITY_CREDENTIALS_CONFIG_NAME = "eagle.ldap.security-credentials";
+    public final static String LDAP_SERVER_CONFIG_NAME = "eagle.ldap.server";
+    public final static String LDAP_CERTS_CONFIG_NAME = "eagle.ldap.certs";
+    public final static String DEFAULT_LDAP_CERTS_FILE_NAME = "jssecacerts";
+
+    private LdapService(){
+        EagleConfig manager = EagleConfigFactory.load();
+        securityPrincipal = manager.getConfig().getString(SECURITY_PRINCIPAL_CONFIG_NAME);
+        securityCredentials = manager.getConfig().getString(SECURITY_CREDENTIALS_CONFIG_NAME);
+        String ldapServer = manager.getConfig().getString(LDAP_SERVER_CONFIG_NAME);
+        if(LOG.isDebugEnabled())
+            LOG.debug(SECURITY_PRINCIPAL_CONFIG_NAME+":"+securityPrincipal);
+        if(securityCredentials!=null){
+            if(LOG.isDebugEnabled())
+                LOG.debug(SECURITY_CREDENTIALS_CONFIG_NAME+": (hidden for security, length: "+securityCredentials.length()+")");
+        }else{
+            LOG.warn(SECURITY_CREDENTIALS_CONFIG_NAME+":"+null);
+        }
+        if(LOG.isDebugEnabled())
+            LOG.debug(LDAP_SERVER_CONFIG_NAME+":"+ldapServer);
+
+        ldapSrvs = Arrays.asList(ldapServer.split(","));
+        ldapCerts = manager.getConfig().getString(LDAP_CERTS_CONFIG_NAME);
+        if(ldapCerts == null) {
+            ldapCerts = LdapService.class.getClassLoader().getResource(DEFAULT_LDAP_CERTS_FILE_NAME).getPath();
+        }else if(!ldapCerts.startsWith("/") && !ldapCerts.matches("[a-zA-Z]+:.*")) {
+            ldapCerts = LdapService.class.getClassLoader().getResource(ldapCerts).getPath();
+        }
+        if(LOG.isDebugEnabled()) {
+            LOG.debug(SECURITY_PRINCIPAL_CONFIG_NAME +": "+securityPrincipal);
+            if(securityCredentials == null){
+                LOG.debug(SECURITY_CREDENTIALS_CONFIG_NAME +": null");
+            }else{
+                LOG.debug(SECURITY_CREDENTIALS_CONFIG_NAME +": (hidden, length: "+securityCredentials .length()+")");
+            }
+
+            LOG.debug(LDAP_SERVER_CONFIG_NAME +": "+ldapSrvs);
+            LOG.debug(LDAP_CERTS_CONFIG_NAME +": "+ldapCerts);
+        }
+    }
+
+    private static LdapService instance;
+
+    public static LdapService getInstance(){
+        if(instance == null){
+            instance = new LdapService();
+        }
+        return instance;
+    }
+
+    protected DirContext getDirContext(int id) {
+        if (ldapCerts != null) {
+            System.setProperty("javax.net.ssl.keyStore", ldapCerts);
+            System.setProperty("javax.net.ssl.trustStore", ldapCerts);
+        }
+
+        String host = ldapSrvs.get(id);
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+//		if (ldapCerts != null) {
+        env.put(Context.SECURITY_PROTOCOL, "ssl");
+//		}
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, host);
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        env.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
+        env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+        env.put("java.naming.ldap.attributes.binary", "objectSID");
+        env.put("java.naming.ldap.factory.socket","hadoop.eagle.common.service.TrustAllSSLSocketFactory");
+
+        DirContext ctx = null;
+        try {
+            ctx = new InitialDirContext(env);
+        } catch (Exception e) {
+            ctx = null;
+            LOG.error("LDAP authentication failed with exception: " + e.getMessage(), e);
+        }
+        return ctx;
+    }
+
+    public final static String CN= "cn";
+    public final static String DISPLAY_NAME =  "displayName";
+    public final static String DESCRIPTION= "description";
+    public final static String SAMACCOUNT_NAME= "sAMAccountName";
+    public final static String TELEPHONE_NUMBER= "telephonenumber";
+    public final static String GIVEN_NAME= "givenName";
+    public final static String UID_NUMBER =  "uidNumber";
+    public final static String L = "l";
+    public final static String ST = "st";
+    public final static String CO = "co";
+    public final static String MEMBER_OF = "memberof";
+    public final static String SN =  "sn";
+    public final static String MAIL = "mail";
+    public final static String DISTINGUISHED_NAME =  "distinguishedName";
+
+    protected SearchControls getSearchControl() {
+        SearchControls sc = new SearchControls();
+
+        String[] attributeFilter = new String[15];
+        attributeFilter[0] = CN;
+        attributeFilter[1] =  DISPLAY_NAME ;
+        attributeFilter[2] = DESCRIPTION;
+        attributeFilter[3] =  SAMACCOUNT_NAME;
+        attributeFilter[4] =  TELEPHONE_NUMBER;
+        attributeFilter[5] = GIVEN_NAME;
+        attributeFilter[6] = UID_NUMBER;
+        attributeFilter[7] = L;
+        attributeFilter[8] = ST;
+        attributeFilter[9] =CO;
+        attributeFilter[10] = MEMBER_OF;
+        attributeFilter[11] = SN;
+        attributeFilter[12] = MAIL;
+        attributeFilter[13] = DISTINGUISHED_NAME;
+
+        sc.setReturningAttributes(attributeFilter);
+        sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+        return sc;
+    }
+
+    public Map<String, String> getUserInfo(String userName) {
+        Map<String, String> infos = null;
+        for (int i = 0; i < ldapSrvs.size(); i++) {
+            if(LOG.isDebugEnabled()) LOG.debug("Using server: "+ldapSrvs.get(i));
+            infos = getUserInfo(i, userName);
+            if (infos.size() > 0)
+                break;
+        }
+        return infos;
+    }
+
+    public Map<String, String> getUserInfo(int id, String userName) {
+        if(LOG.isDebugEnabled()) LOG.debug("Ldap get user information for id:"+id+", username:"+userName);
+        DirContext ctx = getDirContext(id);
+        Map<String, String> infos = new HashMap<String, String>();
+
+        if (ctx != null) {
+            try {
+                SearchControls sc = getSearchControl();
+                String filter = "(&(objectClass=user)(sAMAccountName=" + userName + "))";
+                NamingEnumeration<?> results = ctx.search("OU=Accounts_User,DC=corp,DC=company1,DC=com", filter, sc);
+
+                while (results.hasMore()) {
+                    SearchResult sr = (SearchResult) results.next();
+                    Attributes attrs = sr.getAttributes();
+
+                    for (NamingEnumeration<?> ae = attrs.getAll(); ae.hasMoreElements();) {
+                        Attribute attr = (Attribute) ae.next();
+                        String attrId = attr.getID();
+                        for (NamingEnumeration<?> vals = attr.getAll(); vals.hasMore();) {
+                            String thing = vals.next().toString();
+                            infos.put(attrId, thing);
+                        }
+                    }
+                }
+            } catch (NamingException e) {
+                LOG.error("LDAP authentication failed with exception: "+e.getMessage(),e);
+            }
+        }
+
+        if(LOG.isDebugEnabled()) LOG.debug(infos.toString());
+        return infos;
+    }
+
+    public boolean authenticate(String userName, String password) {
+        for (int i = 0; i < ldapSrvs.size(); i++) {
+            if (authenticate(i, userName, password))
+                return true;
+        }
+        return false;
+    }
+
+    public boolean authenticate(int id, String userName, String password) {
+        boolean result = false;
+
+        DirContext ctx = getDirContext(id);
+        if (ctx != null) {
+            try {
+                SearchControls sc = getSearchControl();
+                String filter = "(&(objectClass=user)(sAMAccountName=" + userName + "))";
+                NamingEnumeration<?> results = ctx.search("OU=Accounts_User,DC=corp,DC=company1,DC=com", filter, sc);
+
+                String userDN = null;
+                if (results.hasMore()) {
+                    while (results.hasMore()) {
+                        SearchResult sr = (SearchResult) results.next();
+                        Attributes attrs = sr.getAttributes();
+
+                        userDN = attrs.get("distinguishedName").get().toString();
+                    }
+                }
+                ctx.close();
+
+                if (userDN != null) {
+                    Hashtable<String, String> uenv = new Hashtable<String, String>();
+//					if (ldapCerts != null) {
+                    uenv.put(Context.SECURITY_PROTOCOL, "ssl");
+//					}
+                    uenv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+                    uenv.put(Context.PROVIDER_URL, ldapSrvs.get(id));
+                    uenv.put(Context.SECURITY_AUTHENTICATION, "simple");
+                    uenv.put(Context.SECURITY_PRINCIPAL, userDN);
+                    uenv.put(Context.SECURITY_CREDENTIALS, password);
+                    uenv.put("java.naming.ldap.factory.socket","hadoop.eagle.common.service.TrustAllSSLSocketFactory");
+                    DirContext uctx = new InitialDirContext(uenv);
+                    uctx.close();
+
+                    result = true;
+                }
+            } catch (NamingException e) {
+                LOG.error("LDAP authentication failed with exception: " + e.getMessage(), e);
+            }
+        }
+
+        return result;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/POSTResultEntityBase.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/POSTResultEntityBase.java b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/POSTResultEntityBase.java
new file mode 100644
index 0000000..e0daeb7
--- /dev/null
+++ b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/POSTResultEntityBase.java
@@ -0,0 +1,42 @@
+/*
+ * 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.eagle.common.service;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(propOrder = {"success", "exception"})
+public class POSTResultEntityBase {
+	private boolean success;
+	private String exception;
+	public boolean isSuccess() {
+		return success;
+	}
+	public void setSuccess(boolean success) {
+		this.success = success;
+	}
+	public String getException() {
+		return exception;
+	}
+	public void setException(String exception) {
+		this.exception = exception;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/TrustAllSSLSocketFactory.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/TrustAllSSLSocketFactory.java b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/TrustAllSSLSocketFactory.java
new file mode 100644
index 0000000..9facc82
--- /dev/null
+++ b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/service/TrustAllSSLSocketFactory.java
@@ -0,0 +1,94 @@
+/*
+ * 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.eagle.common.service;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.cert.X509Certificate;
+
+public class TrustAllSSLSocketFactory extends SSLSocketFactory
+{
+	private SSLSocketFactory socketFactory;
+	public TrustAllSSLSocketFactory()
+	{
+		try {
+			SSLContext ctx = SSLContext.getInstance("SSL");
+//			ctx.init(null, new TrustManager[]{new TrustAnyTrustManager() {}}, new SecureRandom());
+			ctx.init(null, new TrustManager[]{new TrustAnyTrustManager() {}}, null);
+			socketFactory = ctx.getSocketFactory();
+		} catch ( Exception ex ){ ex.printStackTrace(System.err);  /* handle exception */ }
+	}
+	public static SocketFactory getDefault(){
+		return new TrustAllSSLSocketFactory();
+	}
+	@Override
+	public String[] getDefaultCipherSuites()
+	{
+		return socketFactory.getDefaultCipherSuites();
+	}
+	@Override
+	public String[] getSupportedCipherSuites()
+	{
+		return socketFactory.getSupportedCipherSuites();
+	}
+	@Override
+	public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException
+	{
+		return socketFactory.createSocket(socket, string, i, bln);
+	}
+	@Override
+	public Socket createSocket(String string, int i) throws IOException, UnknownHostException
+	{
+		return socketFactory.createSocket(string, i);
+	}
+	@Override
+	public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException
+	{
+		return socketFactory.createSocket(string, i, ia, i1);
+	}
+	@Override
+	public Socket createSocket(InetAddress ia, int i) throws IOException
+	{
+		return socketFactory.createSocket(ia, i);
+	}
+	@Override
+	public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException
+	{
+		return socketFactory.createSocket(ia, i, ia1, i1);
+	}
+
+	private static class TrustAnyTrustManager implements X509TrustManager {
+		@Override
+		public void checkClientTrusted( final X509Certificate[] chain, final String authType ) {
+		}
+		@Override
+		public void checkServerTrusted( final X509Certificate[] chain, final String authType ) {
+		}
+		@Override
+		public X509Certificate[] getAcceptedIssuers() {
+			return null;
+		}
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/main/resources/footer.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/main/resources/footer.vm b/eagle-core/eagle-common/src/main/resources/footer.vm
new file mode 100755
index 0000000..89cf245
--- /dev/null
+++ b/eagle-core/eagle-common/src/main/resources/footer.vm
@@ -0,0 +1,25 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+	</td>
+  </tr>
+  </tr>
+</table>
+<!-- End of wrapper table -->
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/main/resources/header.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/main/resources/header.vm b/eagle-core/eagle-common/src/main/resources/header.vm
new file mode 100755
index 0000000..6731aee
--- /dev/null
+++ b/eagle-core/eagle-common/src/main/resources/header.vm
@@ -0,0 +1,303 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+  <title></title>
+  <style type="text/css">
+    /* Based on The MailChimp Reset INLINE: Yes. */
+    /* Client-specific Styles */
+    .outlook a {
+      color: #091D42;
+      padding: 0;
+      text-decoration: none;
+    }
+
+    /* Force Outlook to provide a "view in browser" menu link. */
+    body {
+      width: 100% !important;
+      -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+      margin: 0;
+      padding: 0;
+    }
+
+    /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
+    .ExternalClass {
+      width: 100%;
+    }
+
+    /* Force Hotmail to display emails at full width */
+    .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
+      line-height: 100%;
+    }
+
+    /* Forces Hotmail to display normal line spacing.  More on that: http://www.emailonacid.com/forum/viewthread/43/ */
+    #backgroundTable {
+      margin: 0;
+      padding: 0;
+      width: 100% !important;
+      line-height: 100% !important;
+        border: 1pt solid #BFB8AF;
+        background-color: #fff;
+    }
+
+    /* End reset */
+
+    /* Some sensible defaults for images
+    Bring inline: Yes. */
+    img {
+      outline: none;
+      text-decoration: none;
+      -ms-interpolation-mode: bicubic;
+    }
+
+    a img {
+      border: none;
+    }
+
+    .image_fix {
+      display: block;
+    }
+
+    /* Yahoo paragraph fix
+    Bring inline: Yes. */
+    p {
+      margin: 1em 0;
+    }
+
+    /* Hotmail header color reset
+    Bring inline: Yes. */
+    h1, h2, h3, h4, h5, h6 {
+      color: black !important;
+    }
+
+    h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+      color: blue !important;
+    }
+
+    h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {
+      color: red !important; /* Preferably not the same color as the normal header link color.  There is limited support for psuedo classes in email clients, this was added just for good measure. */
+    }
+
+    h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
+      color: purple !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
+    }
+
+    table{
+        width: 100%;
+    }
+
+    /* Outlook 07, 10 Padding issue fix
+    Bring inline: No.*/
+    table td {
+      border-collapse: collapse;
+    }
+
+    /* Remove spacing around Outlook 07, 10 tables
+    Bring inline: Yes */
+    table {
+      border-collapse: collapse;
+      mso-table-lspace: 0pt;
+      mso-table-rspace: 0pt;
+    }
+
+    /* Styling your links has become much simpler with the new Yahoo.  In fact, it falls in line with the main credo of styling in email and make sure to bring your styles inline.  Your link colors will be uniform across clients when brought inline.
+    Bring inline: Yes. */
+    a {
+      color: orange;
+    }
+
+    /***************************************************
+    ****************************************************
+    MOBILE TARGETING
+    ****************************************************
+    ***************************************************/
+    @media only screen and (max-device-width: 480px) {
+      /* Part one of controlling phone number linking for mobile. */
+      a[href^="tel"], a[href^="sms"] {
+        text-decoration: none;
+        color: blue; /* or whatever your want */
+        pointer-events: none;
+        cursor: default;
+      }
+
+      .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
+        text-decoration: default;
+        color: orange !important;
+        pointer-events: auto;
+        cursor: default;
+      }
+
+    }
+
+    /* More Specific Targeting */
+
+    @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
+      /* You guessed it, ipad (tablets, smaller screens, etc) */
+      /* repeating for the ipad */
+      a[href^="tel"], a[href^="sms"] {
+        text-decoration: none;
+        color: blue; /* or whatever your want */
+        pointer-events: none;
+        cursor: default;
+      }
+
+      .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
+        text-decoration: default;
+        color: orange !important;
+        pointer-events: auto;
+        cursor: default;
+      }
+    }
+
+    @media only screen and (-webkit-min-device-pixel-ratio: 2) {
+      /* Put your iPhone 4g styles in here */
+    }
+
+    /* Android targeting */
+    @media only screen and (-webkit-device-pixel-ratio: .75) {
+      /* Put CSS for low density (ldpi) Android layouts in here */
+    }
+
+    @media only screen and (-webkit-device-pixel-ratio: 1) {
+      /* Put CSS for medium density (mdpi) Android layouts in here */
+    }
+
+    @media only screen and (-webkit-device-pixel-ratio: 1.5) {
+      /* Put CSS for high density (hdpi) Android layouts in here */
+    }
+
+    /* end Android targeting */
+    .head{
+      font-family:Helvetica, Arial;
+      font-size:30px;
+      text-decoration:none;
+      text-align:left;
+      color:#333;
+      align:left;
+      padding: 25px 0 15px 0;
+      valign:middle;
+      font-weight: 500;
+      border-bottom: 1px solid #cccccc;
+    }
+
+    .head2{
+      font-family:Helvetica, Arial;
+      font-size:24px;
+      text-decoration:none;
+      text-align:left;
+      color:#333;
+      align:left;
+      padding: 20px 0 10px 0;
+      valign:middle;
+      font-weight: 500;
+    }
+
+    .text{
+      font-family:Helvetica, Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      color:#333;
+      align:left;
+      padding: 0.5em 0em 0.5em 0em;
+      valign:middle;
+    }
+
+    .info {
+      font-family:Helvetica, Arial;
+      font-size:16px;
+      text-decoration:none;
+      text-align:left;
+      padding-left:10px;
+      border-left: 3px solid #396A92;
+      background-color: #9AB4CB;
+      color:#091D42;
+      align:left;
+      padding: 1em 0em 1em 1em;
+      valign:middle;
+    }
+
+    .table-border{
+      border: 1px solid #ddd;
+      border-radius: 4px 4px 0 0;
+      box-shadow: none;
+    }
+
+    .table-border-th{
+      font-family:Helvetica, Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      border-top: 1px solid #cccccc;
+      border-right: 1px solid #cccccc;
+      padding: 8px;
+    }
+    .table-border-td{
+      font-family:Helvetica, Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      border-top: 1px solid #cccccc;
+      border-right: 1px solid #cccccc;
+      padding: 8px;
+      word-break: break-all;
+    }
+
+    .foot{
+      font-family:Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      padding: 0.5em 0em 0.5em 0.5em;
+      border-top: 1px solid #cccccc;
+      color:#777;
+    }
+
+  /* extend by hchen9 */
+
+  </style>
+
+  <!-- Targeting Windows Mobile -->
+  <!--[if IEMobile 7]>
+  <style type="text/css">
+
+  </style>
+  <![endif]-->
+
+  <!-- ***********************************************
+  ****************************************************
+  END MOBILE TARGETING
+  ****************************************************
+  ************************************************ -->
+
+  <!--[if gte mso 9]>
+  <style>
+    /* Target Outlook 2007 and 2010 */
+  </style>
+  <![endif]-->
+</head>
+<body>
+<!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
+<table cellpadding="0" cellspacing="0" border="0" id="backgroundTable" width="100%">
+  <tr>
+    <td valign="top" style="padding-left: 10px">

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/main/resources/templates/tec_alert.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/main/resources/templates/tec_alert.vm b/eagle-core/eagle-common/src/main/resources/templates/tec_alert.vm
new file mode 100755
index 0000000..fa3eee5
--- /dev/null
+++ b/eagle-core/eagle-common/src/main/resources/templates/tec_alert.vm
@@ -0,0 +1,113 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+#parse("header.vm")
+<table cellpadding="0" cellspacing="0" border="0" align="left" width="800" style="">
+  <tr>
+    <td class="head" width="800">
+      <span style="color: #091D42">Eagle Service <small style="color: #999999;">$startTime ~ $endTime</small></span>
+    </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Statistics
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+        <tr>
+          <th class="table-border-th" style="width: 25%">type</th>
+          <th class="table-border-th" style="width: 25%">count</th>
+          <th class="table-border-th" style="width: 25%">type</th>
+          <th class="table-border-th" style="width: 25%">count</th>
+        </tr>
+        </thead>
+        <tbody>
+        #foreach($key in $statistics.keySet())
+          #if( $foreach.count % 2 == 1 )
+          <tr>
+          #end
+            <td class="table-border-td">$key</td>
+            <td class="table-border-td">$statistics.get($key)</td>
+          #if( $foreach.count % 2 == 0)
+          </tr>
+          #end
+          #if( $foreach.count % 2 == 1 && !$foreach.hasNext )
+          	<td class="table-border-td"></td>
+            <td class="table-border-td"></td>
+          </tr>
+          #end
+       	#end
+        </tbody>
+      </table>
+    </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Detail Info
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td class="info" width="800">
+      <span>
+          These are the alerts that we can not find its hostname.
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+          <tr>
+            <th class="table-border-th">hostname</th>
+            <th class="table-border-th">date_reception</th>
+            <th class="table-border-th">type</th>
+            <th class="table-border-th">origin</th>
+            <th class="table-border-th">msg</th>
+          </tr>
+        </thead>
+        <tbody>
+        #foreach($entry in $noHostnameItems)
+          <tr>
+            <td class="table-border-td">$entry.hostname</td>
+            <td class="table-border-td">$entry.date_reception</td>
+            <td class="table-border-td">$entry.type</td>
+            <td class="table-border-td">$entry.origin</td>
+            <td class="table-border-td">$entry.msg</td>
+          </tr>
+       	#end
+        </tbody>
+      </table>
+    </td>
+  </tr>
+  <tr>
+    <td class="foot" width="800">
+      <span class="outlook">
+         Apache Eagle <a href="http://<Eagle-Host>:9090/eagle-web/ui/eagle.html"
+                  target ="_blank" title="Hadoop Eagle">Apache Eagle</a>
+      </span>
+    </td>
+  </tr>
+</table>
+#parse("footer.vm")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestByteUtil.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestByteUtil.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestByteUtil.java
new file mode 100644
index 0000000..a08c3d1
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestByteUtil.java
@@ -0,0 +1,111 @@
+/*
+ * 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.eagle.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestByteUtil {
+	
+	@Test
+	public void testLongAndBytesConversion() {
+		long origValue = 0x1234567812345678L;
+		byte[] bytes = ByteUtil.longToBytes(origValue);
+		checkNonZeros(bytes);
+		long value = ByteUtil.bytesToLong(bytes);
+		Assert.assertEquals(origValue, value);
+		bytes = new byte[16];
+		checkZeros(bytes);
+		ByteUtil.longToBytes(origValue, bytes, 4);
+		checkZeros(bytes, 0, 4);
+		checkZeros(bytes, 12, 16);
+		checkNonZeros(bytes, 4, 12);
+		value = ByteUtil.bytesToLong(bytes, 4);
+		Assert.assertEquals(origValue, value);
+	}
+	
+	@Test
+	public void testDoubleAndBytesConversion() {
+		double origValue =  (double)0x1234567812345678L;
+		byte[] bytes = ByteUtil.doubleToBytes(origValue);
+		checkNonZeros(bytes);
+		double value = ByteUtil.bytesToDouble(bytes);
+		Assert.assertEquals(origValue, value, 0.0001);
+		bytes = new byte[16];
+		checkZeros(bytes);
+		ByteUtil.doubleToBytes(origValue, bytes, 4);
+		checkZeros(bytes, 0, 4);
+		checkZeros(bytes, 12, 16);
+		checkNonZeros(bytes, 4, 12);
+		value = ByteUtil.bytesToDouble(bytes, 4);
+		Assert.assertEquals(origValue, value, 0.0001);
+	}
+	
+	@Test
+	public void testIntAndBytesConversion() {
+		int origValue = 0x12345678;
+		byte[] bytes = ByteUtil.intToBytes(origValue);
+		Assert.assertEquals(4, bytes.length);
+		Assert.assertEquals(0x12, bytes[0]);
+		Assert.assertEquals(0x34, bytes[1]);
+		Assert.assertEquals(0x56, bytes[2]);
+		Assert.assertEquals(0x78, bytes[3]);
+		checkNonZeros(bytes);
+		int value = ByteUtil.bytesToInt(bytes);
+		Assert.assertEquals(origValue, value);
+		bytes = new byte[12];
+		checkZeros(bytes);
+		ByteUtil.intToBytes(origValue, bytes, 4);
+		checkZeros(bytes, 0, 4);
+		checkZeros(bytes, 8, 12);
+		checkNonZeros(bytes, 4, 8);
+		value = ByteUtil.bytesToInt(bytes, 4);
+		Assert.assertEquals(origValue, value);
+	}
+
+	@Test
+	public void testShortAndBytesConversion() {
+		short origValue = 0x1234;
+		byte[] bytes = ByteUtil.shortToBytes(origValue);
+		Assert.assertEquals(2, bytes.length);
+		Assert.assertEquals(0x12, bytes[0]);
+		Assert.assertEquals(0x34, bytes[1]);
+		checkNonZeros(bytes);
+		short value = ByteUtil.bytesToShort(bytes);
+		Assert.assertEquals(origValue, value);
+	}
+
+	private void checkZeros(byte[] bytes) {
+		checkZeros(bytes, 0, bytes.length);
+	}
+	
+	private void checkZeros(byte[] bytes, int i, int j) {
+		for (int k = i; k < j; ++k) {
+			Assert.assertEquals((byte)0, bytes[k]);
+		}
+	}
+
+	private void checkNonZeros(byte[] bytes) {
+		checkNonZeros(bytes, 0, bytes.length);
+	}
+	
+	private void checkNonZeros(byte[] bytes, int i, int j) {
+		for (int k = i; k < j; ++k) {
+			Assert.assertNotSame((byte)0, bytes[k]);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayList.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayList.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayList.java
new file mode 100644
index 0000000..3642635
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayList.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
+ *
+ *    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.eagle.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCircularArrayList {
+
+	@Test
+	public void testAddAndRemove() {
+		Long[] array = new Long[5];
+		CircularArrayList<Long> list = new CircularArrayList<Long>(array);
+		
+		for (long i = 0 ; i < 5; ++i) {
+			list.add(i);
+			Assert.assertEquals((Long)i, array[(int) i]);
+			Assert.assertTrue(list.contains(i));
+			Assert.assertEquals(i, list.find(i));
+		}
+		Assert.assertFalse(list.contains(6L));
+		Exception e = null;
+		try {
+			list.add((long)5);
+		} catch (Exception ex) {
+			e = ex;
+		}
+		Assert.assertNotNull(e);
+		Assert.assertEquals(0, list.tail());
+		Assert.assertEquals(0, list.head());
+		Assert.assertEquals(5, list.size());
+		Assert.assertTrue(list.isFull());
+		Long v = list.remove(1);
+		Assert.assertEquals((Long)1L, v);
+		Assert.assertEquals(4, list.size());
+		Assert.assertEquals(1, list.head());
+		Assert.assertEquals(0, list.tail());
+		list.add((long)5);
+		Assert.assertEquals(5, list.size());
+		Assert.assertEquals(1, list.head());
+		Assert.assertEquals(1, list.tail());
+		Assert.assertEquals((Long)0L, list.remove(0));
+		Assert.assertEquals(2, list.head());
+		Assert.assertEquals(1, list.tail());
+		Assert.assertEquals((Long)2L, list.remove(0));
+		Assert.assertEquals(3, list.head());
+		Assert.assertEquals(1, list.tail());
+		Assert.assertEquals((Long)3L, list.remove(0));
+		Assert.assertEquals(4, list.head());
+		Assert.assertEquals(1, list.tail());
+		Assert.assertEquals((Long)4L, list.remove(0));
+		Assert.assertEquals(0, list.head());
+		Assert.assertEquals(1, list.tail());
+		Assert.assertEquals((Long)5L, list.remove(0));
+		Assert.assertEquals(1, list.head());
+		Assert.assertEquals(1, list.tail());
+		Assert.assertTrue(list.isEmpty());
+		Assert.assertFalse(list.isFull());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayListSortedSet.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayListSortedSet.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayListSortedSet.java
new file mode 100644
index 0000000..e4fbb18
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestCircularArrayListSortedSet.java
@@ -0,0 +1,58 @@
+/*
+ * 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.eagle.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCircularArrayListSortedSet {
+
+	@Test
+	public void testInsertAndRemove() {
+		Long[] array = new Long[5];
+		CircularArrayListSortedSet<Long> set = new CircularArrayListSortedSet<Long>(array);
+		
+		set.insert(3L);
+		set.insert(2L);
+		set.insert(1L);
+		set.insert(5L);
+		set.insert(4L);
+		
+		for (int i = 0; i < 5; ++i) {
+			Assert.assertEquals((Long)(long)(i + 1),set.get(i));
+			Assert.assertEquals(i, set.binarySearch((Long)(long)(i + 1)));
+		}
+		Assert.assertEquals(0, set.head());
+		Assert.assertEquals(0, set.tail());
+		Assert.assertTrue(set.isFull());
+		Assert.assertEquals(-(5 + 1), set.binarySearch(6L));
+		
+		Assert.assertEquals(2, set.remove(3L));
+		Assert.assertEquals(2, set.remove(4L));
+		Assert.assertEquals(-(2 + 1), set.binarySearch(3L));
+		set.insert(3L);
+		set.insert(4L);
+		
+		for (int i = 0; i < 5; ++i) {
+			Assert.assertEquals((Long)(long)(i + 1),set.get(i));
+			Assert.assertEquals(i, set.binarySearch((Long)(long)(i + 1)));
+		}
+		Assert.assertEquals(2, set.head());
+		Assert.assertEquals(2, set.tail());
+		Assert.assertTrue(set.isFull());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestDateTimeUtil.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestDateTimeUtil.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestDateTimeUtil.java
new file mode 100755
index 0000000..062d44a
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestDateTimeUtil.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
+ *
+ *    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.eagle.common;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestDateTimeUtil {
+	@Test
+	public void testRound1(){
+		long tsInMS = 1397016731576L;
+		long tsInMin = DateTimeUtil.roundDown(Calendar.MINUTE, tsInMS);
+		Assert.assertEquals(1397016720000L, tsInMin);
+		
+		GregorianCalendar cal = new GregorianCalendar();
+		cal.setTimeInMillis(tsInMS);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		Assert.assertEquals(tsInMin, cal.getTimeInMillis());
+	}
+	
+	@Test
+	public void testRound2(){
+		long tsInMS = 1397016731576L;
+		long tsInHour = DateTimeUtil.roundDown(Calendar.HOUR, tsInMS);
+		Assert.assertEquals(1397016000000L, tsInHour);
+		
+		GregorianCalendar cal = new GregorianCalendar();
+		cal.setTimeInMillis(tsInMS);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		Assert.assertEquals(tsInHour, cal.getTimeInMillis());
+	}
+	
+	@Test
+	public void testRound3(){
+		long tsInMS = 1L;
+		long tsInDay = DateTimeUtil.roundDown(Calendar.DATE, tsInMS);
+		Assert.assertEquals(0L, tsInDay);
+//		Assert.assertEquals("1970-01-01 08:00:00", DateTimeUtil.millisecondsToHumanDateWithSeconds(tsInDay));
+	}
+	
+	@Test
+	public void testRound4(){
+		long tsInMS = 0L;
+		long tsInDay = DateTimeUtil.roundDown(Calendar.DATE, tsInMS);
+		Assert.assertEquals(0L, tsInDay);
+		String str = DateTimeUtil.millisecondsToHumanDateWithSeconds(tsInMS);
+		System.out.println(str);
+	}
+	
+	@Test
+	public void testRound5(){
+		long tsInMS = 8*3600*1000L;
+		long tsInDay = DateTimeUtil.roundDown(Calendar.DATE, tsInMS);
+		Assert.assertEquals(0L, tsInDay);
+		String str = DateTimeUtil.millisecondsToHumanDateWithSeconds(tsInDay);
+		System.out.println(str);
+	}
+	
+	@Test
+	public void testDayOfWeek() {
+		GregorianCalendar cal = new GregorianCalendar();
+		long tsInMS = 0L;
+		cal.setTimeInMillis(tsInMS);
+		//cal.setTimeInMillis(System.currentTimeMillis());
+		System.out.println(cal.get(Calendar.DAY_OF_WEEK));
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestEagleBase64Wrapper.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestEagleBase64Wrapper.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestEagleBase64Wrapper.java
new file mode 100755
index 0000000..384c274
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/TestEagleBase64Wrapper.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.eagle.common;
+
+import org.junit.Test;
+
+public class TestEagleBase64Wrapper {
+	@Test
+	public void test(){
+		byte[] b = EagleBase64Wrapper.decode("BgVz-6vkdM8AAbGAf__-trtos5aqSGPod4Q1GwA268vF50iNBgmpmAxLXKkGbxkREWcmOzT3YIx3hDUb");
+		byte[] c = EagleBase64Wrapper.decode("BgVz-6vkdM8AAbGAf__-trtos5aqSGPod4Q1G6pLeJcAATVuADbry8XnSI0GCamYDEtcqQZvGRERZyY7NPdgjHeENRs");
+		
+		System.out.println(new String(b));
+		System.out.println(new String(c));
+		
+		int hash = "jobType".hashCode();
+		byte b1 = (byte)((hash >> 24) & 0xff);
+		byte b2 = (byte)(((hash << 8) >> 24) & 0xff);
+		byte b3 = (byte)(((hash << 16) >> 24) & 0xff);
+		byte b4 = (byte)(((hash << 24) >> 24) & 0xff);
+		
+		System.out.println(b1 + "," + b2 + "," + b3 + "," + b4);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/config/TestEagleConfig.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/config/TestEagleConfig.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/config/TestEagleConfig.java
new file mode 100644
index 0000000..892db0c
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/config/TestEagleConfig.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
+ *
+ *    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.eagle.common.config;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @since 9/22/15
+ */
+public class TestEagleConfig {
+    private EagleConfig config;
+
+    @Before
+    public void setUp(){
+        System.setProperty("config.resource","test-service-config.conf");
+        config = EagleConfigFactory.load();
+    }
+
+    @Test
+    public void testInit(){
+        Assert.assertEquals("test",config.getEnv());
+        Assert.assertEquals("localhost",config.getServiceHost());
+        Assert.assertEquals(9090,config.getServicePort());
+        Assert.assertEquals("hbase",config.getStorageType());
+        Assert.assertEquals(false,config.isCoprocessorEnabled());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/metric/TestAlertContext.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/metric/TestAlertContext.java b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/metric/TestAlertContext.java
new file mode 100644
index 0000000..894961b
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/java/org/apache/eagle/common/metric/TestAlertContext.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.eagle.common.metric;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+/**
+ * since 1/20/16.
+ */
+public class TestAlertContext {
+    @Test
+    public void test(){
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put("key1", "value1");
+        map.put("key2", "value2");
+        AlertContext context = new AlertContext();
+        context.addAll(map);
+        String json = context.toJsonString();
+        System.out.println(json);
+        Assert.assertEquals(map, AlertContext.fromJsonString(json).getProperties());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/resources/footer.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/resources/footer.vm b/eagle-core/eagle-common/src/test/resources/footer.vm
new file mode 100755
index 0000000..8df4b06
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/resources/footer.vm
@@ -0,0 +1,25 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+    </td>
+  </tr>
+  </tr>
+</table>
+<!-- End of wrapper table -->
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/resources/header.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/resources/header.vm b/eagle-core/eagle-common/src/test/resources/header.vm
new file mode 100755
index 0000000..8bd4388
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/resources/header.vm
@@ -0,0 +1,299 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+  <title></title>
+  <style type="text/css">
+    /* Based on The MailChimp Reset INLINE: Yes. */
+    /* Client-specific Styles */
+    .outlook a {
+      color: #091D42;
+      padding: 0;
+      text-decoration: none;
+    }
+
+    /* Force Outlook to provide a "view in browser" menu link. */
+    body {
+      width: 100% !important;
+      -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+      margin: 0;
+      padding: 0;
+    }
+
+    /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
+    .ExternalClass {
+      width: 100%;
+    }
+
+    /* Force Hotmail to display emails at full width */
+    .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
+      line-height: 100%;
+    }
+
+    /* Forces Hotmail to display normal line spacing.  More on that: http://www.emailonacid.com/forum/viewthread/43/ */
+    #backgroundTable {
+      margin: 0;
+      padding: 0;
+      width: 100% !important;
+      line-height: 100% !important;
+    }
+
+    /* End reset */
+
+    /* Some sensible defaults for images
+    Bring inline: Yes. */
+    img {
+      outline: none;
+      text-decoration: none;
+      -ms-interpolation-mode: bicubic;
+    }
+
+    a img {
+      border: none;
+    }
+
+    .image_fix {
+      display: block;
+    }
+
+    /* Yahoo paragraph fix
+    Bring inline: Yes. */
+    p {
+      margin: 1em 0;
+    }
+
+    /* Hotmail header color reset
+    Bring inline: Yes. */
+    h1, h2, h3, h4, h5, h6 {
+      color: black !important;
+    }
+
+    h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+      color: blue !important;
+    }
+
+    h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {
+      color: red !important; /* Preferably not the same color as the normal header link color.  There is limited support for psuedo classes in email clients, this was added just for good measure. */
+    }
+
+    h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
+      color: purple !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
+    }
+
+    /* Outlook 07, 10 Padding issue fix
+    Bring inline: No.*/
+    table td {
+      border-collapse: collapse;
+    }
+
+    table{
+        width: 100%;
+    }
+
+    /* Remove spacing around Outlook 07, 10 tables
+    Bring inline: Yes */
+    table {
+      border-collapse: collapse;
+      mso-table-lspace: 0pt;
+      mso-table-rspace: 0pt;
+    }
+
+    /* Styling your links has become much simpler with the new Yahoo.  In fact, it falls in line with the main credo of styling in email and make sure to bring your styles inline.  Your link colors will be uniform across clients when brought inline.
+    Bring inline: Yes. */
+    a {
+      color: orange;
+    }
+
+    /***************************************************
+    ****************************************************
+    MOBILE TARGETING
+    ****************************************************
+    ***************************************************/
+    @media only screen and (max-device-width: 480px) {
+      /* Part one of controlling phone number linking for mobile. */
+      a[href^="tel"], a[href^="sms"] {
+        text-decoration: none;
+        color: blue; /* or whatever your want */
+        pointer-events: none;
+        cursor: default;
+      }
+
+      .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
+        text-decoration: default;
+        color: orange !important;
+        pointer-events: auto;
+        cursor: default;
+      }
+
+    }
+
+    /* More Specific Targeting */
+
+    @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
+      /* You guessed it, ipad (tablets, smaller screens, etc) */
+      /* repeating for the ipad */
+      a[href^="tel"], a[href^="sms"] {
+        text-decoration: none;
+        color: blue; /* or whatever your want */
+        pointer-events: none;
+        cursor: default;
+      }
+
+      .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
+        text-decoration: default;
+        color: orange !important;
+        pointer-events: auto;
+        cursor: default;
+      }
+    }
+
+    @media only screen and (-webkit-min-device-pixel-ratio: 2) {
+      /* Put your iPhone 4g styles in here */
+    }
+
+    /* Android targeting */
+    @media only screen and (-webkit-device-pixel-ratio: .75) {
+      /* Put CSS for low density (ldpi) Android layouts in here */
+    }
+
+    @media only screen and (-webkit-device-pixel-ratio: 1) {
+      /* Put CSS for medium density (mdpi) Android layouts in here */
+    }
+
+    @media only screen and (-webkit-device-pixel-ratio: 1.5) {
+      /* Put CSS for high density (hdpi) Android layouts in here */
+    }
+
+    /* end Android targeting */
+    .head{
+      font-family:Helvetica, Arial;
+      font-size:30px;
+      text-decoration:none;
+      text-align:left;
+      color:#333;
+      align:left;
+      padding: 25px 0 15px 0;
+      valign:middle;
+      font-weight: 500;
+      border-bottom: 1px solid #cccccc;
+    }
+
+    .head2{
+      font-family:Helvetica, Arial;
+      font-size:24px;
+      text-decoration:none;
+      text-align:left;
+      color:#333;
+      align:left;
+      padding: 20px 0 10px 0;
+      valign:middle;
+      font-weight: 500;
+    }
+
+    .text{
+      font-family:Helvetica, Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      color:#333;
+      align:left;
+      padding: 0.5em 0em 0.5em 0em;
+      valign:middle;
+    }
+
+    .info {
+      font-family:Helvetica, Arial;
+      font-size:16px;
+      text-decoration:none;
+      text-align:left;
+      padding-left:10px;
+      border-left: 3px solid #396A92;
+      background-color: #9AB4CB;
+      color:#091D42;
+      align:left;
+      padding: 1em 0em 1em 1em;
+      valign:middle;
+    }
+
+    .table-border{
+      border: 1px solid #ddd;
+      border-radius: 4px 4px 0 0;
+      box-shadow: none;
+    }
+
+    .table-border-th{
+      font-family:Helvetica, Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      border-top: 1px solid #cccccc;
+      border-right: 1px solid #cccccc;
+      padding: 8px;
+    }
+    .table-border-td{
+      font-family:Helvetica, Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      border-top: 1px solid #cccccc;
+      border-right: 1px solid #cccccc;
+      padding: 8px;
+      word-break: break-all;
+    }
+
+    .foot{
+      font-family:Arial;
+      font-size:14px;
+      text-decoration:none;
+      text-align:left;
+      padding: 0.5em 0em 0.5em 0.5em;
+      border-top: 1px solid #cccccc;
+      color:#777;
+    }
+
+  </style>
+
+  <!-- Targeting Windows Mobile -->
+  <!--[if IEMobile 7]>
+  <style type="text/css">
+
+  </style>
+  <![endif]-->
+
+  <!-- ***********************************************
+  ****************************************************
+  END MOBILE TARGETING
+  ****************************************************
+  ************************************************ -->
+
+  <!--[if gte mso 9]>
+  <style>
+    /* Target Outlook 2007 and 2010 */
+  </style>
+  <![endif]-->
+</head>
+<body>
+<!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
+<table cellpadding="0" cellspacing="0" border="0" id="backgroundTable">
+  <tr>
+    <td valign="top" style="padding-left: 10px">

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/resources/log4j.properties b/eagle-core/eagle-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..5a5709e
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/resources/log4j.properties
@@ -0,0 +1,34 @@
+# 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=INFO, DRFA, stdout
+eagle.log.dir=./logs
+eagle.log.file=eagle.log
+
+# 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
+
+# Daily Rolling File Appender
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.File=${eagle.log.dir}/${eagle.log.file}
+log4j.appender.DRFA.DatePattern=.yyyy-MM-dd
+# 30-day backup
+#log4j.appender.DRFA.MaxBackupIndex=30
+log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %p [%t] %c{2}[%L]: %m%n

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/resources/templates/tec_alert.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/resources/templates/tec_alert.vm b/eagle-core/eagle-common/src/test/resources/templates/tec_alert.vm
new file mode 100755
index 0000000..2219ebf
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/resources/templates/tec_alert.vm
@@ -0,0 +1,119 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+#parse("header.vm")
+<table cellpadding="0" cellspacing="0" border="0" align="left" width="800" style="">
+  <tr>
+    <td class="head" width="800">
+      <span style="color: #091D42">Eagle Service <small style="color: #999999;">$startTime ~ $endTime</small></span>
+    </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Statistics
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+        <tr>
+          <th class="table-border-th" style="width: 25%">type</th>
+          <th class="table-border-th" style="width: 25%">count</th>
+          <th class="table-border-th" style="width: 25%">type</th>
+          <th class="table-border-th" style="width: 25%">count</th>
+        </tr>
+        </thead>
+        <tbody>
+        #foreach($key in $statistics.keySet())
+          #if( $foreach.count % 2 == 1 )
+          <tr>
+          #end
+            <td class="table-border-td">$key</td>
+            <td class="table-border-td">$statistics.get($key)</td>
+          #if( $foreach.count % 2 == 0)
+          </tr>
+          #end
+          #if( $foreach.count % 2 == 1 && !$foreach.hasNext )
+          	<td class="table-border-td"></td>
+            <td class="table-border-td"></td>
+          </tr>
+          #end
+       	#end
+        </tbody>
+      </table>
+    </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Detail Info
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td class="info" width="800">
+      <span>
+          These are the alerts that we can not find its hostname.
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+          <tr>
+            <th class="table-border-th">hostname</th>
+            <th class="table-border-th">date_reception</th>
+            <th class="table-border-th">type</th>
+            <th class="table-border-th">origin</th>
+            <th class="table-border-th">msg</th>
+          </tr>
+        </thead>
+        <tbody>
+        #foreach($entry in $noHostnameItems)
+          <tr>
+            <td class="table-border-td">$entry.hostname</td>
+            <td class="table-border-td">$entry.date_reception</td>
+            <td class="table-border-td">$entry.type</td>
+            <td class="table-border-td">$entry.origin</td>
+            <td class="table-border-td">$entry.msg</td>
+          </tr>
+       	#end
+        </tbody>
+      </table>
+    </td>
+  </tr>
+    <tr>
+        <td>
+            <img src="cid:$cid.get("chart.png")"/>
+        </td>
+    </tr>
+  <tr>
+    <td class="foot" width="800">
+      <span class="outlook">
+         Apache Eagle <a href="http://<Eagle-Host>:9090/eagle-web/ui/eagle.html"
+                  target ="_blank" title="Hadoop Eagle">Apache Eagle</a>
+      </span>
+    </td>
+  </tr>
+</table>
+
+#parse("footer.vm")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/resources/templates/test_anomaly_alert.vm
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/resources/templates/test_anomaly_alert.vm b/eagle-core/eagle-common/src/test/resources/templates/test_anomaly_alert.vm
new file mode 100644
index 0000000..da1f8d0
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/resources/templates/test_anomaly_alert.vm
@@ -0,0 +1,137 @@
+#*
+ * 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.
+ *
+ * @version 0.3.0
+ *#
+#parse("header.vm")
+<table cellpadding="0" cellspacing="0" border="0" align="left" width="800" style="">
+  <tr>
+    <td class="head" width="800">
+      <span style="color: #091D42">Eagle Job Anomaly Host Detection <small style="color: #999999;"><br>$startTime ~ $endTime</small></span>
+    </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Statistics
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+        <tr>
+          <th class="table-border-th" style="width: 25%">Cluster</th>
+          <th class="table-border-th" style="width: 25%">Datacener</th>
+          <th class="table-border-th" style="width: 25%">Count</th>
+        </tr>
+        </thead>
+        <tbody>
+          <tr>
+              <td class="table-border-td">$cluster</td>
+              <td class="table-border-td">$datacenter</td>
+              <td class="table-border-td">$count</td>
+          </tr>
+        </tbody>
+      </table>
+    </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Anomaly detector configurations
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+        <tr>
+          <th class="table-border-th" style="width: 25%">Parameter Name</th>
+          <th class="table-border-th" style="width: 25%">Value</th>
+          <th class="table-border-th" style="width: 25%">Description</th>
+        </tr>
+        </thead>
+        <tbody>
+        #foreach($key in $configMap.keySet())
+        <tr>
+            <td class="table-border-td">$key</td>
+            <td class="table-border-td">$configMap.get($key)</td>
+            <td class="table-border-td">$configDescMap.get($key)</td>
+        </tr>
+       	#end
+         </tbody>
+        </table>
+      </td>
+  </tr>
+  <tr>
+    <td class="head2" width="800">
+      <span>
+        Detail Information
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td class="info" width="800">
+      <span>
+          These are the anomaly hosts detected by Hadoop Eagle:
+      </span>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top" style="padding: 0.5em 0em 0.5em 0em;">
+      <table cellpadding="0" cellspacing="0" border="0" align="left" width="800" class="table-border">
+        <thead>
+          <tr>
+            <th class="table-border-th">Cluster</th>
+            <th class="table-border-th">Datacenter</th>
+            <th class="table-border-th">Hostname</th>
+            <th class="table-border-th">Description</th>
+            <th class="table-border-th">Job Types</th>
+            <th class="table-border-th">Errors</th>
+          </tr>
+        </thead>
+        <tbody>
+        #foreach($key in $anomalyHostMap.keySet())
+        <tr>
+            <td class="table-border-td">$cluster</td>
+            <td class="table-border-td">$datacenter</td>
+            <td class="table-border-td"><a href="http://123.dc1.xyz.com:9090/eagle-web/ui/AnomalyDetection.html?cluster=$cluster&datacenter=$datacenter&intervalmin=1&startTime=$startTime&endTime=$endTime&top=20">$key</a></td>
+            <td class="table-border-td">$anomalyHostMap.get($key)</td>
+            <td class="table-border-td">$jobNameMap.get($key)</td>
+            <td class="table-border-td"><a href="http://hadoop-eagle.vip.dc1.xyz.com/eagle-service/rest/list?query=TaskExecutionService[@cluster=%22$cluster%22%20AND%20@datacenter=%22$datacenter%22%20AND%20@hostname=%22$key%22%20AND%20@taskStatus=%22FAILED%22]{@error,@jobID,@taskID,@normJobName}&pageSize=1000&startTime=$startTime&endTime=$endTime">$errorMap.get($key)</a></td>
+        </tr>
+       	#end
+        </tbody>
+      </table>
+    </td>
+  </tr>
+  <br>
+  <tr>
+    <td class="foot" width="800">
+      <span class="outlook">
+         Apache Eagle <a href="http://hadoop-eagle.vip.dc1.xyz.com"
+                  target ="_blank" title="Hadoop Eagle">Apache Eagle</a>
+      </span>
+    </td>
+  </tr>
+</table>
+<br>
+<br>
+
+#parse("footer.vm")
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-common/src/test/resources/test-service-config.conf
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-common/src/test/resources/test-service-config.conf b/eagle-core/eagle-common/src/test/resources/test-service-config.conf
new file mode 100644
index 0000000..848391d
--- /dev/null
+++ b/eagle-core/eagle-common/src/test/resources/test-service-config.conf
@@ -0,0 +1,30 @@
+# 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.
+
+eagle {
+	timezone = "UTC"
+
+	service {
+		env = "test"
+		host = "localhost"
+		port = 9090
+		storage-type = "hbase"
+		table-name-prefixed-with-environment = true
+		hbase-zookeeper-quorum = "localhost"
+		hbase-zookeeper-property-clientPort = 2181
+		zookeeper-znode-parent = "/hbase-unsecure"
+		coprocessor-enabled = false
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-metadata/eagle-metadata-base/pom.xml
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/pom.xml b/eagle-core/eagle-metadata/eagle-metadata-base/pom.xml
new file mode 100644
index 0000000..5e74a41
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~    http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>eagle-metadata</artifactId>
+        <groupId>org.apache.eagle</groupId>
+        <version>0.5.0-incubating-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>eagle-metadata-base</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.typesafe</groupId>
+            <artifactId>config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.eagle</groupId>
+            <artifactId>alert-metadata</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject.extensions</groupId>
+            <artifactId>guice-servlet</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDesc.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDesc.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDesc.java
new file mode 100644
index 0000000..46b5dbe
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDesc.java
@@ -0,0 +1,141 @@
+/*
+ * 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.metadata.model;
+
+import org.apache.eagle.alert.engine.coordinator.StreamDefinition;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Static metadata provided by installed apps
+ */
+public class ApplicationDesc implements Serializable {
+    private String type;
+    private String name;
+    private String version;
+    private String description;
+    private Class<?> appClass;
+    private String jarPath;
+    private String viewPath;
+    private Class<?> providerClass;
+    private Configuration configuration;
+    private List<StreamDefinition> streams;
+    private Class<?> sinkClass;
+    private ApplicationDocs docs;
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getViewPath() {
+        return viewPath;
+    }
+
+    public String getJarPath() {
+        return jarPath;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+    public void setJarPath(String jarPath) {
+        this.jarPath = jarPath;
+    }
+
+    public void setViewPath(String viewPath) {
+        this.viewPath = viewPath;
+    }
+
+    public Class<?> getAppClass() {
+        return appClass;
+    }
+
+    public void setAppClass(Class<?> appClass) {
+        this.appClass = appClass;
+    }
+
+    public Class<?> getProviderClass() {
+        return providerClass;
+    }
+
+    public void setProviderClass(Class<?> providerClass) {
+        this.providerClass = providerClass;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ApplicationDesc [type=%s, name=%s, version=%s, appClass=%s, viewPath=%s, jarPath=%s, providerClass=%s, configuration= %s properties, description=%s",
+                    getType(),getName(),getVersion(),getAppClass(), getViewPath(),getJarPath(),getProviderClass(), getConfiguration() == null ? 0: getConfiguration().size(),getDescription());
+    }
+
+    public void setConfiguration(Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    public Class<?> getSinkClass() {
+        return sinkClass;
+    }
+
+    public void setSinkClass(Class<?> sinkClass) {
+        this.sinkClass = sinkClass;
+    }
+
+    public List<StreamDefinition> getStreams() {
+        return streams;
+    }
+
+    public void setStreams(List<StreamDefinition> streams) {
+        this.streams = streams;
+    }
+
+    public ApplicationDocs getDocs() {
+        return docs;
+    }
+
+    public void setDocs(ApplicationDocs docs) {
+        this.docs = docs;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/e21b073f/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDocs.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDocs.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDocs.java
new file mode 100644
index 0000000..f83f377
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/ApplicationDocs.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
+ * <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.metadata.model;
+
+public class ApplicationDocs {
+    private String install;
+    private String uninstall;
+
+    public String getInstall() {
+        return install;
+    }
+
+    public void setInstall(String install) {
+        this.install = install;
+    }
+
+    public String getUninstall() {
+        return uninstall;
+    }
+
+    public void setUninstall(String uninstall) {
+        this.uninstall = uninstall;
+    }
+}