Working on the async lotro server.

This commit is contained in:
marcins78@gmail.com
2013-01-09 03:56:07 +00:00
parent c295385df4
commit 386e8907a7
16 changed files with 918 additions and 115 deletions

View File

@@ -0,0 +1,262 @@
package com.gempukku.lotro.async;
import com.gempukku.lotro.SubscriptionConflictException;
import com.gempukku.lotro.SubscriptionExpiredException;
import com.gempukku.lotro.collection.CollectionsManager;
import com.gempukku.lotro.db.vo.League;
import com.gempukku.lotro.game.LotroFormat;
import com.gempukku.lotro.game.Player;
import com.gempukku.lotro.game.formats.LotroFormatLibrary;
import com.gempukku.lotro.hall.HallChannelVisitor;
import com.gempukku.lotro.hall.HallServer;
import com.gempukku.lotro.league.LeagueSerieData;
import com.gempukku.lotro.league.LeagueService;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Type;
import java.util.Map;
public class HallRequestHandler extends LotroServerRequestHandler implements UriRequestHandler {
private CollectionsManager _collectionManager;
private LotroFormatLibrary _formatLibrary;
private HallServer _hallServer;
private LeagueService _leagueService;
private long _longPollingLength = 5000;
private long _longPollingInterval = 200;
public HallRequestHandler(Map<Type, Object> context) {
super(context);
_collectionManager = extractObject(context, CollectionsManager.class);
_formatLibrary = extractObject(context, LotroFormatLibrary.class);
_hallServer = extractObject(context, HallServer.class);
_leagueService = extractObject(context, LeagueService.class);
}
@Override
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, MessageEvent e) {
if (uri.equals("") && request.getMethod() == HttpMethod.GET) {
getHall(request, responseWriter);
} else if (uri.equals("/update") && request.getMethod() == HttpMethod.POST) {
updateHall(request, responseWriter);
} else {
responseWriter.writeError(404);
}
}
private void getHall(HttpRequest request, ResponseWriter responseWriter) {
QueryStringDecoder queryDecoder = new QueryStringDecoder(request.getUri());
String participantId = getQueryParameterSafely(queryDecoder, "participantId");
try {
Player resourceOwner = getResourceOwnerSafely(request, participantId);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element hall = doc.createElement("hall");
hall.setAttribute("currency", String.valueOf(_collectionManager.getPlayerCollection(resourceOwner, "permanent").getCurrency()));
_hallServer.signupUserForHall(resourceOwner, new SerializeHallInfoVisitor(doc, hall));
for (Map.Entry<String, LotroFormat> format : _formatLibrary.getHallFormats().entrySet()) {
Element formatElem = doc.createElement("format");
formatElem.setAttribute("type", format.getKey());
formatElem.appendChild(doc.createTextNode(format.getValue().getName()));
hall.appendChild(formatElem);
}
for (League league : _leagueService.getActiveLeagues()) {
final LeagueSerieData currentLeagueSerie = _leagueService.getCurrentLeagueSerie(league);
if (currentLeagueSerie != null && _leagueService.isPlayerInLeague(league, resourceOwner)) {
Element formatElem = doc.createElement("format");
formatElem.setAttribute("type", league.getType());
formatElem.appendChild(doc.createTextNode(league.getName()));
hall.appendChild(formatElem);
}
}
doc.appendChild(hall);
responseWriter.writeResponse(doc);
} catch (HttpProcessingException exp) {
responseWriter.writeError(exp.getStatus());
} catch (Exception exp) {
responseWriter.writeError(500);
}
}
private void updateHall(HttpRequest request, ResponseWriter responseWriter) {
try {
HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request);
String participantId = getFormParameterSafely(postDecoder, "participantId");
int channelNumber = Integer.parseInt(getFormParameterSafely(postDecoder, "channelNumber"));
Player resourceOwner = getResourceOwnerSafely(request, participantId);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element hall = doc.createElement("hall");
try {
// Use long polling
long start = System.currentTimeMillis();
while (System.currentTimeMillis() < start + _longPollingLength && !_hallServer.hasChanges(resourceOwner, channelNumber)) {
try {
Thread.sleep(_longPollingInterval);
} catch (InterruptedException exp) {
}
}
_hallServer.processHall(resourceOwner, channelNumber, new SerializeHallInfoVisitor(doc, hall));
} catch (SubscriptionExpiredException exp) {
throw new WebApplicationException(Response.Status.GONE);
} catch (SubscriptionConflictException exp) {
throw new WebApplicationException(Response.Status.CONFLICT);
}
hall.setAttribute("currency", String.valueOf(_collectionManager.getPlayerCollection(resourceOwner, "permanent").getCurrency()));
doc.appendChild(hall);
responseWriter.writeResponse(doc);
} catch (HttpProcessingException exp) {
responseWriter.writeError(exp.getStatus());
} catch (Exception exp) {
responseWriter.writeError(500);
}
}
private class SerializeHallInfoVisitor implements HallChannelVisitor {
private Document _doc;
private Element _hall;
public SerializeHallInfoVisitor(Document doc, Element hall) {
_doc = doc;
_hall = hall;
}
@Override
public void channelNumber(int channelNumber) {
_hall.setAttribute("channelNumber", String.valueOf(channelNumber));
}
@Override
public void serverTime(String serverTime) {
_hall.setAttribute("serverTime", serverTime);
}
@Override
public void motdChanged(String motd) {
_hall.setAttribute("motd", motd);
}
@Override
public void addTournamentQueue(String queueId, Map<String, String> props) {
Element queue = _doc.createElement("queue");
queue.setAttribute("action", "add");
queue.setAttribute("id", queueId);
for (Map.Entry<String, String> attribute : props.entrySet())
queue.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(queue);
}
@Override
public void updateTournamentQueue(String queueId, Map<String, String> props) {
Element queue = _doc.createElement("queue");
queue.setAttribute("action", "update");
queue.setAttribute("id", queueId);
for (Map.Entry<String, String> attribute : props.entrySet())
queue.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(queue);
}
@Override
public void removeTournamentQueue(String queueId) {
Element queue = _doc.createElement("queue");
queue.setAttribute("action", "remove");
queue.setAttribute("id", queueId);
_hall.appendChild(queue);
}
@Override
public void addTournament(String tournamentId, Map<String, String> props) {
Element tournament = _doc.createElement("tournament");
tournament.setAttribute("action", "add");
tournament.setAttribute("id", tournamentId);
for (Map.Entry<String, String> attribute : props.entrySet())
tournament.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(tournament);
}
@Override
public void updateTournament(String tournamentId, Map<String, String> props) {
Element tournament = _doc.createElement("tournament");
tournament.setAttribute("action", "update");
tournament.setAttribute("id", tournamentId);
for (Map.Entry<String, String> attribute : props.entrySet())
tournament.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(tournament);
}
@Override
public void removeTournament(String tournamentId) {
Element tournament = _doc.createElement("tournament");
tournament.setAttribute("action", "remove");
tournament.setAttribute("id", tournamentId);
_hall.appendChild(tournament);
}
@Override
public void addTable(String tableId, Map<String, String> props) {
Element table = _doc.createElement("table");
table.setAttribute("action", "add");
table.setAttribute("id", tableId);
for (Map.Entry<String, String> attribute : props.entrySet())
table.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(table);
}
@Override
public void updateTable(String tableId, Map<String, String> props) {
Element table = _doc.createElement("table");
table.setAttribute("action", "update");
table.setAttribute("id", tableId);
for (Map.Entry<String, String> attribute : props.entrySet())
table.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(table);
}
@Override
public void removeTable(String tableId) {
Element table = _doc.createElement("table");
table.setAttribute("action", "remove");
table.setAttribute("id", tableId);
_hall.appendChild(table);
}
@Override
public void runningPlayerGame(String gameId) {
Element runningGame = _doc.createElement("game");
runningGame.setAttribute("id", gameId);
_hall.appendChild(runningGame);
}
@Override
public void playerBusy(boolean busy) {
_hall.setAttribute("busy", String.valueOf(busy));
}
}
}

