Commit 1d703dc2 by Johannes Stelzer

Added Hazelcast support

parent ea931248
......@@ -7,33 +7,29 @@ This is a simple admin interface for [Spring Boot](http://projects.spring.io/spr
This application provides a simple GUI to administrate Spring Boot applications in some ways. At the moment it provides the following features for every registered application.
<ul>
<li>Show name/id and version number</li>
<li>Show online status</li>
<li>Download main logfile</li>
<li>Show details, like</li>
<ul>
<li>Java system properties</li>
<li>Java environment properties</li>
<li>Memory metrics</li>
<li>Spring environment properties</li>
</ul>
</ul>
* Show name/id and version number
* Show health status
* Download main logfile
* Show details, like
* Java System- / Environment- / Spring properties
* JVM & memory metrics
* Counter & gauge Metrics
* Datasource Metrics
* Easy loggerlevel management
* Interact with JMX-Beans
* View Threaddump
#### Server application
Add the following dependency to your pom.xml.
```
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>1.0.2</version>
<version>1.0.6</version>
</dependency>
```
Create the Spring Boot Admin Server with only one single Annotation.
```
@Configuration
@EnableAutoConfiguration
......@@ -45,16 +41,17 @@ public class Application {
}
```
See also the [example project](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-admin-example) in this repository.
See also the [example project](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-admin-samples/spring-boot-admin-samples) in this repository.
#### Client applications
For configuring Hazelcast support see [spring-boot-admin-server](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-admin-server/README.md) or [hazelcast-example project](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-admin-samples/spring-boot-admin-samples-hazelcast)
Each application that want to register itself to the admin application has to include the [spring-boot-starter-admin-client](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-starter-admin-client) as dependency. This starter JAR includes some [AutoConfiguration](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-auto-configuration "Spring Boot docu") features that includes registering tasks, controller, etc.
#### Client applications
Each application that want to register itself to the admin application has to include the [spring-boot-starter-admin-client](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-starter-admin-client) as dependency. This starter JAR includes some [AutoConfiguration](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-auto-configuration "Spring Boot documentation") features that includes registering tasks, controller, etc.
```
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-starter-admin-client</artifactId>
<version>1.0.2</version>
<version>1.0.6</version>
</dependency>
```
......@@ -63,7 +60,7 @@ Inside your configuration (e.g. application.properties) you also have to define
spring.boot.admin.url=http://localhost:8080
```
For all configuration options see [spring-boot-starter-admin-client](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-starter-admin-client)
For all configuration options see [spring-boot-starter-admin-client](https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-starter-admin-client/README.md)
#### Screenshots
......
......@@ -18,6 +18,7 @@
<properties>
<spring-boot.version>1.1.7.RELEASE</spring-boot.version>
<spring.version>4.0.7.RELEASE</spring.version>
<hazelcast.version>3.3.3</hazelcast.version>
<bootstrap.version>2.3.2</bootstrap.version>
<jquery.version>1.11.0</jquery.version>
<angularjs.version>1.2.12</angularjs.version>
......@@ -32,8 +33,8 @@
</properties>
<modules>
<module>spring-boot-admin-server</module>
<module>spring-boot-admin-samples</module>
<module>spring-boot-starter-admin-client</module>
<module>spring-boot-admin-example</module>
</modules>
<organization>
<name>codecentric AG</name>
......@@ -189,6 +190,16 @@
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>${bootstrap.version}</version>
......
<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin</artifactId>
<version>1.0.6-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-samples</artifactId>
<version>1.0.6-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Boot Admin Samples</name>
<description>Spring Boot Admin Samples</description>
<url>https://github.com/codecentric/spring-boot-admin/</url>
<properties>
<main.basedir>${basedir}/..</main.basedir>
</properties>
<modules>
<module>spring-boot-admin-sample</module>
<module>spring-boot-admin-sample-hazelcast</module>
</modules>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-samples</artifactId>
<version>1.0.6-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-boot-admin-sample-hazelcast</artifactId>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>de.codecentric.boot.admin.SpringBootAdminApplication</mainClass>
<addResources>false</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
......@@ -18,22 +18,18 @@ package de.codecentric.boot.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import de.codecentric.boot.admin.config.EnableAdminServer;
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
public class TestAdminApplication {
@ImportResource({ "classpath:hazelcast-config.xml" })
public class SpringBootAdminApplication {
/**
* Starting point for application to boot.
*
* @param args
* Passed arguments.
*/
public static void main(String[] args) {
SpringApplication.run(TestAdminApplication.class, args);
SpringApplication.run(SpringBootAdminApplication.class, args);
}
}
spring.resources.cachePeriod=3600
server.port=8081
info.version=@pom.version@
info.stage=test
logging.file=/tmp/log.log
spring.application.name=@pom.artifactId@
spring.boot.admin.url=http://localhost:8081
hz.instance.name=Spring Boot Admin
\ No newline at end of file
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:hz="http://www.hazelcast.com/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.hazelcast.com/schema/spring
http://www.hazelcast.com/schema/spring/hazelcast-spring-3.3.xsd">
<hz:config id="hazelcastConfig">
<hz:instance-name>${hz.instance.name}</hz:instance-name>
<hz:properties>
<hz:property name="hazelcast.jmx">true</hz:property>
</hz:properties>
<hz:map name="spring-boot-admin-application-store" backup-count="1" eviction-policy="NONE" />
</hz:config>
</beans>
\ No newline at end of file
......@@ -3,11 +3,11 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin</artifactId>
<artifactId>spring-boot-admin-samples</artifactId>
<version>1.0.6-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-boot-admin-example</artifactId>
<artifactId>spring-boot-admin-sample</artifactId>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
......
......@@ -26,12 +26,6 @@ import de.codecentric.boot.admin.config.EnableAdminServer;
@EnableAdminServer
public class SpringBootAdminApplication {
/**
* Starting point for application to boot.
*
* @param args
* Passed arguments.
*/
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminApplication.class, args);
}
......
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<jmxConfigurator/>
</configuration>
\ No newline at end of file
spring-boot-admin-server
================================
## Easy Setup
Add the following dependency to your pom.xml.
```
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>1.0.6</version>
</dependency>
```
Create the Spring Boot Admin Server with only one single Annotation.
Example in spring-admin-samples/spring-boot-admin-sample.
```
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
```
## Hazelcast Support
Spring Boot Admin Server supports cluster replication with Hazelcast.
It is automatically enabled when its found on the classpath.
Just add Hazelcast to your dependencies:
```
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.3.3</version>
</dependency>
```
And thats it! The server is going to use the default Hazelcast configuration.
### Custom Hazelcast configuration
To change the configuration add a com.hazelcast.config.Config bean to your application context (for example with hazelcast-spring):
Add hazelcast-spring to dependencies:
```
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>3.3.3</version>
</dependency>
```
Import hazelcast spring configuration xml-file:
```
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
@ImportResource({ "classpath:hazelcast-config.xml" })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
```
Write xml-config hazelcast-config.xml:
```
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:hz="http://www.hazelcast.com/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.hazelcast.com/schema/spring http://www.hazelcast.com/schema/spring/hazelcast-spring-3.3.xsd">
<hz:config id="hazelcastConfig">
<hz:instance-name>${hz.instance.name}</hz:instance-name>
<hz:map name="spring-boot-admin-application-store" backup-count="1" eviction-policy="NONE" />
</hz:config>
</beans>
```
### Further configuration
To disable Hazelcast support by setting ``spring.boot.admin.hazelcast.enable=false``.
To alter the name of the Hazelcast-Map set ``spring.boot.admin.hazelcast.map= my-own-map-name``.
......@@ -26,6 +26,11 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
</dependency>
......
......@@ -17,6 +17,10 @@ package de.codecentric.boot.admin.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -24,11 +28,17 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import de.codecentric.boot.admin.controller.RegistryController;
import de.codecentric.boot.admin.model.Application;
import de.codecentric.boot.admin.registry.ApplicationIdGenerator;
import de.codecentric.boot.admin.registry.ApplicationRegistry;
import de.codecentric.boot.admin.registry.HashingApplicationUrlIdGenerator;
import de.codecentric.boot.admin.registry.store.ApplicationStore;
import de.codecentric.boot.admin.registry.store.HazelcastApplicationStore;
import de.codecentric.boot.admin.registry.store.SimpleApplicationStore;
@Configuration
......@@ -54,7 +64,6 @@ public class WebappConfig extends WebMvcConfigurerAdapter {
* Default registry for all registered application.
*/
@Bean
@ConditionalOnMissingBean
public ApplicationRegistry applicationRegistry(ApplicationStore applicationStore,
ApplicationIdGenerator applicationIdGenerator) {
return new ApplicationRegistry(applicationStore, applicationIdGenerator);
......@@ -65,17 +74,45 @@ public class WebappConfig extends WebMvcConfigurerAdapter {
*/
@Bean
@ConditionalOnMissingBean
public HashingApplicationUrlIdGenerator applicationIdGenerator() {
public ApplicationIdGenerator applicationIdGenerator() {
return new HashingApplicationUrlIdGenerator();
}
/**
* Default applicationId Generator
*/
@Configuration
public static class SimpleConfig {
@Bean
@ConditionalOnMissingBean
public ApplicationStore applicationStore() {
return new SimpleApplicationStore();
}
}
@Configuration
@ConditionalOnClass({ Hazelcast.class })
@ConditionalOnExpression("${spring.boot.admin.hazelcast.enable:true}")
@AutoConfigureBefore(SimpleConfig.class)
public static class HazelcastConfig {
@Value("${spring.boot.admin.hazelcast.map:spring-boot-admin-application-store}")
private String hazelcastMapName;
@Bean
@ConditionalOnMissingBean
public Config hazelcastConfig() {
return new Config();
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean
public HazelcastInstance hazelcastInstance(Config hazelcastConfig) {
return Hazelcast.newHazelcastInstance(hazelcastConfig);
}
@Bean
@ConditionalOnMissingBean
public ApplicationStore applicationStore(HazelcastInstance hazelcast) {
return new HazelcastApplicationStore(hazelcast.<String, Application> getMap(hazelcastMapName));
}
}
}
package de.codecentric.boot.admin.registry.store;
import java.util.ArrayList;
import java.util.List;
import com.hazelcast.core.IMap;
import de.codecentric.boot.admin.model.Application;
public class HazelcastApplicationStore implements ApplicationStore {
private IMap<String, Application> store;
public HazelcastApplicationStore(IMap<String, Application> store) {
this.store = store;
}
@Override
public Application put(Application app) {
return store.putIfAbsent(app.getId(), app);
}
@Override
public List<Application> getAll() {
return new ArrayList<Application>(store.values());
}
@Override
public Application get(String id) {
return store.get(id);
}
@Override
public Application remove(String id) {
return store.remove(id);
}
}
/*
* Copyright 2014 the original author or authors.
*
* Licensed 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 de.codecentric.boot.admin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import de.codecentric.boot.admin.config.EnableAdminServer;
import de.codecentric.boot.admin.model.Application;
/**
*
* Integration test to verify the correct functionality of the REST API with Hazelcast
*
* @author Dennis Schulte
*/
public class AdminApplicationHazelcastTest {
private RestTemplate template = new TestRestTemplate();
private AnnotationConfigEmbeddedWebApplicationContext instance1;
private AnnotationConfigEmbeddedWebApplicationContext instance2;
@Before
public void setup() {
instance1 = (AnnotationConfigEmbeddedWebApplicationContext) SpringApplication.run(TestAdminApplication.class,
new String[] { "--server.port=0", "--spring.jmx.enabled=false" });
instance2 = (AnnotationConfigEmbeddedWebApplicationContext) SpringApplication.run(TestAdminApplication.class,
new String[] { "--server.port=0", "--spring.jmx.enabled=false" });
}
@After
public void shutdown() {
instance1.stop();
instance2.stop();
}
@Test
public void test() {
Application app = new Application("http://127.0.0.1", "Hazelcast Test");
// publish application on instance1
int port1 = instance1.getEmbeddedServletContainer().getPort();
ResponseEntity<Application> postResponse = template.postForEntity("http://localhost:" + port1
+ "/api/applications", app, Application.class);
assertEquals(HttpStatus.CREATED, postResponse.getStatusCode());
assertNotNull(postResponse.getBody().getId());
// retrieve application from instance2
int port2 = instance2.getEmbeddedServletContainer().getPort();
ResponseEntity<Application> getResponse = template.getForEntity("http://localhost:" + port2
+ "/api/application/" + postResponse.getBody().getId(), Application.class);
assertEquals(HttpStatus.OK, getResponse.getStatusCode());
assertEquals(postResponse.getBody(), getResponse.getBody());
}
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
public static class TestAdminApplication {
}
}
......@@ -22,15 +22,20 @@ import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
import de.codecentric.boot.admin.AdminApplicationTest.TestAdminApplication;
import de.codecentric.boot.admin.config.EnableAdminServer;
/**
*
* Integration test to verify the correct functionality of the REST API.
......@@ -38,9 +43,9 @@ import org.springframework.web.client.RestTemplate;
* @author Dennis Schulte
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=TestAdminApplication.class)
@SpringApplicationConfiguration(classes = TestAdminApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port=0")
@IntegrationTest({ "server.port=0", "spring.boot.admin.hazelcast.enable=false" })
public class AdminApplicationTest {
RestTemplate restTemplate = new TestRestTemplate();
......@@ -49,10 +54,16 @@ public class AdminApplicationTest {
private int port = 0;
@Test
public void testGetApplications() throws InterruptedException{
public void testGetApplications() {
@SuppressWarnings("rawtypes")
ResponseEntity<List> entity = new TestRestTemplate().getForEntity("http://localhost:" + port + "/api/applications", List.class);
ResponseEntity<List> entity = new TestRestTemplate().getForEntity("http://localhost:" + port
+ "/api/applications", List.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
public static class TestAdminApplication {
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment