Commit ea931248 by Johannes Stelzer

Redesign Registry

* Separate AppStore from AppRegistry * Allow Id generation to be altered * Remove logic from Application; Move id genretation into AppRegistry * Make AppStore threadSafe
parent 75afae26
...@@ -25,8 +25,11 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert ...@@ -25,8 +25,11 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import de.codecentric.boot.admin.controller.RegistryController; import de.codecentric.boot.admin.controller.RegistryController;
import de.codecentric.boot.admin.service.ApplicationRegistry; import de.codecentric.boot.admin.registry.ApplicationIdGenerator;
import de.codecentric.boot.admin.service.SimpleApplicationRegistry; 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.SimpleApplicationStore;
@Configuration @Configuration
public class WebappConfig extends WebMvcConfigurerAdapter { public class WebappConfig extends WebMvcConfigurerAdapter {
...@@ -52,8 +55,27 @@ public class WebappConfig extends WebMvcConfigurerAdapter { ...@@ -52,8 +55,27 @@ public class WebappConfig extends WebMvcConfigurerAdapter {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ApplicationRegistry applicationRegistry() { public ApplicationRegistry applicationRegistry(ApplicationStore applicationStore,
return new SimpleApplicationRegistry(); ApplicationIdGenerator applicationIdGenerator) {
return new ApplicationRegistry(applicationStore, applicationIdGenerator);
}
/**
* Default applicationId Generator
*/
@Bean
@ConditionalOnMissingBean
public HashingApplicationUrlIdGenerator applicationIdGenerator() {
return new HashingApplicationUrlIdGenerator();
}
/**
* Default applicationId Generator
*/
@Bean
@ConditionalOnMissingBean
public ApplicationStore applicationStore() {
return new SimpleApplicationStore();
} }
} }
...@@ -28,7 +28,8 @@ import org.springframework.web.bind.annotation.RequestMethod; ...@@ -28,7 +28,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.model.Application;
import de.codecentric.boot.admin.service.ApplicationRegistry; import de.codecentric.boot.admin.registry.ApplicationRegistry;
import de.codecentric.boot.admin.registry.ApplicationRegistryConflictException;
/** /**
* REST controller for controlling registration of managed applications. * REST controller for controlling registration of managed applications.
...@@ -53,14 +54,10 @@ public class RegistryController { ...@@ -53,14 +54,10 @@ public class RegistryController {
@RequestMapping(value = "/api/applications", method = RequestMethod.POST) @RequestMapping(value = "/api/applications", method = RequestMethod.POST)
public ResponseEntity<Application> register(@RequestBody Application app) { public ResponseEntity<Application> register(@RequestBody Application app) {
LOGGER.debug("Register application {}", app.toString()); LOGGER.debug("Register application {}", app.toString());
try {
Application registered = registry.getApplication(app.getId()); Application registeredApp = registry.register(app);
if (registered == null || registered.equals(app)) { return new ResponseEntity<Application>(registeredApp, HttpStatus.CREATED);
LOGGER.info("Application {} registered.", app.toString()); } catch (ApplicationRegistryConflictException ex) {
registry.register(app);
return new ResponseEntity<Application>(app, HttpStatus.CREATED);
}
else {
return new ResponseEntity<Application>(HttpStatus.CONFLICT); return new ResponseEntity<Application>(HttpStatus.CONFLICT);
} }
} }
...@@ -77,8 +74,7 @@ public class RegistryController { ...@@ -77,8 +74,7 @@ public class RegistryController {
Application application = registry.getApplication(id); Application application = registry.getApplication(id);
if (application != null) { if (application != null) {
return new ResponseEntity<Application>(application, HttpStatus.OK); return new ResponseEntity<Application>(application, HttpStatus.OK);
} } else {
else {
return new ResponseEntity<Application>(application, HttpStatus.NOT_FOUND); return new ResponseEntity<Application>(application, HttpStatus.NOT_FOUND);
} }
} }
...@@ -90,12 +86,11 @@ public class RegistryController { ...@@ -90,12 +86,11 @@ public class RegistryController {
*/ */
@RequestMapping(value = "/api/application/{id}", method = RequestMethod.DELETE) @RequestMapping(value = "/api/application/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Application> unregister(@PathVariable String id) { public ResponseEntity<Application> unregister(@PathVariable String id) {
LOGGER.info("Unregister application with ID '{}'", id); LOGGER.debug("Unregister application with ID '{}'", id);
Application app = registry.unregister(id); Application app = registry.unregister(id);
if (app != null) { if (app != null) {
return new ResponseEntity<Application>(app, HttpStatus.NO_CONTENT); return new ResponseEntity<Application>(app, HttpStatus.NO_CONTENT);
} } else {
else {
return new ResponseEntity<Application>(HttpStatus.NOT_FOUND); return new ResponseEntity<Application>(HttpStatus.NOT_FOUND);
} }
} }
......
/*
* Copyright 2013-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.registry;
import de.codecentric.boot.admin.model.Application;
public interface ApplicationIdGenerator {
/**
* Generate an id based on the given Application
*
* @param a the application the id is computed for.
* @return the application id
*/
String generateId(Application a);
}
...@@ -13,43 +13,67 @@ ...@@ -13,43 +13,67 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.codecentric.boot.admin.service; package de.codecentric.boot.admin.registry;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.springframework.stereotype.Service; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.model.Application;
import de.codecentric.boot.admin.registry.store.ApplicationStore;
/** /**
* This registry is just "in-memory", so that after a restart all applications have to be * Registry for all applications that should be managed/administrated by the Spring Boot Admin application.
* registered again. * Backed by an ApplicationStore for persistence and an ApplicationIdGenerator for id generation.
*/ */
@Service public class ApplicationRegistry {
public class SimpleApplicationRegistry implements ApplicationRegistry { private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRegistry.class);
private final Map<String, Application> store = new HashMap<>(); private final ApplicationStore store;
private final ApplicationIdGenerator generator;
@Override public ApplicationRegistry(ApplicationStore store, ApplicationIdGenerator generator) {
public void register(Application app) { this.store = store;
this.generator = generator;
}
/**
* Register application.
*
* @param app The Application.
*/
public Application register(Application app) {
Validate.notNull(app, "Application must not be null"); Validate.notNull(app, "Application must not be null");
Validate.notNull(app.getId(), "ID must not be null");
Validate.notNull(app.getUrl(), "URL must not be null"); Validate.notNull(app.getUrl(), "URL must not be null");
Validate.isTrue(checkUrl(app.getUrl()), "URL is not valid"); Validate.isTrue(checkUrl(app.getUrl()), "URL is not valid");
store.put(app.getId(), app);
String applicationId = generator.generateId(app);
Validate.notNull(applicationId, "ID must not be null");
Application newApp = new Application(app.getUrl(), app.getName(), applicationId);
Application oldApp = store.put(newApp);
if (oldApp == null) {
LOGGER.info("New Application {} registered ", newApp);
} else {
if ((app.getUrl().equals(oldApp.getUrl()) && app.getName().equals(oldApp.getName()))) {
LOGGER.info("Application {} refreshed", newApp);
} else {
LOGGER.warn("Application {} not registered because of conflict with {}", newApp, oldApp);
throw new ApplicationRegistryConflictException(oldApp, app);
}
}
return newApp;
} }
/** /**
* Checks the syntax of the given URL. * Checks the syntax of the given URL.
* *
* @param url * @param url The URL.
* The URL.
* @return true, if valid. * @return true, if valid.
*/ */
private boolean checkUrl(String url) { private boolean checkUrl(String url) {
...@@ -61,19 +85,34 @@ public class SimpleApplicationRegistry implements ApplicationRegistry { ...@@ -61,19 +85,34 @@ public class SimpleApplicationRegistry implements ApplicationRegistry {
return true; return true;
} }
@Override /**
* Get a list of all registered applications.
*
* @return List.
*/
public List<Application> getApplications() { public List<Application> getApplications() {
return new ArrayList<>(store.values()); return store.getAll();
} }
@Override /**
* Get a specific application inside the registry.
*
* @param id Id.
* @return Application.
*/
public Application getApplication(String id) { public Application getApplication(String id) {
return store.get(id); return store.get(id);
} }
@Override /**
* Remove a specific application from registry
*
* @param id
* @return the unregistered Application
*/
public Application unregister(String id) { public Application unregister(String id) {
return store.remove(id); Application app = store.remove(id);
LOGGER.info("Application {} unregistered ", app);
return app;
} }
} }
/*
* Copyright 2013-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.registry;
import de.codecentric.boot.admin.model.Application;
public class ApplicationRegistryConflictException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ApplicationRegistryConflictException(Application oldApp, Application newApp) {
super("Conflict in ApplicationRegistry: " + oldApp.toString() + " vs. " + newApp.toString());
}
}
/*
* Copyright 2013-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.registry;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import de.codecentric.boot.admin.model.Application;
/**
* Generates an SHA-1 Hash based on the applications url.
*/
public class HashingApplicationUrlIdGenerator implements ApplicationIdGenerator {
private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
@Override
public String generateId(Application a) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] bytes = digest.digest(a.getUrl().getBytes(Charset.forName("UTF-8")));
return new String(encodeHex(bytes, 0, 8));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private char[] encodeHex(byte[] bytes, int offset, int length) {
char chars[] = new char[length];
for (int i = 0; i < length; i = i + 2) {
byte b = bytes[offset + (i / 2)];
chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
chars[i + 1] = HEX_CHARS[b & 0xf];
}
return chars;
}
}
/* /*
* Copyright 2014 the original author or authors. * Copyright 2013-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,45 +13,39 @@ ...@@ -13,45 +13,39 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.codecentric.boot.admin.service; package de.codecentric.boot.admin.registry.store;
import java.util.List; import java.util.List;
import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.model.Application;
/** /**
* Registry for all applications that should be managed/administrated by the * Responsible for storing applications.
* spring-boot-admin application.
*/ */
public interface ApplicationRegistry { public interface ApplicationStore {
/** /**
* Register application. * Inserts a new Application into the store. If the Id is already present in the store the Application is NOT stored.
* *
* @param app The Application. * @param app Application to store
* @return the Application associated previosly with the applications id.
*/ */
void register(Application app); Application put(Application app);
/** /**
* Get a list of all registered applications. * @return all Applications in the store;
*
* @return List.
*/ */
List<Application> getApplications(); List<Application> getAll();
/** /**
* Get a specific application inside the registry. * @param id the applications id
* * @return the Application with the specified id;
* @param id Id.
* @return Application.
*/ */
Application getApplication(String id); Application get(String id);
/** /**
* Remove a specific application from registry * @param id id of the Application to be removed
* @param id * @return the Application associated previosly with the applications id.
* @return the unregistered Application
*/ */
Application unregister(String id); Application remove(String id);
} }
/*
* Copyright 2013-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.registry.store;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import de.codecentric.boot.admin.model.Application;
/**
* Simple ApplicationStore backed by a ConcurrentHashMap.
*/
public class SimpleApplicationStore implements ApplicationStore {
private final ConcurrentHashMap<String, Application> map = new ConcurrentHashMap<>();
@Override
public Application put(Application app) {
return map.putIfAbsent(app.getId(), app);
}
@Override
public List<Application> getAll() {
return new ArrayList<Application>(map.values());
}
@Override
public Application get(String id) {
return map.get(id);
}
@Override
public Application remove(String id) {
return map.remove(id);
}
}
...@@ -17,7 +17,6 @@ package de.codecentric.boot.admin.controller; ...@@ -17,7 +17,6 @@ package de.codecentric.boot.admin.controller;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
...@@ -26,7 +25,9 @@ import org.springframework.http.HttpStatus; ...@@ -26,7 +25,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.model.Application;
import de.codecentric.boot.admin.service.SimpleApplicationRegistry; import de.codecentric.boot.admin.registry.ApplicationRegistry;
import de.codecentric.boot.admin.registry.HashingApplicationUrlIdGenerator;
import de.codecentric.boot.admin.registry.store.SimpleApplicationStore;
public class RegistryControllerTest { public class RegistryControllerTest {
...@@ -34,25 +35,27 @@ public class RegistryControllerTest { ...@@ -34,25 +35,27 @@ public class RegistryControllerTest {
@Before @Before
public void setup() { public void setup() {
controller = new RegistryController(new SimpleApplicationRegistry()); controller = new RegistryController(new ApplicationRegistry(new SimpleApplicationStore(),
new HashingApplicationUrlIdGenerator()));
} }
@Test @Test
public void register() { public void register() {
ResponseEntity<?> response = controller.register(new Application("http://localhost", "test")); ResponseEntity<Application> response = controller.register(new Application("http://localhost", "test"));
assertEquals(HttpStatus.CREATED, response.getStatusCode()); assertEquals(HttpStatus.CREATED, response.getStatusCode());
assertEquals(new Application("http://localhost", "test"), response.getBody()); assertEquals("http://localhost", response.getBody().getUrl());
assertEquals("test", response.getBody().getName());
} }
@Test @Test
public void register_twice() { public void register_twice() {
controller.register(new Application("http://localhost", "test")); controller.register(new Application("http://localhost", "test"));
Application app = new Application("http://localhost", "test"); Application app = new Application("http://localhost", "test");
ResponseEntity<?> response = controller.register(app); ResponseEntity<Application> response = controller.register(app);
assertEquals(HttpStatus.CREATED, response.getStatusCode()); assertEquals(HttpStatus.CREATED, response.getStatusCode());
assertEquals(new Application("http://localhost", "test"), response.getBody()); assertEquals("http://localhost", response.getBody().getUrl());
assertEquals("test", response.getBody().getName());
} }
@Test @Test
...@@ -65,11 +68,12 @@ public class RegistryControllerTest { ...@@ -65,11 +68,12 @@ public class RegistryControllerTest {
@Test @Test
public void get() { public void get() {
Application app = new Application("http://localhost", "FOO"); Application app = new Application("http://localhost", "FOO");
controller.register(app); app = controller.register(app).getBody();
ResponseEntity<?> response = controller.get(app.getId()); ResponseEntity<Application> response = controller.get(app.getId());
assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(app, response.getBody()); assertEquals("http://localhost", response.getBody().getUrl());
assertEquals("FOO", response.getBody().getName());
} }
@Test @Test
...@@ -80,11 +84,10 @@ public class RegistryControllerTest { ...@@ -80,11 +84,10 @@ public class RegistryControllerTest {
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
} }
@Test @Test
public void unregister() { public void unregister() {
Application app = new Application("http://localhost", "FOO"); Application app = new Application("http://localhost", "FOO");
controller.register(app); app = controller.register(app).getBody();
ResponseEntity<?> response = controller.unregister(app.getId()); ResponseEntity<?> response = controller.unregister(app.getId());
assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
...@@ -107,7 +110,9 @@ public class RegistryControllerTest { ...@@ -107,7 +110,9 @@ public class RegistryControllerTest {
controller.register(app); controller.register(app);
List<Application> applications = controller.applications(); List<Application> applications = controller.applications();
assertEquals(Collections.singletonList(app), applications); assertEquals(1, applications.size());
assertEquals(app.getName(), applications.get(0).getName());
assertEquals(app.getUrl(), applications.get(0).getUrl());
} }
} }
...@@ -13,17 +13,22 @@ ...@@ -13,17 +13,22 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.codecentric.boot.admin.service; package de.codecentric.boot.admin.registry;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test; import org.junit.Test;
import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.model.Application;
import de.codecentric.boot.admin.registry.ApplicationRegistry;
import de.codecentric.boot.admin.registry.HashingApplicationUrlIdGenerator;
import de.codecentric.boot.admin.registry.store.SimpleApplicationStore;
public class SimpleApplicationRegistryTest { public class ApplicationRegistryTest {
private ApplicationRegistry registry = new SimpleApplicationRegistry(); private ApplicationRegistry registry = new ApplicationRegistry(new SimpleApplicationStore(),
new HashingApplicationUrlIdGenerator());
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void registerFailed1() throws Exception { public void registerFailed1() throws Exception {
...@@ -45,14 +50,17 @@ public class SimpleApplicationRegistryTest { ...@@ -45,14 +50,17 @@ public class SimpleApplicationRegistryTest {
@Test @Test
public void register() throws Exception { public void register() throws Exception {
Application app = new Application("http://localhost:8080", "abc"); Application app = new Application("http://localhost:8080", "abc");
registry.register(app); Application response = registry.register(app);
}
assertEquals("http://localhost:8080", response.getUrl());
assertEquals("abc", response.getName());
assertNotNull(response.getId());
}
@Test @Test
public void getApplication() throws Exception { public void getApplication() throws Exception {
Application app = new Application("http://localhost:8080", "abc"); Application app = new Application("http://localhost:8080", "abc");
registry.register(app); app = registry.register(app);
assertEquals(app, registry.getApplication(app.getId())); assertEquals(app, registry.getApplication(app.getId()));
} }
...@@ -60,10 +68,10 @@ public class SimpleApplicationRegistryTest { ...@@ -60,10 +68,10 @@ public class SimpleApplicationRegistryTest {
@Test @Test
public void getApplications() throws Exception { public void getApplications() throws Exception {
Application app = new Application("http://localhost:8080", "abc"); Application app = new Application("http://localhost:8080", "abc");
registry.register(app); app = registry.register(app);
assertEquals(1, registry.getApplications().size()); assertEquals(1, registry.getApplications().size());
assertEquals(app, registry.getApplications().get(0)); assertEquals("http://localhost:8080", registry.getApplications().get(0).getUrl());
assertEquals("abc", registry.getApplications().get(0).getName());
} }
} }
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package de.codecentric.boot.admin.model; package de.codecentric.boot.admin.model;
import java.io.Serializable; import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
...@@ -34,13 +32,14 @@ public class Application implements Serializable { ...@@ -34,13 +32,14 @@ public class Application implements Serializable {
private final String url; private final String url;
private final String name; private final String name;
@JsonCreator public Application(String url, String name) {
public Application(@JsonProperty("url") String url, @JsonProperty("name") String name) { this(url, name, null);
this(url.replaceFirst("/+$", ""), name, generateId(url.replaceFirst("/+$", "")));
} }
protected Application(String url, String name, String id) { @JsonCreator
this.url = url; public Application(@JsonProperty(value = "url", required = true) String url,
@JsonProperty(value = "name", required = true) String name, @JsonProperty("id") String id) {
this.url = url.replaceFirst("/+$", "");
this.name = name; this.name = name;
this.id = id; this.id = id;
} }
...@@ -66,6 +65,7 @@ public class Application implements Serializable { ...@@ -66,6 +65,7 @@ public class Application implements Serializable {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((url == null) ? 0 : url.hashCode()); result = prime * result + ((url == null) ? 0 : url.hashCode());
return result; return result;
...@@ -83,46 +83,28 @@ public class Application implements Serializable { ...@@ -83,46 +83,28 @@ public class Application implements Serializable {
return false; return false;
} }
Application other = (Application) obj; Application other = (Application) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) { if (name == null) {
if (other.name != null) { if (other.name != null) {
return false; return false;
} }
} } else if (!name.equals(other.name)) {
else if (!name.equals(other.name)) {
return false; return false;
} }
if (url == null) { if (url == null) {
if (other.url != null) { if (other.url != null) {
return false; return false;
} }
} } else if (!url.equals(other.url)) {
else if (!url.equals(other.url)) {
return false; return false;
} }
return true; return true;
} }
private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
private static String generateId(String url) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] bytes = digest.digest(url.getBytes(Charset.forName("UTF-8")));
return new String(encodeHex(bytes, 0, 8));
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
private static char[] encodeHex(byte[] bytes, int offset, int length) {
char chars[] = new char[length];
for (int i = 0; i < length; i = i + 2) {
byte b = bytes[offset + (i / 2)];
chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
chars[i + 1] = HEX_CHARS[b & 0xf];
}
return chars;
}
} }
/*
* 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.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class ApplicationTest {
@Test
public void url_id() {
Application a = new Application("http://localhost:8080/", null);
Application b = new Application("http://localhost:8080", null);
assertEquals("same urls must have same id", a.getId(), b.getId());
Application z = new Application("http://127.0.0.1:8080", null);
assertFalse("different urls must have diffenrent Id", a.getId().equals(z.getId()));
}
public void equals() {
Application a = new Application("http://localhost:8080/", "FOO");
Application b = new Application("http://localhost:8080", "FOO");
assertEquals("same url and same name must be equals", a, b);
assertEquals("hashcode should be equals", a.hashCode(), b.hashCode());
Application z = new Application("http://127.0.0.1:8080", "FOO");
assertFalse("different urls same name must not be equals", a.equals(z));
Application y = new Application("http://localhost:8080", "BAR");
assertFalse("same urls different name must not be equals", a.getId().equals(y.getId()));
}
}
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