View File

@@ -0,0 +1,13 @@
package com.gempukku.lotro.async;
public class HttpProcessingException extends Exception {
private int _status;
public HttpProcessingException(int status) {
_status = status;
}
public int getStatus() {
return _status;
}
}

View File

@@ -1,104 +0,0 @@
package com.gempukku.lotro.async;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.*;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import org.jboss.netty.util.CharsetUtil;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
private HttpRequest _request;
private boolean _readingChunks;
private String _uri;
private Map<Type, Object> _objects;
public HttpRequestHandler(Map<Type, Object> objects) {
_objects = objects;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
_request = (HttpRequest) e.getMessage();
if (is100ContinueExpected(_request)) {
send100Continue(e);
}
String uri = _request.getUri();
if (uri.indexOf("?")>-1)
_uri = uri.substring(0, uri.indexOf("?"));
else
_uri = uri;
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(_request.getUri());
Map<String, List<String>> queryStringParams = queryStringDecoder.getParameters();
HttpMethod httpMethod = _request.getMethod();
if (_request.isChunked()) {
send400Error(e);
} else {
ChannelBuffer content = _request.getContent();
if (content.readable()) {
}
writeResponse(e);
}
}
private void writeResponse(MessageEvent e) {
// Decide whether to close the connection or not.
boolean keepAlive = isKeepAlive(_request);
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
response.setContent(ChannelBuffers.copiedBuffer("Response: " + _uri, CharsetUtil.UTF_8));
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
// Close the non-keep-alive connection after the write operation is done.
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private void send400Error(MessageEvent e) {
boolean keepAlive = isKeepAlive(_request);
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST);
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private void send100Continue(MessageEvent e) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
e.getChannel().write(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
e.getChannel().close();
}
}

