com.netflix.ribbon.examples.rx.RxMovieServer Maven / Gradle / Ivy
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbon.examples.rx;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.netflix.ribbon.examples.rx.common.Movie;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.logging.LogLevel;
import io.netty.util.internal.ConcurrentSet;
import io.reactivex.netty.RxNetty;
import io.reactivex.netty.pipeline.PipelineConfigurators;
import io.reactivex.netty.protocol.http.server.HttpServer;
import io.reactivex.netty.protocol.http.server.HttpServerRequest;
import io.reactivex.netty.protocol.http.server.HttpServerResponse;
import io.reactivex.netty.protocol.http.server.RequestHandler;
import rx.Observable;
import rx.functions.Func1;
import static java.lang.String.*;
/**
* The client examples assume that the movie server runs on the default port 8080.
*
* @author Tomasz Bak
*/
public class RxMovieServer {
public static final int DEFAULT_PORT = 8080;
private static final Pattern USER_RECOMMENDATIONS_PATH_RE = Pattern.compile(".*/users/([^/]*)/recommendations");
private final int port;
final Map movies = new ConcurrentHashMap();
final Map> userRecommendations = new ConcurrentHashMap>();
public RxMovieServer(int port) {
this.port = port;
}
public HttpServer createServer() {
HttpServer server = RxNetty.newHttpServerBuilder(port, new RequestHandler() {
@Override
public Observable handle(HttpServerRequest request, final HttpServerResponse response) {
if (request.getPath().contains("/users")) {
if (request.getHttpMethod().equals(HttpMethod.GET)) {
return handleRecommendationsByUserId(request, response);
} else {
return handleUpdateRecommendationsForUser(request, response);
}
}
if (request.getPath().contains("/recommendations")) {
return handleRecommendationsBy(request, response);
}
if (request.getPath().contains("/movies")) {
return handleRegisterMovie(request, response);
}
response.setStatus(HttpResponseStatus.NOT_FOUND);
return response.close();
}
}).pipelineConfigurator(PipelineConfigurators.httpServerConfigurator()).enableWireLogging(LogLevel.ERROR).build();
System.out.println("RxMovie server started...");
return server;
}
private Observable handleRecommendationsByUserId(HttpServerRequest request, HttpServerResponse response) {
System.out.println("HTTP request -> recommendations by user id request: " + request.getPath());
final String userId = userIdFromPath(request.getPath());
if (userId == null) {
response.setStatus(HttpResponseStatus.BAD_REQUEST);
return response.close();
}
if (!userRecommendations.containsKey(userId)) {
response.setStatus(HttpResponseStatus.NOT_FOUND);
return response.close();
}
StringBuilder builder = new StringBuilder();
for (String movieId : userRecommendations.get(userId)) {
System.out.println(" returning: " + movies.get(movieId));
builder.append(movies.get(movieId)).append('\n');
}
ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer();
byteBuf.writeBytes(builder.toString().getBytes(Charset.defaultCharset()));
response.write(byteBuf);
return response.close();
}
private Observable handleRecommendationsBy(HttpServerRequest request, HttpServerResponse response) {
System.out.println(format("HTTP request -> recommendations by multiple criteria: %s?%s", request.getPath(), request.getQueryString()));
List category = request.getQueryParameters().get("category");
List ageGroup = request.getQueryParameters().get("ageGroup");
if (category.isEmpty() || ageGroup.isEmpty()) {
response.setStatus(HttpResponseStatus.BAD_REQUEST);
return response.close();
}
boolean any = false;
StringBuilder builder = new StringBuilder();
for (Movie movie : movies.values()) {
if (movie.getCategory().equals(category.get(0)) && movie.getAgeGroup().equals(ageGroup.get(0))) {
System.out.println(" returning: " + movie);
builder.append(movie).append('\n');
any = true;
}
}
if (!any) {
System.out.println("No movie matched the given criteria:");
for (Movie movie : movies.values()) {
System.out.print(" ");
System.out.println(movie);
}
}
ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer();
byteBuf.writeBytes(builder.toString().getBytes(Charset.defaultCharset()));
response.write(byteBuf);
return response.close();
}
private Observable handleUpdateRecommendationsForUser(HttpServerRequest request, final HttpServerResponse response) {
System.out.println("HTTP request -> update recommendations for user: " + request.getPath());
final String userId = userIdFromPath(request.getPath());
if (userId == null) {
response.setStatus(HttpResponseStatus.BAD_REQUEST);
return response.close();
}
return request.getContent().flatMap(new Func1>() {
@Override
public Observable call(ByteBuf byteBuf) {
String movieId = byteBuf.toString(Charset.defaultCharset());
System.out.println(format(" updating: {user=%s, movie=%s}", userId, movieId));
synchronized (this) {
Set recommendations;
if (userRecommendations.containsKey(userId)) {
recommendations = userRecommendations.get(userId);
} else {
recommendations = new ConcurrentSet();
userRecommendations.put(userId, recommendations);
}
recommendations.add(movieId);
}
response.setStatus(HttpResponseStatus.OK);
return response.close();
}
});
}
private Observable handleRegisterMovie(HttpServerRequest request, final HttpServerResponse response) {
System.out.println("Http request -> register movie: " + request.getPath());
return request.getContent().flatMap(new Func1>() {
@Override
public Observable call(ByteBuf byteBuf) {
String formatted = byteBuf.toString(Charset.defaultCharset());
System.out.println(" movie: " + formatted);
try {
Movie movie = Movie.from(formatted);
movies.put(movie.getId(), movie);
response.setStatus(HttpResponseStatus.CREATED);
} catch (Exception e) {
System.err.println("Invalid movie content");
e.printStackTrace();
response.setStatus(HttpResponseStatus.BAD_REQUEST);
}
return response.close();
}
});
}
private static String userIdFromPath(String path) {
Matcher matcher = USER_RECOMMENDATIONS_PATH_RE.matcher(path);
return matcher.matches() ? matcher.group(1) : null;
}
public static void main(final String[] args) {
new RxMovieServer(DEFAULT_PORT).createServer().startAndWait();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy