Commit d944145c by Dave Syer

Make hystrix dashboard work with a context path

I didn't see a way to do this without dynamic rendering, so now we have a freemarker dependency (not huge I guess). Fixes gh-51
parent 5ef304a4
......@@ -10,11 +10,13 @@ import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.configuration.ConfigurationBuilder;
import org.apache.commons.configuration.EnvironmentConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
......@@ -27,6 +29,7 @@ import com.netflix.config.DynamicURLConfiguration;
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnClass({ConcurrentCompositeConfiguration.class, ConfigurationBuilder.class})
public class ArchaiusAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ArchaiusAutoConfiguration.class);
......
......@@ -26,7 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean;
......
......@@ -20,6 +20,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-core</artifactId>
......@@ -29,10 +33,6 @@
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
</dependency>
......@@ -48,5 +48,10 @@
<groupId>org.webjars</groupId>
<artifactId>d3js</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
......@@ -37,6 +37,11 @@ public class HystrixDashboardConfiguration {
return new ServletRegistrationBean(new ProxyStreamServlet(), "/proxy.stream");
}
@Bean
public HystrixDashboardController hsytrixDashboardController() {
return new HystrixDashboardController();
}
/**
* Proxy an EventStream request (data.stream via proxy.stream) since EventStream does not yet support CORS (https://bugs.webkit.org/show_bug.cgi?id=61862)
* so that a UI can request a stream from a different server.
......
/*
* 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 org.springframework.cloud.netflix.hystrix.dashboard;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;
/**
* @author Dave Syer
*
*/
@Controller
public class HystrixDashboardController {
@RequestMapping("/hystrix")
public String home(Model model, WebRequest request) {
model.addAttribute("basePath", extractPath(request));
return "index";
}
@RequestMapping("/hystrix/monitor")
public String monitor(Model model, WebRequest request) {
model.addAttribute("basePath", extractPath(request));
model.addAttribute("contextPath", request.getContextPath());
return "monitor";
}
private String extractPath(WebRequest request) {
String path = request.getContextPath()
+ (String) request
.getAttribute(
"org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping",
WebRequest.SCOPE_REQUEST);
return path;
}
}
......@@ -2,10 +2,10 @@
(function(window) {
// cache the templates we use on this page as global variables (asynchronously)
jQuery.get(getRelativePath("../components/hystrixCommand/templates/hystrixCircuit.html"), function(data) {
jQuery.get(getRelativePath("components/hystrixCommand/templates/hystrixCircuit.html"), function(data) {
hystrixTemplateCircuit = data;
});
jQuery.get(getRelativePath("../components/hystrixCommand/templates/hystrixCircuitContainer.html"), function(data) {
jQuery.get(getRelativePath("components/hystrixCommand/templates/hystrixCircuitContainer.html"), function(data) {
hystrixTemplateCircuitContainer = data;
});
......
......@@ -2,10 +2,10 @@
(function(window) {
// cache the templates we use on this page as global variables (asynchronously)
jQuery.get(getRelativePath("../components/hystrixThreadPool/templates/hystrixThreadPool.html"), function(data) {
jQuery.get(getRelativePath("components/hystrixThreadPool/templates/hystrixThreadPool.html"), function(data) {
htmlTemplate = data;
});
jQuery.get(getRelativePath("../components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html"), function(data) {
jQuery.get(getRelativePath("components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html"), function(data) {
htmlTemplateContainer = data;
});
......
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="${basePath}">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Hystrix Dashboard</title>
<!-- Javascript to monitor and display -->
<script src="/webjars/jquery/2.1.1/jquery.min.js" type="text/javascript"></script>
<script src="webjars/jquery/2.1.1/jquery.min.js" type="text/javascript"></script>
<script>
function sendToMonitor() {
if($('#stream').val().length > 0) {
var url = "/hystrix/monitor/monitor.html?stream=" + encodeURIComponent($('#stream').val()) + "";
var url = "hystrix/monitor?stream=" + encodeURIComponent($('#stream').val()) + "";
if($('#delay').val().length > 0) {
url += "&delay=" + $('#delay').val();
}
......
<!doctype html>
<html lang="en">
<head>
<base href="${basePath}">
<meta charset="utf-8" />
<title>Hystrix Monitor</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Setup base for everything -->
<link rel="stylesheet" type="text/css" href="../css/global.css" />
<link rel="stylesheet" type="text/css" href="css/global.css" />
<!-- Our custom CSS -->
<link rel="stylesheet" type="text/css" href="monitor.css" />
<link rel="stylesheet" type="text/css" href="css/monitor.css" />
<!-- d3 -->
<script type="text/javascript" src="/webjars/d3js/3.4.11/d3.min.js" ></script>
<script type="text/javascript" src="../webjars/d3js/3.4.11/d3.min.js" ></script>
<!-- Javascript to monitor and display -->
<script type="text/javascript" src="/webjars/jquery/2.1.1/jquery.min.js" ></script>
<script type="text/javascript" src="../js/jquery.tinysort.min.js"></script>
<script type="text/javascript" src="../js/tmpl.js"></script>
<script type="text/javascript" src="../webjars/jquery/2.1.1/jquery.min.js" ></script>
<script type="text/javascript" src="js/jquery.tinysort.min.js"></script>
<script type="text/javascript" src="js/tmpl.js"></script>
<!-- HystrixCommand -->
<script type="text/javascript" src="../components/hystrixCommand/hystrixCommand.js"></script>
<link rel="stylesheet" type="text/css" href="../components/hystrixCommand/hystrixCommand.css" />
<script type="text/javascript" src="components/hystrixCommand/hystrixCommand.js"></script>
<link rel="stylesheet" type="text/css" href="components/hystrixCommand/hystrixCommand.css" />
<!-- HystrixThreadPool -->
<script type="text/javascript" src="../components/hystrixThreadPool/hystrixThreadPool.js"></script>
<link rel="stylesheet" type="text/css" href="../components/hystrixThreadPool/hystrixThreadPool.css" />
<script type="text/javascript" src="components/hystrixThreadPool/hystrixThreadPool.js"></script>
<link rel="stylesheet" type="text/css" href="components/hystrixThreadPool/hystrixThreadPool.css" />
</head>
<body>
......@@ -96,8 +97,8 @@
stream = stream + "&delay=" + getUrlVars()["delay"];
}
var commandStream = "/proxy.stream?origin=" + stream;
var poolStream = "/proxy.stream?origin=" + stream;
var commandStream = "${contextPath}/proxy.stream?origin=" + stream;
var poolStream = "${contextPath}/proxy.stream?origin=" + stream;
if(getUrlVars()["title"] != undefined) {
$('#title_name').html("Hystrix Stream: " + decodeURIComponent(getUrlVars()["title"]))
......
/*
* 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 org.springframework.cloud.netflix.hystrix.dashboard;
import static org.junit.Assert.assertEquals;
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.builder.SpringApplicationBuilder;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardContextTests.Application;
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;
/**
* @author Dave Syer
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest({ "server.port=0", "spring.application.name=hystrix-dashboard", "server.contextPath=/context" })
public class HystrixDashboardContextTests {
@Value("${local.server.port}")
private int port = 0;
@Test
public void homePage() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/context/hystrix", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Test
public void cssAvailable() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/context/hystrix/css/global.css", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Test
public void webjarsAvailable() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/context/webjars/jquery/2.1.1/jquery.min.js", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Test
public void monitorPage() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/context/hystrix/monitor", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Configuration
@EnableAutoConfiguration
@EnableHystrixDashboard
protected static class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).properties(
"spring.application.name=hystrix-dashboard", "server.contextPath=/context").run();
}
}
}
/*
* 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 org.springframework.cloud.netflix.hystrix.dashboard;
import static org.junit.Assert.assertEquals;
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.builder.SpringApplicationBuilder;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardTests.Application;
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;
/**
* @author Dave Syer
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest({ "server.port=0", "spring.application.name=hystrix-dashboard" })
public class HystrixDashboardTests {
@Value("${local.server.port}")
private int port = 0;
@Test
public void homePage() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/hystrix", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Test
public void cssAvailable() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/hystrix/css/global.css", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Test
public void monitorPage() {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/hystrix/monitor", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Configuration
@EnableAutoConfiguration
@EnableHystrixDashboard
protected static class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).properties(
"spring.application.name=hystrix-dashboard").run();
}
}
}
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