View File

@@ -0,0 +1,49 @@
package com.gempukku.lotro.async;
import org.jboss.netty.handler.codec.http.Cookie;
import org.jboss.netty.handler.codec.http.CookieDecoder;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.COOKIE;
import org.jboss.netty.handler.codec.http.HttpRequest;
import java.util.*;
public class LoggedUserHolder {
private Map<String, String> _users = Collections.synchronizedMap(new HashMap<String, String>());
public String getLoggedUser(HttpRequest request) {
CookieDecoder cookieDecoder = new CookieDecoder();
Set<Cookie> cookies = cookieDecoder.decode(request.getHeader(COOKIE));
for (Cookie cookie : cookies) {
if (cookie.getName().equals("loggedUser")) {
String value = cookie.getValue();
if (value != null) {
String loggedUser = _users.get(value);
if (loggedUser != null)
return loggedUser;
}
}
}
return null;
}
public Map<String, String> logUser(String userName) {
Map<String, String> cookies = new HashMap<String, String>();
cookies.put("loggedUser", insertValueForUser(userName));
return cookies;
}
private char[] _chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
private synchronized String insertValueForUser(String userName) {
Random rnd = new Random();
String randomStr;
do {
StringBuilder result = new StringBuilder();
for (int i = 0; i < 20; i++)
result.append(_chars[rnd.nextInt(_chars.length)]);
randomStr = result.toString();
} while (_users.containsKey(randomStr));
_users.put(randomStr, userName);
return randomStr;
}
}

View File

@@ -0,0 +1,49 @@
package com.gempukku.lotro.async;
import com.gempukku.lotro.game.Player;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.Map;
public class LoginRequestHandler extends LotroServerRequestHandler implements UriRequestHandler {
public LoginRequestHandler(Map<Type, Object> context) {
super(context);
}
@Override
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, MessageEvent e) {
try {
if (uri.equals("") && request.getMethod() == HttpMethod.POST) {
HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request);
String login = getFormParameterSafely(postDecoder, "login");
String password = getFormParameterSafely(postDecoder, "password");
Player player = _playerDao.loginUser(login, password);
if (player != null) {
if (player.getType().contains("u")) {
responseWriter.writeResponse(null, logUserReturningCookies(e, login));
} else {
responseWriter.writeError(403);
}
} else {
responseWriter.writeError(401);
}
} else {
responseWriter.writeError(404);
}
} catch (Exception exp) {
responseWriter.writeError(500);
}
}
private Map<String, String> logUserReturningCookies(MessageEvent e, String login) throws SQLException {
_playerDao.updateLastLoginIp(login, e.getChannel().getRemoteAddress().toString());
return _loggedUserHolder.logUser(login);
}
}

