diff --git a/pom.xml b/pom.xml
index e55b37a0..c5d055b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -159,6 +159,21 @@
jersey-server
1.13
+
+ org.webjars
+ d3js
+ 3.4.11
+
+
+ org.webjars
+ jquery
+ 2.1.1
+
+
+ org.webjars
+ bootstrap
+ 3.2.0
+
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixConfiguration.java
index 84bebf5a..afc41eb3 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixConfiguration.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixConfiguration.java
@@ -1,6 +1,7 @@
package org.springframework.cloud.netflix.hystrix;
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -8,7 +9,6 @@ import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrix;
-import org.springframework.cloud.netflix.endpoint.HystrixStreamEndpoint;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixDashboardConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixDashboardConfiguration.java
new file mode 100644
index 00000000..3ff80319
--- /dev/null
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixDashboardConfiguration.java
@@ -0,0 +1,168 @@
+package org.springframework.cloud.netflix.hystrix;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.embedded.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Dave Syer
+ */
+@SuppressWarnings("deprecation")
+@Configuration
+public class HystrixDashboardConfiguration {
+
+ @Bean
+ public ServletRegistrationBean proxyStreamServlet() {
+ return new ServletRegistrationBean(new ProxyStreamServlet(), "/proxy.stream");
+ }
+
+ /**
+ * 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.
+ */
+ public static class ProxyStreamServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ private static final Logger logger = LoggerFactory.getLogger(ProxyStreamServlet.class);
+
+ public ProxyStreamServlet() {
+ super();
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String origin = request.getParameter("origin");
+ if (origin == null) {
+ response.setStatus(500);
+ response.getWriter().println("Required parameter 'origin' missing. Example: 107.20.175.135:7001");
+ }
+ origin = origin.trim();
+
+ HttpGet httpget = null;
+ InputStream is = null;
+ boolean hasFirstParameter = false;
+ StringBuilder url = new StringBuilder();
+ if (!origin.startsWith("http")) {
+ url.append("http://");
+ }
+ url.append(origin);
+ if (origin.contains("?")) {
+ hasFirstParameter = true;
+ }
+ Map params = request.getParameterMap();
+ for (String key : params.keySet()) {
+ if (!key.equals("origin")) {
+ String[] values = params.get(key);
+ String value = values[0].trim();
+ if (hasFirstParameter) {
+ url.append("&");
+ } else {
+ url.append("?");
+ hasFirstParameter = true;
+ }
+ url.append(key).append("=").append(value);
+ }
+ }
+ String proxyUrl = url.toString();
+ logger.info("\n\nProxy opening connection to: " + proxyUrl + "\n\n");
+ try {
+ httpget = new HttpGet(proxyUrl);
+ HttpClient client = ProxyConnectionManager.httpClient;
+ HttpResponse httpResponse = client.execute(httpget);
+ int statusCode = httpResponse.getStatusLine().getStatusCode();
+ if (statusCode == HttpStatus.SC_OK) {
+ // writeTo swallows exceptions and never quits even if outputstream is throwing IOExceptions (such as broken pipe) ... since the inputstream is infinite
+ // httpResponse.getEntity().writeTo(new OutputStreamWrapper(response.getOutputStream()));
+ // so I copy it manually ...
+ is = httpResponse.getEntity().getContent();
+
+ // set headers
+ for (Header header : httpResponse.getAllHeaders()) {
+ response.addHeader(header.getName(), header.getValue());
+ }
+
+ // copy data from source to response
+ OutputStream os = response.getOutputStream();
+ int b = -1;
+ while ((b = is.read()) != -1) {
+ try {
+ os.write(b);
+ if (b == 10 /** flush buffer on line feed */) {
+ os.flush();
+ }
+ } catch (Exception e) {
+ if (e.getClass().getSimpleName().equalsIgnoreCase("ClientAbortException")) {
+ // don't throw an exception as this means the user closed the connection
+ logger.debug("Connection closed by client. Will stop proxying ...");
+ // break out of the while loop
+ break;
+ } else {
+ // received unknown error while writing so throw an exception
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Error proxying request: " + url, e);
+ } finally {
+ if (httpget != null) {
+ try {
+ httpget.abort();
+ } catch (Exception e) {
+ logger.error("failed aborting proxy connection.", e);
+ }
+ }
+
+ // httpget.abort() MUST be called first otherwise is.close() hangs (because data is still streaming?)
+ if (is != null) {
+ // this should already be closed by httpget.abort() above
+ try {
+ is.close();
+ } catch (Exception e) {
+ // e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private static class ProxyConnectionManager {
+ private final static PoolingClientConnectionManager threadSafeConnectionManager = new PoolingClientConnectionManager();
+ private final static HttpClient httpClient = new DefaultHttpClient(threadSafeConnectionManager);
+
+ static {
+ logger.debug("Initialize ProxyConnectionManager");
+ /* common settings */
+ HttpParams httpParams = httpClient.getParams();
+ HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
+ HttpConnectionParams.setSoTimeout(httpParams, 10000);
+
+ /* number of connections to allow */
+ threadSafeConnectionManager.setDefaultMaxPerRoute(400);
+ threadSafeConnectionManager.setMaxTotal(400);
+ }
+ }
+ }
+}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/endpoint/HystrixStreamEndpoint.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixStreamEndpoint.java
similarity index 74%
rename from spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/endpoint/HystrixStreamEndpoint.java
rename to spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixStreamEndpoint.java
index ac279c91..9c0aa3ca 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/endpoint/HystrixStreamEndpoint.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/HystrixStreamEndpoint.java
@@ -1,4 +1,6 @@
-package org.springframework.cloud.netflix.endpoint;
+package org.springframework.cloud.netflix.hystrix;
+
+import org.springframework.cloud.netflix.endpoint.ServletWrappingEndpoint;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/annotations/EnableHystrixDashboard.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/annotations/EnableHystrixDashboard.java
new file mode 100644
index 00000000..3537346b
--- /dev/null
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/annotations/EnableHystrixDashboard.java
@@ -0,0 +1,20 @@
+package org.springframework.cloud.netflix.hystrix.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.cloud.netflix.hystrix.HystrixDashboardConfiguration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * Created by sgibb on 6/19/14.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(HystrixDashboardConfiguration.class)
+public @interface EnableHystrixDashboard {
+}
\ No newline at end of file
diff --git a/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/Application.java b/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/HystrixDashboardApplication.java
similarity index 69%
rename from spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/Application.java
rename to spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/HystrixDashboardApplication.java
index 82f21cec..57d0cb51 100644
--- a/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/Application.java
+++ b/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/HystrixDashboardApplication.java
@@ -1,11 +1,12 @@
package io.spring.cloud.netflix.hystrix;
import io.spring.cloud.netflix.hystrix.stream.MockStreamServlet;
-import io.spring.cloud.netflix.hystrix.stream.ProxyStreamServlet;
+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.web.SpringBootServletInitializer;
+import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -15,16 +16,17 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@ComponentScan
+@EnableHystrixDashboard
@EnableAutoConfiguration
-public class Application extends SpringBootServletInitializer {
+public class HystrixDashboardApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
- return application.sources(Application.class).web(true);
+ return application.sources(HystrixDashboardApplication.class).web(true);
}
public static void main(String[] args) {
- new SpringApplicationBuilder(Application.class).web(true).run(args);
+ new SpringApplicationBuilder(HystrixDashboardApplication.class).web(true).run(args);
}
@Bean
@@ -32,8 +34,4 @@ public class Application extends SpringBootServletInitializer {
return new ServletRegistrationBean(new MockStreamServlet(), "/mock.stream");
}
- @Bean
- public ServletRegistrationBean proxyStreamServlet() {
- return new ServletRegistrationBean(new ProxyStreamServlet(), "/proxy.stream");
- }
}
diff --git a/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/stream/ProxyStreamServlet.java b/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/stream/ProxyStreamServlet.java
deleted file mode 100644
index 6bd0e9e2..00000000
--- a/spring-cloud-netflix-hystrix/src/main/java/io/spring/cloud/netflix/hystrix/stream/ProxyStreamServlet.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package io.spring.cloud.netflix.hystrix.stream;
-
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.PoolingClientConnectionManager;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Map;
-
-/**
- * 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.
- */
-public class ProxyStreamServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- private static final Logger logger = LoggerFactory.getLogger(ProxyStreamServlet.class);
-
- public ProxyStreamServlet() {
- super();
- }
-
- /**
- * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
- */
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- String origin = request.getParameter("origin");
- if (origin == null) {
- response.setStatus(500);
- response.getWriter().println("Required parameter 'origin' missing. Example: 107.20.175.135:7001");
- }
- origin = origin.trim();
-
- HttpGet httpget = null;
- InputStream is = null;
- boolean hasFirstParameter = false;
- StringBuilder url = new StringBuilder();
- if (!origin.startsWith("http")) {
- url.append("http://");
- }
- url.append(origin);
- if (origin.contains("?")) {
- hasFirstParameter = true;
- }
- @SuppressWarnings("unchecked")
- Map params = request.getParameterMap();
- for (String key : params.keySet()) {
- if (!key.equals("origin")) {
- String[] values = params.get(key);
- String value = values[0].trim();
- if (hasFirstParameter) {
- url.append("&");
- } else {
- url.append("?");
- hasFirstParameter = true;
- }
- url.append(key).append("=").append(value);
- }
- }
- String proxyUrl = url.toString();
- logger.info("\n\nProxy opening connection to: " + proxyUrl + "\n\n");
- try {
- httpget = new HttpGet(proxyUrl);
- HttpClient client = ProxyConnectionManager.httpClient;
- HttpResponse httpResponse = client.execute(httpget);
- int statusCode = httpResponse.getStatusLine().getStatusCode();
- if (statusCode == HttpStatus.SC_OK) {
- // writeTo swallows exceptions and never quits even if outputstream is throwing IOExceptions (such as broken pipe) ... since the inputstream is infinite
- // httpResponse.getEntity().writeTo(new OutputStreamWrapper(response.getOutputStream()));
- // so I copy it manually ...
- is = httpResponse.getEntity().getContent();
-
- // set headers
- for (Header header : httpResponse.getAllHeaders()) {
- response.addHeader(header.getName(), header.getValue());
- }
-
- // copy data from source to response
- OutputStream os = response.getOutputStream();
- int b = -1;
- while ((b = is.read()) != -1) {
- try {
- os.write(b);
- if (b == 10 /** flush buffer on line feed */) {
- os.flush();
- }
- } catch (Exception e) {
- if (e.getClass().getSimpleName().equalsIgnoreCase("ClientAbortException")) {
- // don't throw an exception as this means the user closed the connection
- logger.debug("Connection closed by client. Will stop proxying ...");
- // break out of the while loop
- break;
- } else {
- // received unknown error while writing so throw an exception
- throw new RuntimeException(e);
- }
- }
- }
- }
- } catch (Exception e) {
- logger.error("Error proxying request: " + url, e);
- } finally {
- if (httpget != null) {
- try {
- httpget.abort();
- } catch (Exception e) {
- logger.error("failed aborting proxy connection.", e);
- }
- }
-
- // httpget.abort() MUST be called first otherwise is.close() hangs (because data is still streaming?)
- if (is != null) {
- // this should already be closed by httpget.abort() above
- try {
- is.close();
- } catch (Exception e) {
- // e.printStackTrace();
- }
- }
- }
- }
-
- private static class ProxyConnectionManager {
- private final static PoolingClientConnectionManager threadSafeConnectionManager = new PoolingClientConnectionManager();
- private final static HttpClient httpClient = new DefaultHttpClient(threadSafeConnectionManager);
-
- static {
- logger.debug("Initialize ProxyConnectionManager");
- /* common settings */
- HttpParams httpParams = httpClient.getParams();
- HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
- HttpConnectionParams.setSoTimeout(httpParams, 10000);
-
- /* number of connections to allow */
- threadSafeConnectionManager.setDefaultMaxPerRoute(400);
- threadSafeConnectionManager.setMaxTotal(400);
- }
- }
-}
diff --git a/spring-cloud-netflix-hystrix/src/main/resources/application.yml b/spring-cloud-netflix-hystrix/src/main/resources/application.yml
index 0510b269..a491a9e4 100644
--- a/spring-cloud-netflix-hystrix/src/main/resources/application.yml
+++ b/spring-cloud-netflix-hystrix/src/main/resources/application.yml
@@ -9,7 +9,6 @@ endpoints:
server:
port: 7979
- context-path: /hystrix-dashboard
logging:
level: INFO
diff --git a/spring-cloud-netflix-zuul/src/main/java/io/spring/cloud/netflix/zuul/Application.java b/spring-cloud-netflix-zuul/src/main/java/io/spring/cloud/netflix/zuul/Application.java
index d796c862..fbea9781 100644
--- a/spring-cloud-netflix-zuul/src/main/java/io/spring/cloud/netflix/zuul/Application.java
+++ b/spring-cloud-netflix-zuul/src/main/java/io/spring/cloud/netflix/zuul/Application.java
@@ -1,13 +1,14 @@
package io.spring.cloud.netflix.zuul;
import com.netflix.zuul.context.ContextLifecycleFilter;
+
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
-import org.springframework.cloud.netflix.endpoint.HystrixStreamEndpoint;
+import org.springframework.cloud.netflix.hystrix.HystrixStreamEndpoint;
import org.springframework.cloud.netflix.zuul.Routes;
import org.springframework.scheduling.annotation.EnableScheduling;