From 38140c3564a123b6438ce7c2c66a99f3289f3669 Mon Sep 17 00:00:00 2001 From: Teerat Pitakrat Date: Sun, 29 Mar 2015 15:25:25 +0200 Subject: [PATCH 1/6] Upgraded jersey to 1.19 --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 08d0f77..fd878d6 100644 --- a/build.gradle +++ b/build.gradle @@ -34,10 +34,10 @@ subprojects { compile 'com.netflix.netflix-commons:netflix-commons-util:0.1.1' compile 'javax.ws.rs:jsr311-api:1.1.1' compile 'commons-collections:commons-collections:3.2.1' - compile 'com.sun.jersey:jersey-server:1.13' - compile 'com.sun.jersey:jersey-core:1.13' - compile 'com.sun.jersey:jersey-bundle:1.13' - compile 'com.sun.jersey.contribs:jersey-guice:1.13' + compile 'com.sun.jersey:jersey-server:1.19' + compile 'com.sun.jersey:jersey-core:1.19' + compile 'com.sun.jersey:jersey-bundle:1.19' + compile 'com.sun.jersey.contribs:jersey-guice:1.19' compile 'com.netflix.eureka:eureka-client:1.1.73' compile 'com.netflix.blitz4j:blitz4j:1.21' compile 'jetty:jetty:6.0.2' @@ -79,7 +79,7 @@ project(':rss-middletier') { compile 'asm:asm-all:3.2' compile 'commons-io:commons-io:2.4' compile 'commons-configuration:commons-configuration:1.9' - compile 'com.sun.jersey:jersey-server:1.9.1' + compile 'com.sun.jersey:jersey-server:1.19' } jar { From 591f7f0a91f7d1f63bbfde5cc7697ff24156d5d6 Mon Sep 17 00:00:00 2001 From: Teerat Pitakrat Date: Tue, 31 Mar 2015 13:02:08 +0200 Subject: [PATCH 2/6] Upgraded netty to 4.0.26.Final --- build.gradle | 3 +- .../rss/netty/NettyHandlerContainer.java | 81 ++++---- .../recipes/rss/netty/NettyServer.java | 175 +++++------------- .../recipes/rss/server/BaseNettyServer.java | 2 +- .../jersey/resources/MiddleTierResource.java | 2 +- 5 files changed, 96 insertions(+), 167 deletions(-) diff --git a/build.gradle b/build.gradle index fd878d6..41921bb 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ subprojects { dependencies { compile 'org.slf4j:slf4j-api:1.7.2' - compile 'io.netty:netty:3.6.1.Final' + compile 'io.netty:netty-all:4.0.26.Final' compile 'com.netflix.servo:servo-core:0.4.32' compile 'com.google.guava:guava:11.0.2' compile 'com.google.inject:guice:3.0' @@ -64,6 +64,7 @@ subprojects { project(':rss-core') { apply plugin: 'java' + apply plugin : 'eclipse' dependencies { compile 'com.netflix.netflix-commons:netflix-statistics:0.1.1' } diff --git a/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyHandlerContainer.java b/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyHandlerContainer.java index cd9133b..638ef5d 100755 --- a/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyHandlerContainer.java +++ b/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyHandlerContainer.java @@ -35,23 +35,33 @@ import com.sun.jersey.spi.container.ContainerResponse; import com.sun.jersey.spi.container.ContainerResponseWriter; import com.sun.jersey.spi.container.WebApplication; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBufferInputStream; -import org.jboss.netty.buffer.ChannelBufferOutputStream; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.*; -import org.jboss.netty.channel.ChannelHandler.Sharable; -import org.jboss.netty.handler.codec.http.*; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; import java.io.IOException; import java.io.OutputStream; import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; @Sharable -public class NettyHandlerContainer extends SimpleChannelUpstreamHandler { +public class NettyHandlerContainer extends ChannelInboundHandlerAdapter { public static final String PROPERTY_BASE_URI = "com.sun.jersey.server.impl.container.netty.baseUri"; private final WebApplication application; @@ -63,60 +73,61 @@ public class NettyHandlerContainer extends SimpleChannelUpstreamHandler { } private final static class Writer implements ContainerResponseWriter { - private final Channel channel; - private HttpResponse response; + private final ChannelHandlerContext ctx; + private DefaultFullHttpResponse response; - private Writer(Channel channel) { - this.channel = channel; + private Writer(ChannelHandlerContext ctx) { + this.ctx = ctx; } public OutputStream writeStatusAndHeaders(long contentLength, ContainerResponse cResponse) throws IOException { - - response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.valueOf(cResponse.getStatus())); + ByteBuf buffer = Unpooled.buffer(); + response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.valueOf(cResponse + .getStatus()), buffer); for (Map.Entry> e : cResponse.getHttpHeaders().entrySet()) { List values = new ArrayList(); for (Object v : e.getValue()) values.add(ContainerResponse.getHeaderValue(v)); - response.setHeader(e.getKey(), values); + response.headers().set(e.getKey(), values); } - ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(); - response.setContent(buffer); - return new ChannelBufferOutputStream(buffer); + return new ByteBufOutputStream(buffer); } public void finish() throws IOException { // Streaming is not supported. Entire response will be written // downstream once finish() is called. - channel.write(response).addListener(ChannelFutureListener.CLOSE); + final ChannelFuture f = ctx.writeAndFlush(response); + f.addListener(ChannelFutureListener.CLOSE); } } @Override - public void messageReceived(ChannelHandlerContext context, MessageEvent e) throws Exception { - HttpRequest request = (HttpRequest) e.getMessage(); - String base = getBaseUri(request); - URI baseUri = new URI(base); - URI requestUri = new URI(base.substring(0, base.length() - 1) + request.getUri()); - ContainerRequest cRequest = new ContainerRequest(application, request - .getMethod().getName(), baseUri, requestUri, - getHeaders(request), new ChannelBufferInputStream( - request.getContent())); - application.handleRequest(cRequest, new Writer(e.getChannel())); + public void channelRead(ChannelHandlerContext ctx, Object msg) throws URISyntaxException, IOException { + if (msg instanceof FullHttpRequest) { + FullHttpRequest httpRequest = (FullHttpRequest) msg; + String base = getBaseUri(httpRequest); + URI baseUri = new URI(base); + URI requestUri = new URI(base.substring(0, base.length() - 1) + httpRequest.getUri()); + ContainerRequest cRequest = new ContainerRequest(application, httpRequest.getMethod().name(), baseUri, + requestUri, getHeaders(httpRequest), new ByteBufInputStream(httpRequest.content())); + application.handleRequest(cRequest, new Writer(ctx)); + } } - private String getBaseUri(HttpRequest request) { + private String getBaseUri(HttpMessage httpMessage) { if (baseUri != null) { return baseUri; } - - return "http://" + request.getHeader(HttpHeaders.Names.HOST) + "/"; + return "http://" + HttpHeaders.getHost(httpMessage) + "/"; } - private InBoundHeaders getHeaders(HttpRequest request) { + private InBoundHeaders getHeaders(FullHttpRequest httpRequest) { InBoundHeaders headers = new InBoundHeaders(); - for (String name : request.getHeaderNames()) { - headers.put(name, request.getHeaders(name)); + HttpHeaders httpHeaders = httpRequest.headers(); + for (String name : httpHeaders.names()) { + headers.put(name, httpHeaders.getAll(name)); } return headers; } + } diff --git a/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java b/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java index 43f4afd..f518c6a 100755 --- a/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java +++ b/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java @@ -17,28 +17,33 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Maps; -import com.netflix.recipes.rss.util.DescriptiveThreadFactory; -import org.jboss.netty.bootstrap.ServerBootstrap; -import org.jboss.netty.channel.*; -import org.jboss.netty.channel.group.ChannelGroup; -import org.jboss.netty.channel.group.DefaultChannelGroup; -import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; -import org.jboss.netty.handler.codec.http.HttpRequestDecoder; -import org.jboss.netty.handler.codec.http.HttpResponseEncoder; -import org.jboss.netty.handler.execution.ExecutionHandler; -import org.jboss.netty.logging.InternalLoggerFactory; -import org.jboss.netty.logging.Slf4JLoggerFactory; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.Slf4JLoggerFactory; + import java.io.Closeable; import java.lang.Thread.UncaughtExceptionHandler; import java.net.InetSocketAddress; import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; /** * NettyServer and Builder @@ -50,7 +55,7 @@ public final class NettyServer implements Closeable { public static final int cpus = Runtime.getRuntime().availableProcessors(); - private ChannelGroup channelGroup = new DefaultChannelGroup(); + private ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); static { InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory()); @@ -66,14 +71,6 @@ public void uncaughtException(Thread thread, Throwable exc) { }); } - public String getListenHost() { - return ((InetSocketAddress) channelGroup.find(1).getLocalAddress()).getHostName(); - } - - public int getListenPort() { - return ((InetSocketAddress) channelGroup.find(1).getLocalAddress()).getPort(); - } - public void addChannel(Channel channel) { channelGroup.add(channel); } @@ -91,9 +88,6 @@ public static class Builder { private Map handlers = Maps.newHashMap(); - private ChannelHandler encoder = new HttpResponseEncoder(); - private ChannelHandler decoder = new HttpRequestDecoder(); - private int numBossThreads = cpus; // IO boss threads private int numWorkerThreads = cpus * 4; // worker threads @@ -113,16 +107,6 @@ public Builder addHandler(String name, ChannelHandler handler) { return this; } - public Builder encoder(ChannelHandler encoder) { - this.encoder = encoder; - return this; - } - - public Builder decoder(ChannelHandler decoder) { - this.decoder = decoder; - return this; - } - public Builder numBossThreads(int numBossThreads) { this.numBossThreads = numBossThreads; return this; @@ -136,100 +120,33 @@ public Builder numWorkerThreads(int numWorkerThreads) { /** * Builds and starts netty */ - public NettyServer build() { - PipelineFactory factory = new PipelineFactory(handlers, encoder, - decoder, numBossThreads); - - ThreadPoolExecutor bossPool = new ThreadPoolExecutor( - numBossThreads, numBossThreads, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(), - new DescriptiveThreadFactory("Boss-Thread")); - - ThreadPoolExecutor workerPool = new ThreadPoolExecutor( - numWorkerThreads, numWorkerThreads, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(), - new DescriptiveThreadFactory("Worker-Thread")); - - ChannelFactory nioServer = new NioServerSocketChannelFactory( - bossPool, workerPool, numWorkerThreads); - - ServerBootstrap serverBootstrap = new ServerBootstrap(nioServer); - serverBootstrap.setOption("reuseAddress", true); - serverBootstrap.setOption("keepAlive", true); - serverBootstrap.setPipelineFactory(factory); - - Channel serverChannel = serverBootstrap.bind(new InetSocketAddress( - host, port)); + public ChannelFuture build() { + EventLoopGroup bossGroup = new NioEventLoopGroup(this.numBossThreads); + EventLoopGroup workerGroup = new NioEventLoopGroup(this.numWorkerThreads); + + ServerBootstrap serverBootstrap = new ServerBootstrap(); + + serverBootstrap.option(ChannelOption.SO_REUSEADDR, true) + .option(ChannelOption.SO_KEEPALIVE, true) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new HttpResponseEncoder()); + ch.pipeline().addLast(new HttpRequestDecoder()); + ch.pipeline().addLast(new HttpObjectAggregator(1048576)); + for (String name : handlers.keySet()) { + ch.pipeline().addLast(handlers.get(name)); + } + } + }); + + ChannelFuture serverChannelFuture = serverBootstrap.bind(new InetSocketAddress(host, port)); logger.info("Started netty server {}:{}", host, port); - NettyServer server = new NettyServer(); - server.addChannel(serverChannel); - - return server; - } - } - - public static class PipelineFactory implements ChannelPipelineFactory { - static final String CHANNEL_HANDLERS = "channelHandlers"; - static final String ENCODER_NAME = "encoder"; - static final String DECODER_NAME = "decoder"; - - final ChannelHandler executionHandler; - final Map handlers; - - final ChannelHandler encoder; - final ChannelHandler decoder; - - public PipelineFactory(Map handlers, - ChannelHandler encoder, ChannelHandler decoder, int numThreads) { - - this.handlers = handlers; - this.encoder = encoder; - this.decoder = decoder; - - if (numThreads != 0) { - ThreadPoolExecutor executorThreadPool = new ThreadPoolExecutor( - NettyServer.cpus, NettyServer.cpus * 4, 60, - TimeUnit.SECONDS, new LinkedBlockingQueue(), - new DescriptiveThreadFactory("Executor-Thread")); - - this.executionHandler = new ExecutionHandler(executorThreadPool); - } else { - this.executionHandler = null; - } - } - - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - pipeline.addLast("executionHandler", executionHandler); - pipeline.addLast(DECODER_NAME, decoder); - pipeline.addLast(ENCODER_NAME, encoder); - - for (Entry handler : handlers.entrySet()) { - pipeline.addLast(handler.getKey(), handler.getValue()); - } - - return pipeline; - } - } - - public static class ClientPipelineFactory extends PipelineFactory { - public ClientPipelineFactory(Map handlers, - ChannelHandler encoder, ChannelHandler decoder) { - - super(handlers, encoder, decoder, 0); - } - - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - pipeline.addLast(DECODER_NAME, decoder); - pipeline.addLast(ENCODER_NAME, encoder); - - for (Entry handler : handlers.entrySet()) { - pipeline.addLast(handler.getKey(), handler.getValue()); - } - - return pipeline; + return serverChannelFuture; } } diff --git a/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java b/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java index 3abb5e1..2a6c6f1 100755 --- a/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java +++ b/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java @@ -62,7 +62,7 @@ public void start() { final PackagesResourceConfig rcf = new PackagesResourceConfig(ConfigurationManager.getConfigInstance().getString("jersey.resources.package","not-found-in-configuration")); - nettyServer = NettyServer + NettyServer .builder() .host(host) .port(port) diff --git a/rss-middletier/src/main/java/com/netflix/recipes/rss/jersey/resources/MiddleTierResource.java b/rss-middletier/src/main/java/com/netflix/recipes/rss/jersey/resources/MiddleTierResource.java index 0ea79bc..d17e18f 100755 --- a/rss-middletier/src/main/java/com/netflix/recipes/rss/jersey/resources/MiddleTierResource.java +++ b/rss-middletier/src/main/java/com/netflix/recipes/rss/jersey/resources/MiddleTierResource.java @@ -74,7 +74,7 @@ public MiddleTierResource() { @Path("/rss/user/{user}") @Produces({MediaType.APPLICATION_JSON}) public Response fetchSubscriptions (final @PathParam("user") String user) { - + // Start timer Stopwatch stopwatch = getRSSStatsTimer.start(); From cc03f01413ed89145bcf6b6c722e7316e86c0c68 Mon Sep 17 00:00:00 2001 From: Teerat Pitakrat Date: Tue, 14 Apr 2015 21:51:00 +0200 Subject: [PATCH 3/6] Cleaned up unused code --- .../netflix/recipes/rss/netty/NettyServer.java | 16 +++------------- .../recipes/rss/server/BaseNettyServer.java | 7 ++++--- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java b/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java index f518c6a..0497a49 100755 --- a/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java +++ b/rss-core/src/main/java/com/netflix/recipes/rss/netty/NettyServer.java @@ -22,21 +22,17 @@ import org.slf4j.LoggerFactory; import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.Slf4JLoggerFactory; @@ -55,8 +51,6 @@ public final class NettyServer implements Closeable { public static final int cpus = Runtime.getRuntime().availableProcessors(); - private ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - static { InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory()); @@ -71,10 +65,6 @@ public void uncaughtException(Thread thread, Throwable exc) { }); } - public void addChannel(Channel channel) { - channelGroup.add(channel); - } - /** * @return Builder object which will help build the client and server */ @@ -123,14 +113,15 @@ public Builder numWorkerThreads(int numWorkerThreads) { public ChannelFuture build() { EventLoopGroup bossGroup = new NioEventLoopGroup(this.numBossThreads); EventLoopGroup workerGroup = new NioEventLoopGroup(this.numWorkerThreads); - + ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.option(ChannelOption.SO_REUSEADDR, true) .option(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.SO_KEEPALIVE, true); - serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) throws Exception { @@ -151,7 +142,6 @@ protected void initChannel(SocketChannel ch) throws Exception { } public void close() { - channelGroup.close(); } private NettyServer() { diff --git a/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java b/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java index 2a6c6f1..f2b7e97 100755 --- a/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java +++ b/rss-core/src/main/java/com/netflix/recipes/rss/server/BaseNettyServer.java @@ -15,6 +15,8 @@ */ package com.netflix.recipes.rss.server; +import io.netty.channel.ChannelFuture; + import java.io.Closeable; import com.google.common.io.Closeables; @@ -36,7 +38,7 @@ public class BaseNettyServer implements Closeable { LoggingConfiguration.getInstance().configure(); } - public NettyServer nettyServer; + public ChannelFuture nettyServerFuture; public final KaryonServer karyonServer; public String host; @@ -62,7 +64,7 @@ public void start() { final PackagesResourceConfig rcf = new PackagesResourceConfig(ConfigurationManager.getConfigInstance().getString("jersey.resources.package","not-found-in-configuration")); - NettyServer + nettyServerFuture = NettyServer .builder() .host(host) .port(port) @@ -80,7 +82,6 @@ public void start() { } public void close() { - Closeables.closeQuietly(nettyServer); Closeables.closeQuietly(karyonServer); LoggingConfiguration.getInstance().stop(); } From 3c2401519952510bf57f8aef5a2a7c727167e9e9 Mon Sep 17 00:00:00 2001 From: Teerat Pitakrat Date: Tue, 14 Apr 2015 21:57:58 +0200 Subject: [PATCH 4/6] Added multi-user support for edge --- .../recipes/rss/hystrix/AddRSSCommand.java | 37 +++++++++++++--- .../recipes/rss/hystrix/DeleteRSSCommand.java | 40 ++++++++++++++--- .../recipes/rss/hystrix/GetRSSCommand.java | 44 +++++++++++++++---- rss-edge/webapp/jsp/rss.jsp | 43 +++++++++++++++--- 4 files changed, 135 insertions(+), 29 deletions(-) diff --git a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java index 15d56a5..d5802c3 100755 --- a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java +++ b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java @@ -35,6 +35,7 @@ */ public class AddRSSCommand extends HystrixCommand { + private String username = null; // RSS Feed Url (encoded) private final String url; @@ -49,6 +50,18 @@ public AddRSSCommand(String url) { this.url = url; } + public AddRSSCommand(String username, String url) { + super ( + Setter.withGroupKey( + HystrixCommandGroupKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_MUTATIONS_GROUP)) + .andCommandKey(HystrixCommandKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_ADD_COMMAND_KEY)) + .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_THREAD_POOL) + ) + ); + this.username = username; + this.url = url; + } + @Override protected String run() { try { @@ -58,14 +71,26 @@ protected String run() { */ RestClient client = (RestClient) ClientFactory.getNamedClient(RSSConstants.MIDDLETIER_REST_CLIENT); - HttpClientRequest request = HttpClientRequest + HttpClientRequest request; + if (this.username == null) { + request = HttpClientRequest + .newBuilder() + .setVerb(Verb.POST) + .setUri(new URI("/" + + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH + + RSSConstants.RSS_ENTRY_POINT + + "?url=" + url)) + .build(); + } else { + request = HttpClientRequest .newBuilder() .setVerb(Verb.POST) .setUri(new URI("/" - + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH - + RSSConstants.RSS_ENTRY_POINT - + "?url=" + url)) + + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH + + RSSConstants.RSS_ENTRY_POINT.replaceAll("rss/user/default","rss/user/"+this.username) + + "?url=" + url)) .build(); + } HttpClientResponse response = client.executeWithLoadBalancer(request); return IOUtils.toString(response.getRawEntity(), Charsets.UTF_8); @@ -76,7 +101,7 @@ protected String run() { @Override protected String getFallback() { - // Empty json + // Empty json return "{}"; } -} \ No newline at end of file +} diff --git a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java index 0e6b8fc..66df977 100755 --- a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java +++ b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java @@ -35,6 +35,7 @@ */ public class DeleteRSSCommand extends HystrixCommand { + private String username = null; // RSS Feed Url (encoded) private final String url; @@ -49,6 +50,18 @@ public DeleteRSSCommand(String url) { this.url = url; } + public DeleteRSSCommand(String username, String url) { + super ( + Setter.withGroupKey( + HystrixCommandGroupKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_MUTATIONS_GROUP)) + .andCommandKey(HystrixCommandKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_DEL_COMMAND_KEY)) + .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_THREAD_POOL) + ) + ); + this.username = username; + this.url = url; + } + @Override protected String run() { try { @@ -56,15 +69,28 @@ protected String run() { // configuration specified in the edge.properties file RestClient client = (RestClient) ClientFactory.getNamedClient(RSSConstants.MIDDLETIER_REST_CLIENT); - HttpClientRequest request = HttpClientRequest + HttpClientRequest request; + if (this.username == null) { + request = HttpClientRequest + .newBuilder() + .setVerb(Verb.DELETE) + .setUri(new URI("/" + + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH + + RSSConstants.RSS_ENTRY_POINT + + "?url=" + url) + ) + .build(); + } else { + request = HttpClientRequest .newBuilder() .setVerb(Verb.DELETE) .setUri(new URI("/" - + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH - + RSSConstants.RSS_ENTRY_POINT - + "?url=" + url) - ) + + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH + + RSSConstants.RSS_ENTRY_POINT.replaceAll("rss/user/default","rss/user/"+this.username) + + "?url=" + url) + ) .build(); + } HttpClientResponse response = client.executeWithLoadBalancer(request); return IOUtils.toString(response.getRawEntity(), Charsets.UTF_8); @@ -75,7 +101,7 @@ protected String run() { @Override protected String getFallback() { - // Empty json + // Empty json return "{}"; } -} \ No newline at end of file +} diff --git a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java index 2684743..a84fc10 100755 --- a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java +++ b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java @@ -34,6 +34,9 @@ * Calls the middle tier Get RSS entry point */ public class GetRSSCommand extends HystrixCommand { + + private String username = null; + public GetRSSCommand() { super ( Setter.withGroupKey( @@ -44,6 +47,17 @@ public GetRSSCommand() { ); } + public GetRSSCommand(String username) { + super ( + Setter.withGroupKey( + HystrixCommandGroupKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_GET_GROUP)) + .andCommandKey(HystrixCommandKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_GET_COMMAND_KEY)) + .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_THREAD_POOL) + ) + ); + this.username = username; + } + @Override protected String run() { try { @@ -51,25 +65,37 @@ protected String run() { // configuration specified in the edge.properties file RestClient client = (RestClient) ClientFactory.getNamedClient(RSSConstants.MIDDLETIER_REST_CLIENT); - HttpClientRequest request = HttpClientRequest + HttpClientRequest request; + if (this.username == null) { + request = HttpClientRequest .newBuilder() .setVerb(Verb.GET) .setUri(new URI("/" - + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH - + RSSConstants.RSS_ENTRY_POINT) - ) + + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH + + RSSConstants.RSS_ENTRY_POINT) + ) .build(); + } else { + request = HttpClientRequest + .newBuilder() + .setVerb(Verb.GET) + .setUri(new URI("/" + + RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH + + RSSConstants.RSS_ENTRY_POINT.replaceAll("rss/user/default","rss/user/"+this.username)) + ) + .build(); + } HttpClientResponse response = client.executeWithLoadBalancer(request); return IOUtils.toString(response.getRawEntity(), Charsets.UTF_8); - } catch (Exception exc) { - throw new RuntimeException("Exception", exc); - } + } catch (Exception exc) { + throw new RuntimeException("Exception", exc); + } } @Override protected String getFallback() { - // Empty json + // Empty json return "{}"; } -} \ No newline at end of file +} diff --git a/rss-edge/webapp/jsp/rss.jsp b/rss-edge/webapp/jsp/rss.jsp index 82cdddd..27e58b1 100644 --- a/rss-edge/webapp/jsp/rss.jsp +++ b/rss-edge/webapp/jsp/rss.jsp @@ -71,28 +71,51 @@ <% // Delete a RSS feed + String username = request.getParameter("username"); String delFeedUrl = request.getParameter("delFeedUrl"); if (delFeedUrl != null) { - HystrixCommand deleteCommand = new DeleteRSSCommand(delFeedUrl); + HystrixCommand deleteCommand; + if (username == null) { + deleteCommand = new DeleteRSSCommand(delFeedUrl); + username = "default"; + } else { + deleteCommand = new DeleteRSSCommand(username,delFeedUrl); + } Future future = deleteCommand.queue(); String responseString = future.get(); - response.sendRedirect("/jsp/rss.jsp"); + response.sendRedirect("/jsp/rss.jsp?username="+username); } // Add a RSS feed String url = request.getParameter("url"); if (url != null) { + HystrixCommand addCommand; url = URLEncoder.encode(url, "UTF-8"); - HystrixCommand addCommand = new AddRSSCommand(url); + if (username == null) { + addCommand = new AddRSSCommand(url); + username = "default"; + } else { + addCommand = new AddRSSCommand(username,url); + } Future future = addCommand.queue(); String responseString = future.get(); - response.sendRedirect("/jsp/rss.jsp"); + response.sendRedirect("/jsp/rss.jsp?username="+username); } // Get RSS feeds - HystrixCommand getCommand = new GetRSSCommand(); + HystrixCommand getCommand; + if (username == null) { + getCommand = new GetRSSCommand(); + } else { + getCommand = new GetRSSCommand(username); + } Future future = getCommand.queue(); String responseString = future.get(); + boolean errorOccurred = false; + if (responseString.equals("An error occurred")) { + errorOccurred = true; + responseString = "{}"; + } // When a user has only 1 subcription, middle tier returns a jsonobject instead of an array final JSONObject jo = new JSONObject(responseString); @@ -103,7 +126,7 @@ } else { subscriptions = jo.getJSONArray("subscriptions"); } - } + } // Compute the number of rows int numFeeds = subscriptions.length(); @@ -119,7 +142,13 @@ visibility:visible; } - +<% +if (errorOccurred) { +%> + An error occurred. +<% +} +%>
<% int index = 0; From d8816eb80cd4a56d35c203c9ad34be7762d7c613 Mon Sep 17 00:00:00 2001 From: Teerat Pitakrat Date: Wed, 16 Sep 2015 11:29:17 +0200 Subject: [PATCH 5/6] Added healthcheck.jsp page for edge --- rss-edge/webapp/jsp/healthcheck.jsp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 rss-edge/webapp/jsp/healthcheck.jsp diff --git a/rss-edge/webapp/jsp/healthcheck.jsp b/rss-edge/webapp/jsp/healthcheck.jsp new file mode 100644 index 0000000..cb076ff --- /dev/null +++ b/rss-edge/webapp/jsp/healthcheck.jsp @@ -0,0 +1,17 @@ + + + Edge health check page + + + + + + + + +
+

Edge health check page

+
+ + + From 36bb75991cc9700c59b6721f3363e5f29da878dd Mon Sep 17 00:00:00 2001 From: Teerat Pitakrat Date: Wed, 30 Sep 2015 16:01:02 +0200 Subject: [PATCH 6/6] Return 500 to user if an error occurred internally --- .../recipes/rss/hystrix/AddRSSCommand.java | 3 +-- .../recipes/rss/hystrix/DeleteRSSCommand.java | 3 +-- .../recipes/rss/hystrix/GetRSSCommand.java | 3 +-- rss-edge/webapp/jsp/rss.jsp | 20 +++++++++---------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java index d5802c3..fac1041 100755 --- a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java +++ b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/AddRSSCommand.java @@ -101,7 +101,6 @@ protected String run() { @Override protected String getFallback() { - // Empty json - return "{}"; + return "An error occurred while adding feed"; } } diff --git a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java index 66df977..550ca61 100755 --- a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java +++ b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/DeleteRSSCommand.java @@ -101,7 +101,6 @@ protected String run() { @Override protected String getFallback() { - // Empty json - return "{}"; + return "An error occurred while deleting feed"; } } diff --git a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java index a84fc10..33e710c 100755 --- a/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java +++ b/rss-edge/src/main/java/com/netflix/recipes/rss/hystrix/GetRSSCommand.java @@ -95,7 +95,6 @@ protected String run() { @Override protected String getFallback() { - // Empty json - return "{}"; + return "An error occurred while getting feed"; } } diff --git a/rss-edge/webapp/jsp/rss.jsp b/rss-edge/webapp/jsp/rss.jsp index 27e58b1..03d437e 100644 --- a/rss-edge/webapp/jsp/rss.jsp +++ b/rss-edge/webapp/jsp/rss.jsp @@ -83,6 +83,9 @@ } Future future = deleteCommand.queue(); String responseString = future.get(); + if (responseString.equals("An error occurred while deleting feed")) { + response.sendError(500, "An error occurred while deleting feed"); + } response.sendRedirect("/jsp/rss.jsp?username="+username); } @@ -99,6 +102,9 @@ } Future future = addCommand.queue(); String responseString = future.get(); + if (responseString.equals("An error occurred while adding feed")) { + response.sendError(500, "An error occurred while adding feed"); + } response.sendRedirect("/jsp/rss.jsp?username="+username); } @@ -111,10 +117,8 @@ } Future future = getCommand.queue(); String responseString = future.get(); - boolean errorOccurred = false; - if (responseString.equals("An error occurred")) { - errorOccurred = true; - responseString = "{}"; + if (responseString.equals("An error occurred while getting feed")) { + response.sendError(500, "An error occurred while getting feed"); } // When a user has only 1 subcription, middle tier returns a jsonobject instead of an array @@ -142,13 +146,7 @@ visibility:visible; } -<% -if (errorOccurred) { -%> - An error occurred. -<% -} -%> +
<% int index = 0;