View File

@@ -17,10 +17,10 @@ public class LotroAsyncServer {
ServerBootstrap bootstrap = new ServerBootstrap(factory);
bootstrap.setPipelineFactory(new HttpServerPipelineFactory());
bootstrap.setPipelineFactory(new LotroServerPipelineFactory());
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.bind(new InetSocketAddress(8080));
bootstrap.bind(new InetSocketAddress(80));
}
}

View File

@@ -0,0 +1,269 @@
package com.gempukku.lotro.async;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.*;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import org.jboss.netty.util.CharsetUtil;
import org.w3c.dom.Document;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.lang.reflect.Type;
import java.util.Map;
public class LotroHttpRequestHandler extends SimpleChannelUpstreamHandler {
private Logger _log = Logger.getLogger(LotroHttpRequestHandler.class);
private Map<Type, Object> _objects;
private UriRequestHandler _uriRequestHandler;
public LotroHttpRequestHandler(Map<Type, Object> objects, UriRequestHandler uriRequestHandler) {
_objects = objects;
_uriRequestHandler = uriRequestHandler;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
final HttpRequest request = (HttpRequest) e.getMessage();
if (is100ContinueExpected(request)) {
send100Continue(e);
}
String uri = request.getUri();
if (uri.indexOf("?") > -1)
uri = uri.substring(0, uri.indexOf("?"));
if (request.isChunked()) {
send400Error(request, e);
} else {
ResponseWriter responseWriter = new ResponseWriter() {
@Override
public void writeError(int status) {
writeHttpErrorResponse(request, status, e);
}
@Override
public void writeResponse(Document document) {
writeHttpResponse(request, document, null, e);
}
@Override
public void writeResponse(Document document, Map<String, String> addCookies) {
writeHttpResponse(request, document, addCookies, e);
}
@Override
public void writeHtmlResponse(String html) {
writeHttpHtmlResponse(request, html, e);
}
@Override
public void writeFile(File file) {
writeFileResponse(request, file, e);
}
};
_uriRequestHandler.handleRequest(uri, request, _objects, responseWriter, e);
}
}
private void writeFileResponse(HttpRequest request, File file, MessageEvent e) {
try {
RandomAccessFile raf;
try {
raf = new RandomAccessFile(file, "r");
} catch (FileNotFoundException fnfe) {
writeHttpErrorResponse(request, 404, e);
return;
}
long fileLength = raf.length();
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
response.setHeader(CONTENT_LENGTH, fileLength);
String fileName = file.getName();
if (fileName.endsWith(".html")) {
response.setHeader(CACHE_CONTROL, "no-cache");
response.setHeader(PRAGMA, "no-cache");
response.setHeader(EXPIRES, -1);
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
}
Channel ch = e.getChannel();
// Write the initial line and the header.
ch.write(response);
// Write the content.
ChannelFuture writeFuture;
// No encryption - use zero-copy.
final FileRegion region =
new DefaultFileRegion(raf.getChannel(), 0, fileLength);
writeFuture = ch.write(region);
writeFuture.addListener(new ChannelFutureProgressListener() {
public void operationComplete(ChannelFuture future) {
region.releaseExternalResources();
}
public void operationProgressed(
ChannelFuture future, long amount, long current, long total) {
}
});
// Decide whether to close the connection or not.
if (!isKeepAlive(request)) {
// Close the connection when the whole content is written out.
writeFuture.addListener(ChannelFutureListener.CLOSE);
}
} catch (IOException exp) {
writeHttpErrorResponse(request, 500, e);
}
}
private void writeHttpErrorResponse(HttpRequest request, int status, MessageEvent e) {
boolean keepAlive = isKeepAlive(request);
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(status));
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
}
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private void writeHttpResponse(HttpRequest request, Document document, Map<String, String> addCookies, MessageEvent e) {
// Decide whether to close the connection or not.
boolean keepAlive = isKeepAlive(request);
try {
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
if (addCookies != null) {
CookieEncoder cookieEncoder = new CookieEncoder(true);
for (Map.Entry<String, String> cookie : addCookies.entrySet())
cookieEncoder.addCookie(cookie.getKey(), cookie.getValue());
response.setHeader(SET_COOKIE, cookieEncoder.encode());
}
int length = 0;
if (document != null) {
DOMSource domSource = new DOMSource(document);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
String responseString = writer.toString();
response.setContent(ChannelBuffers.copiedBuffer(responseString, CharsetUtil.UTF_8));
response.setHeader(CONTENT_TYPE, "application/xml; charset=UTF-8");
length = responseString.length();
}
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, length);
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
} catch (Exception exp) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(500));
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
}
private void writeHttpHtmlResponse(HttpRequest request, String html, MessageEvent e) {
// Decide whether to close the connection or not.
boolean keepAlive = isKeepAlive(request);
try {
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
response.setContent(ChannelBuffers.copiedBuffer(html, CharsetUtil.UTF_8));
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
int length = html.length();
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, length);
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
} catch (Exception exp) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(500));
if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
}
private void send400Error(HttpRequest request, MessageEvent e) {
boolean keepAlive = isKeepAlive(request);
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST);
ChannelFuture future = e.getChannel().write(response);
if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private void send100Continue(MessageEvent e) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
e.getChannel().write(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
_log.error("Error while processing request", e.getCause());
e.getChannel().close();
}
}

