This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.hc.support-1.0.4 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-hc-support.git
commit 730094505f2d9e4791a9bf76fe2627a0155424fb Author: Bertrand Delacretaz <[email protected]> AuthorDate: Mon Aug 12 16:01:50 2013 +0000 SLING-2987 - split into several bundles git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/healthcheck/healthchecks@1513179 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 154 ++++++++++ .../impl/DefaultLoginsHealthCheck.java | 129 +++++++++ .../hc/healthchecks/impl/HealthCheckInfo.java | 42 +++ .../hc/healthchecks/impl/InternalRequest.java | 319 +++++++++++++++++++++ .../hc/healthchecks/impl/InternalResponse.java | 185 ++++++++++++ .../healthchecks/impl/JmxAttributeHealthCheck.java | 115 ++++++++ .../hc/healthchecks/impl/JmxScriptBinding.java | 62 ++++ .../hc/healthchecks/impl/OsgiScriptBinding.java | 73 +++++ .../healthchecks/impl/ScriptableHealthCheck.java | 131 +++++++++ .../impl/SlingRequestStatusHealthCheck.java | 153 ++++++++++ src/test/java/org/apache/sling/hc/healthcheck | 44 +++ .../healthchecks/DefaultLoginsHealthCheckTest.java | 77 +++++ .../healthchecks/JmxAttributeHealthCheckTest.java | 57 ++++ .../hc/healthchecks/JmxScriptBindingTest.java | 41 +++ .../hc/healthchecks/OsgiScriptBindingTest.java | 67 +++++ .../hc/healthchecks/ScriptableHealthCheckTest.java | 94 ++++++ 16 files changed, 1743 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b146639 --- /dev/null +++ b/pom.xml @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>16</version> + <relativePath>16</relativePath> + </parent> + + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.hc.healthchecks</artifactId> + <packaging>bundle</packaging> + <version>0.0.2-SNAPSHOT</version> + + <name>Sling Health Check Module - Health Check Services</name> + <inceptionYear>2013</inceptionYear> + + <description> + Default Sling Health Check Services + </description> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Private-Package> + org.apache.sling.hc.healthchecks.* + </Private-Package> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <configuration> + <signature> + <groupId>org.codehaus.mojo.signature</groupId> + <artifactId>java16</artifactId> + <version>1.0</version> + </signature> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.hc.api</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.api</artifactId> + <version>2.1.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.jcr.api</artifactId> + <version>2.0.4</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.engine</artifactId> + <version>2.2.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.6.2</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>javax.jcr</groupId> + <artifactId>jcr</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>1.6.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.9.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>rhino</groupId> + <artifactId>js</artifactId> + <version>1.6R6</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/DefaultLoginsHealthCheck.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/DefaultLoginsHealthCheck.java new file mode 100644 index 0000000..357c5c1 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/DefaultLoginsHealthCheck.java @@ -0,0 +1,129 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.jcr.Credentials; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.hc.api.Constants; +import org.apache.sling.hc.api.HealthCheck; +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLogEntry; +import org.apache.sling.jcr.api.SlingRepository; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** {@link HealthCheck} that checks that Sling default logins fail. + * Used to verify that those logins are disabled on production systems */ +@Component( + name="org.apache.sling.hc.DefaultLoginsHealthCheck", + configurationFactory=true, + policy=ConfigurationPolicy.REQUIRE, + metatype=true) +@Service +public class DefaultLoginsHealthCheck implements HealthCheck { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private Map<String, String> info; + + @Property(cardinality=500) + public static final String PROP_LOGINS = "logins"; + private List<String> logins; + + @Property(cardinality=50) + public static final String PROP_TAGS = Constants.HC_TAGS; + + @Property + public static final String PROP_NAME = Constants.HC_NAME; + + @Property + public static final String PROP_MBEAN_NAME = Constants.HC_MBEAN_NAME; + + @Reference + private SlingRepository repository; + + @Activate + public void activate(ComponentContext ctx) { + info = new HealthCheckInfo(ctx.getProperties()); + logins = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get(PROP_LOGINS), new String[] {})); + log.info("Activated, logins={}", logins); + } + + @Override + public Result execute() { + final Result result = new Result(log); + int checked=0; + int failures=0; + + for(String login : logins) { + final String [] parts = login.split(":"); + if(parts.length != 2) { + result.log(ResultLogEntry.LT_WARN, "Expected login in the form username:password, got " + login); + continue; + } + checked++; + final String username = parts[0].trim(); + final String password = parts[1].trim(); + final Credentials creds = new SimpleCredentials(username, password.toCharArray()); + Session s = null; + try { + s = repository.login(creds); + if(s != null) { + failures++; + result.log(ResultLogEntry.LT_WARN_SECURITY, "Login as [" + username + "] succeeded, was expecting it to fail"); + } else { + result.log(ResultLogEntry.LT_DEBUG, "Login as [" + username + "] didn't throw an Exception but returned null Session"); + } + } catch(RepositoryException re) { + result.log(ResultLogEntry.LT_DEBUG, "Login as [" + username + "] failed, as expected"); + } finally { + if(s != null) { + s.logout(); + } + } + } + + if(checked==0) { + result.log(ResultLogEntry.LT_WARN, "Did not check any logins, configured logins=" + logins); + } else if(failures != 0){ + result.log(ResultLogEntry.LT_WARN_SECURITY, "Checked " + checked + " logins, " + failures + " tests failed"); + } else { + result.log(ResultLogEntry.LT_DEBUG, "Checked " + checked + " logins, all tests successful"); + } + return result; + } + + @Override + public Map<String, String> getInfo() { + return info; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/HealthCheckInfo.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/HealthCheckInfo.java new file mode 100644 index 0000000..ac3dc61 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/HealthCheckInfo.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 SF 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.sling.hc.healthchecks.impl; + +import java.util.Arrays; +import java.util.Dictionary; +import java.util.HashMap; + +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.hc.api.Constants; + +// TODO move to services bundle + +/** Utility that copies useful HealthCheck service + * properties to a Map that can be used as the Result's + * info(). Copies all service properties that have names + * that start with the {#Constants.HC_PROP_PREFIX} */ +public class HealthCheckInfo extends HashMap<String, String> { + private static final long serialVersionUID = 8661195387931574705L; + + public HealthCheckInfo(Dictionary<?, ?> serviceProperties) { + put(Constants.HC_NAME, PropertiesUtil.toString(serviceProperties.get(Constants.HC_NAME), "")); + put(Constants.HC_MBEAN_NAME, PropertiesUtil.toString(serviceProperties.get(Constants.HC_MBEAN_NAME), "")); + put(Constants.HC_TAGS, + Arrays.asList(PropertiesUtil.toStringArray(serviceProperties.get(Constants.HC_TAGS), new String[] {})).toString()); + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/InternalRequest.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/InternalRequest.java new file mode 100644 index 0000000..570e57c --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/InternalRequest.java @@ -0,0 +1,319 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Vector; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletInputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +public class InternalRequest implements HttpServletRequest { + + private final String path; + private final Map<String, Object> attributes = new HashMap<String, Object>(); + + public InternalRequest(String path) { + this.path = path; + } + + @Override + public Object getAttribute(String key) { + return attributes.get(key); + } + + @Override + public Enumeration<?> getAttributeNames() { + return new Vector<String>(attributes.keySet()).elements(); + } + + @Override + public String getCharacterEncoding() { + return "UTF-8"; + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return new ServletInputStream() { + @Override + public int read() throws IOException { + return 0; + } + }; + } + + @Override + public String getLocalAddr() { + return "127.0.0.1"; + } + + @Override + public String getLocalName() { + return "localhost"; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public Locale getLocale() { + return Locale.getDefault(); + } + + @Override + public Enumeration<?> getLocales() { + return new Vector<Locale>().elements(); + } + + @Override + public String getParameter(String arg0) { + return null; + } + + @Override + public Map<?,?> getParameterMap() { + return new HashMap<String, Object>(); + } + + @Override + public Enumeration<?> getParameterNames() { + return new Vector<String>().elements(); + } + + @Override + public String[] getParameterValues(String arg0) { + return null; + } + + @Override + public String getProtocol() { + return "http"; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new StringReader("")); + } + + @Override + public String getRealPath(String arg0) { + return path; + } + + @Override + public String getRemoteAddr() { + return "127.0.0.1"; + } + + @Override + public String getRemoteHost() { + return "localhost"; + } + + @Override + public int getRemotePort() { + return 1234; + } + + @Override + public RequestDispatcher getRequestDispatcher(String arg0) { + return null; + } + + @Override + public String getScheme() { + return "http"; + } + + @Override + public String getServerName() { + return "localhost"; + } + + @Override + public int getServerPort() { + return 80; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public void removeAttribute(String arg0) { + } + + @Override + public void setAttribute(String key, Object value) { + attributes.put(key, value); + } + + @Override + public void setCharacterEncoding(String arg0) + throws UnsupportedEncodingException { + } + + @Override + public String getAuthType() { + return null; + } + + @Override + public String getContextPath() { + return ""; + } + + @Override + public Cookie[] getCookies() { + return null; + } + + @Override + public long getDateHeader(String arg0) { + return 0; + } + + @Override + public String getHeader(String arg0) { + return null; + } + + @Override + public Enumeration<?> getHeaderNames() { + return new Vector<String>().elements(); + } + + @Override + public Enumeration<?> getHeaders(String arg0) { + return new Vector<String>().elements(); + } + + @Override + public int getIntHeader(String arg0) { + return 0; + } + + @Override + public String getMethod() { + return "GET"; + } + + @Override + public String getPathInfo() { + return path; + } + + @Override + public String getPathTranslated() { + return path; + } + + @Override + public String getQueryString() { + return ""; + } + + @Override + public String getRemoteUser() { + return "remoteuser"; + } + + @Override + public String getRequestURI() { + return "http://localhost" + path; + } + + @Override + public StringBuffer getRequestURL() { + return new StringBuffer(getRequestURI()); + } + + @Override + public String getRequestedSessionId() { + return ""; + } + + @Override + public String getServletPath() { + return ""; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public HttpSession getSession(boolean arg0) { + return null; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isUserInRole(String arg0) { + return false; + } +} diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/InternalResponse.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/InternalResponse.java new file mode 100644 index 0000000..ead7da5 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/InternalResponse.java @@ -0,0 +1,185 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Locale; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +public class InternalResponse implements HttpServletResponse { + + private int status = 200; + + private static class DevNullOutputStream extends ServletOutputStream { + @Override + public void write(int b) { + } + } + + @Override + public void flushBuffer() throws IOException { + } + + @Override + public int getBufferSize() { + return 0; + } + + @Override + public String getCharacterEncoding() { + return "UTF-8"; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public Locale getLocale() { + return Locale.getDefault(); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return new DevNullOutputStream(); + } + + @Override + public PrintWriter getWriter() throws IOException { + return new PrintWriter(new DevNullOutputStream()); + } + + @Override + public boolean isCommitted() { + return false; + } + + @Override + public void reset() { + } + + @Override + public void resetBuffer() { + } + + @Override + public void setBufferSize(int arg0) { + } + + @Override + public void setCharacterEncoding(String arg0) { + } + + @Override + public void setContentLength(int arg0) { + } + + @Override + public void setContentType(String arg0) { + } + + @Override + public void setLocale(Locale arg0) { + } + + @Override + public void addCookie(Cookie arg0) { + } + + @Override + public void addDateHeader(String arg0, long arg1) { + } + + @Override + public void addHeader(String arg0, String arg1) { + } + + @Override + public void addIntHeader(String arg0, int arg1) { + } + + @Override + public boolean containsHeader(String arg0) { + return false; + } + + @Override + public String encodeRedirectURL(String url) { + return url; + } + + @Override + public String encodeRedirectUrl(String url) { + return url; + } + + @Override + public String encodeURL(String url) { + return url; + } + + @Override + public String encodeUrl(String url) { + return url; + } + + @Override + public void sendError(int s, String arg1) throws IOException { + status = s; + } + + @Override + public void sendError(int s) throws IOException { + status = s; + } + + @Override + public void sendRedirect(String arg0) throws IOException { + } + + @Override + public void setDateHeader(String arg0, long arg1) { + } + + @Override + public void setHeader(String arg0, String arg1) { + } + + @Override + public void setIntHeader(String arg0, int arg1) { + } + + @Override + public void setStatus(int s, String arg1) { + status = s; + } + + @Override + public void setStatus(int s) { + status = s; + } + + public int getStatus() { + return status; + } +} diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/JmxAttributeHealthCheck.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/JmxAttributeHealthCheck.java new file mode 100644 index 0000000..a536fbd --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/JmxAttributeHealthCheck.java @@ -0,0 +1,115 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import java.lang.management.ManagementFactory; +import java.util.Map; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.hc.api.Constants; +import org.apache.sling.hc.api.HealthCheck; +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLogEntry; +import org.apache.sling.hc.util.SimpleConstraintChecker; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.helpers.MessageFormatter; + +/** {@link HealthCheck} that checks a single JMX attribute */ +@Component( + name="org.apache.sling.hc.JmxAttributeHealthCheck", + configurationFactory=true, + policy=ConfigurationPolicy.REQUIRE, + metatype=true) +@Service +public class JmxAttributeHealthCheck implements HealthCheck { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private Map<String, String> info; + private String mbeanName; + private String attributeName; + private String constraint; + + @Property + public static final String PROP_OBJECT_NAME = "mbean.name"; + + @Property + public static final String PROP_ATTRIBUTE_NAME = "attribute.name"; + + @Property + public static final String PROP_CONSTRAINT = "attribute.value.constraint"; + + @Property(cardinality=50) + public static final String PROP_TAGS = Constants.HC_TAGS; + + @Property + public static final String PROP_NAME = Constants.HC_NAME; + + @Property + public static final String PROP_MBEAN_NAME = Constants.HC_MBEAN_NAME; + + @Activate + public void activate(ComponentContext ctx) { + info = new HealthCheckInfo(ctx.getProperties()); + mbeanName = PropertiesUtil.toString(ctx.getProperties().get(PROP_OBJECT_NAME), ""); + attributeName = PropertiesUtil.toString(ctx.getProperties().get(PROP_ATTRIBUTE_NAME), ""); + constraint = PropertiesUtil.toString(ctx.getProperties().get(PROP_CONSTRAINT), ""); + + info.put(PROP_OBJECT_NAME, mbeanName); + info.put(PROP_ATTRIBUTE_NAME, attributeName); + info.put(PROP_CONSTRAINT, constraint); + + log.info("Activated with HealthCheck name={}, objectName={}, attribute={}, constraint={}", + new Object[] { info.get(Constants.HC_NAME), mbeanName, attributeName, constraint }); + } + + @Override + public Result execute() { + final Result result = new Result(log); + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format("Checking {} / {} with constraint {}", + new Object[] { mbeanName, attributeName, constraint }).getMessage()); + try { + final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); + final ObjectName objectName = new ObjectName(mbeanName); + if(jmxServer.queryNames(objectName, null).size() == 0) { + log.error("MBean not found: {}", objectName); + } + final Object value = jmxServer.getAttribute(objectName, attributeName); + log.debug("{} {} returns {}", new Object[] { mbeanName, attributeName, value }); + new SimpleConstraintChecker().check(value, constraint, result); + } catch(Exception e) { + log.warn(e.toString(), e); + } + return result; + } + + @Override + public Map<String, String> getInfo() { + return info; + } +} diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/JmxScriptBinding.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/JmxScriptBinding.java new file mode 100644 index 0000000..bbfc95e --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/JmxScriptBinding.java @@ -0,0 +1,62 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import java.lang.management.ManagementFactory; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLogEntry; +import org.slf4j.helpers.MessageFormatter; + +/** The JmxBinding is meant to be bound as "jmx" global variables + * in scripted rules, to allow for writing scripted expressions + * like jmx.attribute("java.lang:type=ClassLoading", "LoadedClassCount") > 100 + */ +public class JmxScriptBinding { + private MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); + private final Result result; + + public JmxScriptBinding(Result result) { + this.result = result; + } + + public Object attribute(String objectNameString, String attributeName) + throws MalformedObjectNameException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException { + final ObjectName name = new ObjectName(objectNameString); + if(jmxServer.queryNames(name, null).size() == 0) { + final String msg = "JMX object name not found: [" + objectNameString + "]"; + result.log(ResultLogEntry.LT_WARN, msg); + throw new IllegalStateException(msg); + } + result.log(ResultLogEntry.LT_DEBUG, MessageFormatter.format("Got JMX Object [{}]", name).getMessage()); + final Object value = jmxServer.getAttribute(name, attributeName); + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format( + "JMX Object [{}] Attribute [{}] = [{}]", + new Object[] { name, attributeName, value }).getMessage()); + return value; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/OsgiScriptBinding.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/OsgiScriptBinding.java new file mode 100644 index 0000000..27e9f28 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/OsgiScriptBinding.java @@ -0,0 +1,73 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLogEntry; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.slf4j.helpers.MessageFormatter; + +/** The OsgiBinding is meant to be bound as an "osgi" global variable + * in scripted rules, to allow for checking some OSGi states in + * a simple way + */ +public class OsgiScriptBinding { + private final Result result; + private final BundleContext bundleContext; + + public OsgiScriptBinding(BundleContext ctx, Result result) { + this.result = result; + this.bundleContext = ctx; + } + + public int inactiveBundlesCount() { + int count = 0; + for(Bundle b : bundleContext.getBundles()) { + if(!isActive(b)) { + count++; + } + } + result.log(ResultLogEntry.LT_DEBUG, MessageFormatter.format("inactiveBundlesCount={}", count).getMessage()); + return count; + } + + private boolean isActive(Bundle b) { + boolean active = true; + if(!isFragment(b) && Bundle.ACTIVE != b.getState()) { + active = false; + result.log(ResultLogEntry.LT_INFO, + MessageFormatter.format( + "Bundle {} is not active, state={} ({})", + new Object[] { b.getSymbolicName(), b.getState(), b.getState()}).getMessage()); + } + return active; + } + + private boolean isFragment(Bundle b) { + final String header = (String) b.getHeaders().get( Constants.FRAGMENT_HOST ); + if(header!= null && header.trim().length() > 0) { + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format("{} is a fragment bundle, state won't be checked", b).getMessage()); + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/ScriptableHealthCheck.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/ScriptableHealthCheck.java new file mode 100644 index 0000000..6e5d82f --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/ScriptableHealthCheck.java @@ -0,0 +1,131 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; +import java.util.Map; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.hc.api.Constants; +import org.apache.sling.hc.api.HealthCheck; +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLogEntry; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.helpers.MessageFormatter; + +/** {@link HealthCheck} that checks a scriptable expression */ +@Component( + name="org.apache.sling.hc.ScriptableHealthCheck", + configurationFactory=true, + policy=ConfigurationPolicy.REQUIRE, + metatype=true) +@Service +public class ScriptableHealthCheck implements HealthCheck { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private Map<String, String> info; + private String expression; + private String languageExtension; + private BundleContext bundleContext; + + public static final String DEFAULT_LANGUAGE_EXTENSION = "ecma"; + + @Property + public static final String PROP_EXPRESSION = "expression"; + + @Property + public static final String PROP_LANGUAGE_EXTENSION = "language.extension"; + + @Property(cardinality=50) + public static final String PROP_TAGS = Constants.HC_TAGS; + + @Property + public static final String PROP_NAME = Constants.HC_NAME; + + @Property + public static final String PROP_MBEAN_NAME = Constants.HC_MBEAN_NAME; + + @Reference + private ScriptEngineManager scriptEngineManager; + + @Activate + public void activate(ComponentContext ctx) { + info = new HealthCheckInfo(ctx.getProperties()); + bundleContext = ctx.getBundleContext(); + expression = PropertiesUtil.toString(ctx.getProperties().get(PROP_EXPRESSION), ""); + languageExtension = PropertiesUtil.toString(ctx.getProperties().get(PROP_LANGUAGE_EXTENSION), DEFAULT_LANGUAGE_EXTENSION); + + info.put(PROP_EXPRESSION, expression); + info.put(PROP_LANGUAGE_EXTENSION, languageExtension); + + log.info("Activated, name={}, languageExtension={}, expression={}", languageExtension, expression); + } + + @Override + public Result execute() { + final Result result = new Result(log); + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format( + "Checking expression [{}], language extension=[{}]", + expression, languageExtension).getMessage()); + try { + final ScriptEngine engine = scriptEngineManager.getEngineByExtension(languageExtension); + if(engine == null) { + result.log(ResultLogEntry.LT_WARN, + MessageFormatter.format( + "No ScriptEngine available for extension {}", + languageExtension).getMessage()); + } else { + // TODO pluggable Bindings? Reuse the Sling bindings providers? + final Bindings b = engine.createBindings(); + b.put("jmx", new JmxScriptBinding(result)); + b.put("osgi", new OsgiScriptBinding(bundleContext, result)); + final Object value = engine.eval(expression, b); + if(value!=null && "true".equals(value.toString())) { + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format( + "Expression [{}] evaluates to true as expected", expression).getMessage()); + } else { + result.log(ResultLogEntry.LT_WARN, + MessageFormatter.format( + "Expression [{}] does not evaluate to true, value={}", + expression, value).getMessage()); + } + } + } catch(Exception e) { + result.log(ResultLogEntry.LT_WARN, e.toString()); + } + return result; + } + + @Override + public Map<String, String> getInfo() { + return info; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/healthchecks/impl/SlingRequestStatusHealthCheck.java b/src/main/java/org/apache/sling/hc/healthchecks/impl/SlingRequestStatusHealthCheck.java new file mode 100644 index 0000000..744561d --- /dev/null +++ b/src/main/java/org/apache/sling/hc/healthchecks/impl/SlingRequestStatusHealthCheck.java @@ -0,0 +1,153 @@ +/* + * 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 SF 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.sling.hc.healthchecks.impl; + +import java.util.Arrays; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.engine.SlingRequestProcessor; +import org.apache.sling.hc.api.Constants; +import org.apache.sling.hc.api.HealthCheck; +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLogEntry; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.helpers.MessageFormatter; + +/** {@link HealthCheck} that checks the HTTP status of Sling requests */ +@Component( + name="org.apache.sling.hc.SlingRequestStatusHealthCheck", + configurationFactory=true, + policy=ConfigurationPolicy.REQUIRE, + metatype=true) +@Service +public class SlingRequestStatusHealthCheck implements HealthCheck { + + private static final Logger log = LoggerFactory.getLogger(SlingRequestStatusHealthCheck.class); + private Map<String, String> info; + private String [] paths; + + static class PathSpec { + int status; + String path; + + PathSpec(String configuredPath) { + path = configuredPath; + status = 200; + + final String [] parts = configuredPath.split(":"); + if(parts.length == 2) { + try { + status = Integer.valueOf(parts[1].trim()); + path = parts[0].trim(); + } catch(NumberFormatException nfe) { + log.warn("NumberFormatException while parsing [{}], invalid status value?", configuredPath); + } + } + } + } + + @Property(cardinality=Integer.MAX_VALUE) + public static final String PROP_PATH = "path"; + + @Property(cardinality=50) + public static final String PROP_TAGS = Constants.HC_TAGS; + + @Property + public static final String PROP_NAME = Constants.HC_NAME; + + @Property + public static final String PROP_MBEAN_NAME = Constants.HC_MBEAN_NAME; + + @Reference + private SlingRequestProcessor requestProcessor; + + @Reference + private ResourceResolverFactory resolverFactory; + + @Activate + public void activate(ComponentContext ctx) { + info = new HealthCheckInfo(ctx.getProperties()); + paths = PropertiesUtil.toStringArray(ctx.getProperties().get(PROP_PATH), new String [] {}); + log.info("Activated, paths={}", Arrays.asList(paths)); + } + + @Override + public Result execute() { + final Result result = new Result(log); + + ResourceResolver resolver = null; + int checked = 0; + int failed = 0; + + try { + resolver = resolverFactory.getAdministrativeResourceResolver(null); + for(String p : paths) { + final PathSpec ps = new PathSpec(p); + final HttpServletRequest request = new InternalRequest(ps.path); + final InternalResponse response = new InternalResponse(); + requestProcessor.processRequest(request, response, resolver); + final int status = response.getStatus(); + if(status != ps.status) { + failed++; + result.log(ResultLogEntry.LT_WARN, + MessageFormatter.format( + "[{}] returns status {}, expected {}", new Object[] { ps.path, status, ps.status }).getMessage()); + } else { + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format( + "[{}] returns status {} as expected", ps.path, status).getMessage()); + } + checked++; + } + } catch(Exception e) { + result.log(ResultLogEntry.LT_WARN, "Exception while executing request: " + e.toString()); + } finally { + if(resolver != null) { + resolver.close(); + } + } + + if(checked == 0) { + result.log(ResultLogEntry.LT_WARN, "No paths checked, empty paths list?"); + } else { + result.log(ResultLogEntry.LT_DEBUG, + MessageFormatter.format( + "{} paths checked, {} failures", checked, failed).getMessage()); + } + + return result; + } + + @Override + public Map<String, String> getInfo() { + return info; + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/hc/healthcheck b/src/test/java/org/apache/sling/hc/healthcheck new file mode 100644 index 0000000..954f535 --- /dev/null +++ b/src/test/java/org/apache/sling/hc/healthcheck @@ -0,0 +1,44 @@ +package org.apache.sling.hc.impl.healthchecks; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class PathSpecTest { + private final String pathSpec; + private final String expectedPath; + private final int expectedStatus; + + @Parameters(name="{1}") + public static List<Object[]> data() { + final List<Object[]> result = new ArrayList<Object[]>(); + + result.add(new Object[] { "/one.html", "/one.html", 200 } ); + result.add(new Object[] { "/two.html:404", "/two.html", 404 } ); + result.add(new Object[] { "three.html : 404 ", "three.html", 404 } ); + result.add(new Object[] { "four.html:not an integer", "four.html:not an integer", 200 } ); + result.add(new Object[] { "", "", 200 } ); + + return result; + } + + public PathSpecTest(String pathSpec, String expectedPath, int expectedStatus) { + this.pathSpec = pathSpec; + this.expectedPath = expectedPath; + this.expectedStatus = expectedStatus; + } + + @Test + public void testParsing() { + final SlingRequestStatusHealthCheck.PathSpec ps = new SlingRequestStatusHealthCheck.PathSpec(pathSpec); + assertEquals(expectedPath, ps.path); + assertEquals(expectedStatus, ps.status); + } +} diff --git a/src/test/java/org/apache/sling/hc/healthchecks/DefaultLoginsHealthCheckTest.java b/src/test/java/org/apache/sling/hc/healthchecks/DefaultLoginsHealthCheckTest.java new file mode 100644 index 0000000..1c48f00 --- /dev/null +++ b/src/test/java/org/apache/sling/hc/healthchecks/DefaultLoginsHealthCheckTest.java @@ -0,0 +1,77 @@ +/* + * 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 SF 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.sling.hc.healthchecks; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; +import java.util.Arrays; + +import javax.jcr.Credentials; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.healthchecks.impl.DefaultLoginsHealthCheck; +import org.apache.sling.jcr.api.SlingRepository; +import org.junit.Test; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class DefaultLoginsHealthCheckTest { + + private Result getTestResult(String login) throws Exception { + final DefaultLoginsHealthCheck c = new DefaultLoginsHealthCheck(); + setField(c, "logins", Arrays.asList(new String[] { login })); + + final SlingRepository repo = Mockito.mock(SlingRepository.class); + setField(c, "repository", repo); + final Session s = Mockito.mock(Session.class); + Mockito.when(repo.login(Matchers.any(Credentials.class))).thenAnswer(new Answer<Session>() { + @Override + public Session answer(InvocationOnMock invocation) { + final SimpleCredentials c = (SimpleCredentials)invocation.getArguments()[0]; + if("admin".equals(c.getUserID())) { + return s; + } + return null; + } + }); + + return c.execute(); + } + + private void setField(Object o, String name, Object value) throws Exception { + final Field f = o.getClass().getDeclaredField(name); + f.setAccessible(true); + f.set(o, value); + } + + @Test + public void testHealthCheckFails() throws Exception { + assertFalse("Expecting failed check", getTestResult("admin:admin").isOk()); + } + + @Test + public void testHealthCheckSucceeds() throws Exception { + assertTrue("Expecting successful check", getTestResult("FOO:bar").isOk()); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/hc/healthchecks/JmxAttributeHealthCheckTest.java b/src/test/java/org/apache/sling/hc/healthchecks/JmxAttributeHealthCheckTest.java new file mode 100644 index 0000000..81f68fc --- /dev/null +++ b/src/test/java/org/apache/sling/hc/healthchecks/JmxAttributeHealthCheckTest.java @@ -0,0 +1,57 @@ +/* + * 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 SF 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.sling.hc.healthchecks; + +import static org.junit.Assert.assertEquals; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.healthchecks.impl.JmxAttributeHealthCheck; +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.service.component.ComponentContext; + +public class JmxAttributeHealthCheckTest { + + static void assertJmxValue(String objectName, String attributeName, String constraint, boolean expected) { + final JmxAttributeHealthCheck hc = new JmxAttributeHealthCheck(); + + final ComponentContext ctx = Mockito.mock(ComponentContext.class); + final Dictionary<String, String> props = new Hashtable<String, String>(); + props.put(JmxAttributeHealthCheck.PROP_OBJECT_NAME, objectName); + props.put(JmxAttributeHealthCheck.PROP_ATTRIBUTE_NAME, attributeName); + props.put(JmxAttributeHealthCheck.PROP_CONSTRAINT, constraint); + Mockito.when(ctx.getProperties()).thenReturn(props); + hc.activate(ctx); + + final Result r = hc.execute(); + assertEquals("Expected result " + expected, expected, r.isOk()); + } + + @Test + public void testJmxAttributeMatch() { + assertJmxValue("java.lang:type=ClassLoading", "LoadedClassCount", "> 10", true); + } + + @Test + public void testJmxAttributeNoMatch() { + assertJmxValue("java.lang:type=ClassLoading", "LoadedClassCount", "< 10", false); + } +} diff --git a/src/test/java/org/apache/sling/hc/healthchecks/JmxScriptBindingTest.java b/src/test/java/org/apache/sling/hc/healthchecks/JmxScriptBindingTest.java new file mode 100644 index 0000000..7cad43b --- /dev/null +++ b/src/test/java/org/apache/sling/hc/healthchecks/JmxScriptBindingTest.java @@ -0,0 +1,41 @@ +/* + * 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 SF 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.sling.hc.healthchecks; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.healthchecks.impl.JmxScriptBinding; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JmxScriptBindingTest { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Test + public void testJmxAttribute() throws Exception { + final Result r = new Result(logger); + final JmxScriptBinding b = new JmxScriptBinding(r); + final Object value= b.attribute("java.lang:type=ClassLoading", "LoadedClassCount"); + assertNotNull("Expecting non-null attribute value", value); + assertTrue("Expecting non-empty value", value.toString().length() > 0); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/hc/healthchecks/OsgiScriptBindingTest.java b/src/test/java/org/apache/sling/hc/healthchecks/OsgiScriptBindingTest.java new file mode 100644 index 0000000..b9dcf83 --- /dev/null +++ b/src/test/java/org/apache/sling/hc/healthchecks/OsgiScriptBindingTest.java @@ -0,0 +1,67 @@ +/* + * 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 SF 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.sling.hc.healthchecks; + +import static org.junit.Assert.assertEquals; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.healthchecks.impl.OsgiScriptBinding; +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OsgiScriptBindingTest { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private Bundle mockBundle(boolean isFragment, boolean isActive) { + final Bundle b = Mockito.mock(Bundle.class); + Mockito.when(b.getState()).thenReturn(isActive ? Bundle.ACTIVE : Bundle.RESOLVED); + + final Dictionary<String, String> headers = new Hashtable<String, String>(); + if(isFragment) { + headers.put(Constants.FRAGMENT_HOST, "FOO"); + } + Mockito.when(b.getHeaders()).thenReturn(headers); + + return b; + } + + @Test + public void testInactiveBundles() throws Exception { + final BundleContext ctx = Mockito.mock(BundleContext.class); + final Bundle [] bundles = { + mockBundle(false, true), + mockBundle(false, false), + mockBundle(false, true), + mockBundle(true, false) + }; + Mockito.when(ctx.getBundles()).thenReturn(bundles); + + final Result r = new Result(logger); + final OsgiScriptBinding b = new OsgiScriptBinding(ctx, r); + assertEquals(1, b.inactiveBundlesCount()); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/hc/healthchecks/ScriptableHealthCheckTest.java b/src/test/java/org/apache/sling/hc/healthchecks/ScriptableHealthCheckTest.java new file mode 100644 index 0000000..351b88f --- /dev/null +++ b/src/test/java/org/apache/sling/hc/healthchecks/ScriptableHealthCheckTest.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 SF 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.sling.hc.healthchecks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.lang.reflect.Field; +import java.util.Dictionary; +import java.util.Hashtable; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.healthchecks.impl.ScriptableHealthCheck; +import org.junit.Test; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.osgi.service.component.ComponentContext; + +public class ScriptableHealthCheckTest { + + private ScriptableHealthCheck hc; + private Dictionary<String, String> props; + private ComponentContext ctx; + + private void assertExpression(String expression, String languageExtension, boolean expected) throws Exception { + hc = new ScriptableHealthCheck(); + ctx = Mockito.mock(ComponentContext.class); + props = new Hashtable<String, String>(); + + final ScriptEngine rhino = new ScriptEngineManager().getEngineByExtension("js"); + assertNotNull("With the rhino jar in our classpath, we should get a js script engine", rhino); + final ScriptEngineManager manager = Mockito.mock(ScriptEngineManager.class); + Mockito.when(manager.getEngineByExtension(Matchers.same("ecma"))).thenReturn(rhino); + final Field f = hc.getClass().getDeclaredField("scriptEngineManager"); + f.setAccessible(true); + f.set(hc, manager); + + props.put(ScriptableHealthCheck.PROP_EXPRESSION, expression); + if(languageExtension != null) { + props.put(ScriptableHealthCheck.PROP_LANGUAGE_EXTENSION, languageExtension); + } + Mockito.when(ctx.getProperties()).thenReturn(props); + hc.activate(ctx); + final Result r = hc.execute(); + assertEquals("Expecting result " + expected, expected, r.isOk()); + } + + @Test + public void testSimpleExpression() throws Exception { + assertExpression("2 + 3 == 5", null, true); + } + + @Test + public void testJmxExpression() throws Exception { + assertExpression( + "jmx.attribute('java.lang:type=ClassLoading', 'LoadedClassCount') > 10" + + " && jmx.attribute('java.lang:type=Runtime', 'ManagementSpecVersion') > 1", + "ecma", true); + } + + @Test + public void testFalseExpression() throws Exception { + assertExpression("2 + 3 == 15", null, false); + } + + @Test + public void testSyntaxError() throws Exception { + assertExpression("{not [valid ok?", null, false); + } + + @Test + public void testNoEngine() throws Exception { + assertExpression("2 + 3 == 5", null, true); + assertExpression("2 + 3 == 5", "groovy", false); + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