View File

@@ -15,7 +15,23 @@ import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class HttpServerPipelineFactory implements ChannelPipelineFactory {
public class LotroServerPipelineFactory implements ChannelPipelineFactory {
private Map<Type, Object> _context;
private UriRequestHandler _uriRequestHandler;
public LotroServerPipelineFactory() {
Map<Type, Object> objects = new HashMap<Type, Object>();
objects.put(PacksStorage.class, PacksStorageBuilder.createPacksStorage());
DaoBuilder.fillObjectMap(objects);
ServerBuilder.fillObjectMap(objects);
ServerBuilder.constructObjects(objects);
objects.put(LoggedUserHolder.class, new LoggedUserHolder());
_context = objects;
_uriRequestHandler = new RootUriRequestHandler(_context);
}
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = Channels.pipeline();
@@ -32,13 +48,7 @@ public class HttpServerPipelineFactory implements ChannelPipelineFactory {
// Remove the following line if you don't want automatic content compression.
pipeline.addLast("deflater", new HttpContentCompressor());
Map<Type, Object> objects = new HashMap<Type, Object>();
objects.put(PacksStorage.class, PacksStorageBuilder.createPacksStorage());
DaoBuilder.fillObjectMap(objects);
ServerBuilder.fillObjectMap(objects);
ServerBuilder.constructObjects(objects);
pipeline.addLast("handler", new HttpRequestHandler(objects));
pipeline.addLast("handler", new LotroHttpRequestHandler(_context, _uriRequestHandler));
return pipeline;
}
}

View File

@@ -0,0 +1,74 @@
package com.gempukku.lotro.async;
import com.gempukku.lotro.db.PlayerDAO;
import com.gempukku.lotro.game.Player;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
import org.jboss.netty.handler.codec.http.multipart.Attribute;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class LotroServerRequestHandler {
protected PlayerDAO _playerDao;
protected LoggedUserHolder _loggedUserHolder;
public LotroServerRequestHandler(Map<Type, Object> context) {
_playerDao = extractObject(context, PlayerDAO.class);
_loggedUserHolder = extractObject(context, LoggedUserHolder.class);
}
private boolean isTest() {
return Boolean.valueOf(System.getProperty("test"));
}
protected final Player getResourceOwnerSafely(HttpRequest request, String participantId) throws HttpProcessingException {
String loggedUser = _loggedUserHolder.getLoggedUser(request);
if (isTest() && loggedUser == null)
loggedUser = participantId;
if (loggedUser == null)
throw new HttpProcessingException(401);
Player resourceOwner = _playerDao.getPlayer(loggedUser);
if (resourceOwner == null)
throw new HttpProcessingException(401);
if (resourceOwner.getType().contains("a") && participantId != null && !participantId.equals("null") && !participantId.equals("")) {
resourceOwner = _playerDao.getPlayer(participantId);
if (resourceOwner == null)
throw new HttpProcessingException(401);
}
return resourceOwner;
}
protected String getQueryParameterSafely(QueryStringDecoder queryStringDecoder, String parameterName) {
List<String> parameterValues = queryStringDecoder.getParameters().get(parameterName);
if (parameterValues.size() > 0)
return parameterValues.get(0);
else
return null;
}
protected String getFormParameterSafely(HttpPostRequestDecoder postRequestDecoder, String parameterName) throws IOException, HttpPostRequestDecoder.NotEnoughDataDecoderException {
InterfaceHttpData data = postRequestDecoder.getBodyHttpData(parameterName);
if (data == null)
return null;
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
Attribute attribute = (Attribute) data;
return attribute.getValue();
} else {
return null;
}
}
protected <T> T extractObject(Map<Type, Object> context, Class<T> clazz) {
Object value = context.get(clazz);
return (T) value;
}
}

View File

@@ -0,0 +1,18 @@
package com.gempukku.lotro.async;
import org.w3c.dom.Document;
import java.io.File;
import java.util.Map;
public interface ResponseWriter {
public void writeError(int status);
public void writeFile(File file);
public void writeHtmlResponse(String html);
public void writeResponse(Document document);
public void writeResponse(Document document, Map<String, String> addCookies);
}

View File

@@ -0,0 +1,37 @@
package com.gempukku.lotro.async;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpRequest;
import java.lang.reflect.Type;
import java.util.Map;
public class RootUriRequestHandler implements UriRequestHandler {
private String _serverContextPath ="/gemp-lotr-server/";
private String _webContextPath="/gemp-lotr/";
private HallRequestHandler _hallRequestHandler;
private WebRequestHandler _webRequestHandler = new WebRequestHandler();
private LoginRequestHandler _loginRequestHandler;
private StatusRequestHandler _statusRequestHandler;
public RootUriRequestHandler(Map<Type, Object> context) {
_hallRequestHandler = new HallRequestHandler(context);
_loginRequestHandler = new LoginRequestHandler(context);
_statusRequestHandler = new StatusRequestHandler(context);
}
@Override
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, MessageEvent e) {
if (uri.startsWith(_webContextPath)) {
_webRequestHandler.handleRequest(uri.substring(_webContextPath.length()), request, context, responseWriter, e);
} else if (uri.startsWith(_serverContextPath +"hall")) {
_hallRequestHandler.handleRequest(uri.substring(_serverContextPath.length()+4), request, context, responseWriter, e);
} else if (uri.startsWith(_serverContextPath+"login")) {
_loginRequestHandler.handleRequest(uri.substring(_serverContextPath.length()+5), request, context, responseWriter, e);
} else if (uri.equals(_serverContextPath)) {
_statusRequestHandler.handleRequest(uri.substring(_serverContextPath.length()), request, context, responseWriter, e);
} else {
responseWriter.writeError(404);
}
}
}

View File

@@ -0,0 +1,41 @@
package com.gempukku.lotro.async;
import com.gempukku.lotro.chat.ChatServer;
import com.gempukku.lotro.game.GameHistoryService;
import com.gempukku.lotro.hall.HallServer;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import java.lang.reflect.Type;
import java.util.Map;
public class StatusRequestHandler extends LotroServerRequestHandler implements UriRequestHandler {
private HallServer _hallServer;
private GameHistoryService _gameHistoryService;
private ChatServer _chatServer;
public StatusRequestHandler(Map<Type, Object> context) {
super(context);
_hallServer = extractObject(context, HallServer.class);
_gameHistoryService = extractObject(context, GameHistoryService.class);
_chatServer = extractObject(context, ChatServer.class);
}
@Override
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, MessageEvent e) {
if (uri.equals("") && request.getMethod() == HttpMethod.GET) {
StringBuilder sb = new StringBuilder();
int day = 1000 * 60 * 60 * 24;
int week = 1000 * 60 * 60 * 24 * 7;
sb.append("Tables count: ").append(_hallServer.getTablesCount()).append(", players in hall: ").append(_chatServer.getChatRoom("Game Hall").getUsersInRoom().size())
.append(", games played in last 24 hours: ").append(_gameHistoryService.getGamesPlayedCount(System.currentTimeMillis() - day, day))
.append(",<br/> active players in last week: ").append(_gameHistoryService.getActivePlayersCount(System.currentTimeMillis() - week, week));
responseWriter.writeHtmlResponse(sb.toString());
} else {
responseWriter.writeError(404);
}
}
}

View File

@@ -0,0 +1,11 @@
package com.gempukku.lotro.async;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpRequest;
import java.lang.reflect.Type;
import java.util.Map;
public interface UriRequestHandler {
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, MessageEvent e);
}

View File

@@ -0,0 +1,29 @@
package com.gempukku.lotro.async;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpRequest;
import java.io.File;
import java.lang.reflect.Type;
import java.util.Map;
public class WebRequestHandler implements UriRequestHandler {
private String _root = "I:\\dev\\projects\\gemp-lotr\\gemp-lotr-web\\src\\main\\webapp\\";
@Override
public void handleRequest(String uri, HttpRequest request, Map<Type, Object> context, ResponseWriter responseWriter, MessageEvent e) {
if (uri.equals(""))
uri = "index.html";
uri = uri.replace('/', File.separatorChar);
if ((uri.contains(".."))
|| uri.contains(File.separator + ".")
|| uri.startsWith(".") || uri.endsWith(".")) {
responseWriter.writeError(403);
} else {
File file = new File(_root + uri);
responseWriter.writeFile(file);
}
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<!-- Pattern to output the caller's file name and line number -->
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n"/>
</layout>
</appender>
<appender name="R" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${catalina.base}/logs/gemp-lotr.log"/>
<param name="MaxFileSize" value="10MB"/>
<param name="MaxBackupIndex" value="20"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n"/>
</layout>
</appender>
<appender name="chat" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${catalina.base}/logs/chat.log"/>
<param name="MaxFileSize" value="10MB"/>
<!-- Keep one backup file -->
<param name="MaxBackupIndex" value="20"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n"/>
</layout>
</appender>
<logger name="com.gempukku" additivity="false">
<level value="debug"/>
<appender-ref ref="R"/>
<appender-ref ref="stdout"/>
</logger>
<logger name="chat">
<level value="ALL"/>
<appender-ref ref="chat"/>
</logger>
<root>
<level value="error"/>
<appender-ref ref="R"/>
<appender-ref ref="stdout"/>
</root>
</log4j:configuration>

View File

@@ -34,9 +34,11 @@ public class CollectionsManager {
// Hunters have 1-194 normal cards, 9 "O" cards, and 3 extra to cover the different culture versions of 15_60
boolean test = Boolean.valueOf(System.getProperty("test"));
final int[] cardCounts = new int[]{129, 365, 122, 122, 365, 128, 128, 365, 122, 52, 122, 266, 203, 203, 15, 207, 6, 157, 149, 40};
for (int i = 0; i <= 19; i++) {
for (int i = 0; i <= (test?1:19); i++) {
_logger.debug("Loading set " + i);
for (int j = 1; j <= cardCounts[i]; j++) {
String blueprintId = i + "_" + j;