freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java
author Martin Rexa <martin.rexa@centrum.cz>
Wed, 13 Jan 2010 14:36:17 +0100
changeset 212 9dc1915485ba
parent 195 39a607f6f174
child 220 fca206f8e5ef
permissions -rw-r--r--
Handling parameter, and passing it to fmt
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * The contents of this file are subject to the terms of either the GNU
     5  * General Public License Version 2 only ("GPL") or the Common
     6  * Development and Distribution License("CDDL") (collectively, the
     7  * "License"). You may not use this file except in compliance with the
     8  * License. You can obtain a copy of the License at
     9  * http://www.netbeans.org/cddl-gplv2.html
    10  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    11  * specific language governing permissions and limitations under the
    12  * License.  When distributing the software, include this License Header
    13  * Notice in each file and include the License file at
    14  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    15  * particular file as subject to the "Classpath" exception as provided
    16  * by Sun in the GPL Version 2 section of the License file that
    17  * accompanied this code. If applicable, add the following below the
    18  * License Header, with the fields enclosed by brackets [] replaced by
    19  * your own identifying information:
    20  * "Portions Copyrighted [year] [name of copyright owner]"
    21  *
    22  * Contributor(s):
    23  *
    24  * Portions Copyrighted 2009 Jaroslav Tulach
    25  */
    26 
    27 package cz.xelfi.quoridor.freemarkerdor;
    28 
    29 import com.sun.jersey.api.client.Client;
    30 import com.sun.jersey.api.client.UniformInterfaceException;
    31 import com.sun.jersey.api.client.WebResource;
    32 import com.sun.jersey.api.container.httpserver.HttpServerFactory;
    33 import com.sun.jersey.api.core.PackagesResourceConfig;
    34 import com.sun.jersey.api.core.ResourceConfig;
    35 import com.sun.jersey.api.view.Viewable;
    36 import com.sun.net.httpserver.HttpServer;
    37 import cz.xelfi.quoridor.Board;
    38 import cz.xelfi.quoridor.IllegalPositionException;
    39 import java.io.IOException;
    40 import java.io.InputStream;
    41 import java.io.StringWriter;
    42 import java.net.URI;
    43 import java.util.Date;
    44 import java.util.HashMap;
    45 import java.util.List;
    46 import java.util.Locale;
    47 import java.util.Map;
    48 import java.util.MissingResourceException;
    49 import java.util.Properties;
    50 import java.util.ResourceBundle;
    51 import java.util.concurrent.Callable;
    52 import javax.ws.rs.DefaultValue;
    53 import javax.ws.rs.FormParam;
    54 import javax.ws.rs.GET;
    55 import javax.ws.rs.POST;
    56 import javax.ws.rs.Path;
    57 import javax.ws.rs.PathParam;
    58 import javax.ws.rs.Produces;
    59 import javax.ws.rs.QueryParam;
    60 import javax.ws.rs.core.CacheControl;
    61 import javax.ws.rs.core.Context;
    62 import javax.ws.rs.core.Cookie;
    63 import javax.ws.rs.core.HttpHeaders;
    64 import javax.ws.rs.core.MediaType;
    65 import javax.ws.rs.core.NewCookie;
    66 import javax.ws.rs.core.Response;
    67 import javax.ws.rs.core.Response.ResponseBuilder;
    68 import org.openide.util.Exceptions;
    69 import org.w3c.dom.Document;
    70 
    71 /**
    72  *
    73  * @author Jaroslav Tulach <jtulach@netbeans.org>
    74  */
    75 @Path("/")
    76 public final class UI {
    77     private static final String version;
    78     static {
    79         Properties p = new Properties();
    80         try {
    81             InputStream is = FreemarkerProcessor.class.getResourceAsStream("/META-INF/maven/cz.xelfi.quoridor/freemarkerdor/pom.properties"); // NOI18N
    82             if (is != null) {
    83                 p.load(is);
    84             }
    85         } catch (IOException ex) {
    86             ex.printStackTrace();
    87         }
    88         version = p.getProperty("version", "unknown"); // NOI18N
    89     }
    90     private static WebResource base;
    91     private static WebResource stat;
    92 
    93     @Context
    94     private HttpHeaders headers;
    95     private UserInfo user;
    96     private String uuid;
    97 
    98     public UI() {
    99     }
   100 
   101     private Viewable checkLogin() {
   102         Cookie cookie = headers.getCookies().get("login");
   103         if (cookie != null) {
   104             String id = cookie.getValue();
   105             UserInfo us;
   106             try {
   107                 us = base.path("users").queryParam("loginID", id).
   108                     accept(MediaType.TEXT_XML).get(UserInfo.class);
   109             } catch (Exception ex) {
   110                 ex.printStackTrace();
   111                 us = null;
   112             }
   113             if (us != null && us.getId().length() > 0) {
   114                 user = us;
   115                 uuid = id;
   116                 return null;
   117             }
   118         }
   119         return viewable("login.fmt", null);
   120     }
   121 
   122     @POST
   123     @Path("login")
   124     @Produces(MediaType.TEXT_HTML)
   125     public Response login(
   126         @FormParam("name") String name, @FormParam("password") String password
   127     ) throws Exception {
   128         uuid = base.path("login").queryParam("name", name).queryParam("password", password).
   129             accept(MediaType.TEXT_PLAIN).put(String.class);
   130         if (uuid != null) {
   131             user = new UserInfo(name);
   132             NewCookie nc = new NewCookie("login", uuid, null, null, null, 3600 * 24 * 7, false);
   133             return Response.ok().cookie(nc).entity(viewable("login.fmt", null)).build();
   134         } else {
   135             Viewable v = viewable("login.fmt", null, "message", "Invalid name or password: " + name);
   136             return Response.status(1).entity(v).build();
   137         }
   138     }
   139 
   140     @GET
   141     @Produces(MediaType.TEXT_HTML)
   142     public Response welcome(@QueryParam("maxItems") @DefaultValue("10") int maxItems) {
   143         Viewable v = checkLogin();
   144         ResponseBuilder resp = Response.ok();
   145         if (v == null) {
   146             v = welcomeImpl("maxItems", maxItems);
   147         }
   148         CacheControl cc = new CacheControl();
   149         cc.setNoCache(true);
   150         resp.cacheControl(cc);
   151         return resp.entity(v).build();
   152     }
   153 
   154     @GET
   155     @Path("games/{id}.png")
   156     @Produces("image/png")
   157     public Response getBoardImage(
   158         @PathParam("id") String id,
   159         @QueryParam("fieldSize") @DefaultValue("50") int fieldSize,
   160         @QueryParam("move") @DefaultValue("-1") int move
   161     ) throws IllegalPositionException {
   162         WebResource wr = base.path("games").path(id);
   163         if (move != -1) {
   164             wr = wr.queryParam("move", "" + move);
   165         }
   166         String txt = wr.accept(MediaType.TEXT_PLAIN).get(String.class);
   167         Board b = Board.valueOf(txt);
   168 //        Board b = new Board(txt);
   169         ResponseBuilder resp = Response.ok();
   170         CacheControl cc = new CacheControl();
   171         cc.setNoCache(true);
   172         resp.cacheControl(cc);
   173         return resp.entity(BoardImage.draw(b, fieldSize)).build();
   174     }
   175 
   176 
   177     private Response board(String id) {
   178         return board(id, "", null);
   179     }
   180     @GET
   181     @Path("games/{id}/")
   182     @Produces(MediaType.TEXT_HTML)
   183     public Response board(
   184         @PathParam("id") String id,
   185         @QueryParam("format") @DefaultValue("") String format,
   186         @QueryParam("move") @DefaultValue("-1") String move
   187     ) {
   188         return board(id, null, format, move);
   189     }
   190     private Response board(@PathParam("id") String id, String msg, String format, String m) {
   191         Viewable v = checkLogin();
   192         if (v != null) {
   193             return Response.ok().entity(v).build();
   194         }
   195         int move;
   196         try {
   197             move = Integer.parseInt(m);
   198         } catch (NumberFormatException ex) {
   199             move = -1;
   200         }
   201         WebResource url = base.path("games").queryParam("loginID", uuid).path(id);
   202         if (move >= 0) {
   203             url = url.queryParam("move", "" + move);
   204         }
   205         ResponseBuilder resp = Response.ok();
   206         CacheControl cc = new CacheControl();
   207         cc.setNoCache(true);
   208         resp.cacheControl(cc);
   209         Cookie cFormat = headers.getCookies().get("format");
   210         if (format.length() == 0) {
   211             if (cFormat != null) {
   212                 format = cFormat.getValue();
   213             } else {
   214                 if (isMobile(headers)) {
   215                     format = "small";
   216                 }
   217             }
   218         } else {
   219             if (cFormat == null || !format.equals(cFormat.getValue())) {
   220                 resp.cookie(new NewCookie("format", format));
   221             }
   222         }
   223 
   224         Document doc = url.accept(MediaType.TEXT_XML).get(Document.class);
   225         Board b;
   226         String t = doc.getElementsByTagName("board").item(0).getTextContent();
   227         try {
   228             b = Board.valueOf(doc.getElementsByTagName("board").item(0).getTextContent());
   229         } catch (Exception ex) {
   230 //            b = new Board(t);
   231 //        } catch (IllegalStateException ex) {
   232             Exceptions.printStackTrace(ex);
   233             b = Board.empty();
   234         }
   235         String bCode = null;
   236         try{
   237             bCode = stat.path("openings").path(b.getCode()+".check").queryParam("loginID", user.getId()).accept(MediaType.TEXT_PLAIN).get(String.class);
   238         }catch(Exception e){
   239             bCode = null;
   240         }
   241         if(bCode == null || "".equals(bCode))
   242             v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b,"textPicture", boardToPicture(b));
   243         else
   244             v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b,"textPicture", boardToPicture(b),"bCode", bCode);
   245         return resp.entity(v).build();
   246     }
   247 
   248     private static String boardToPicture(Board b) {
   249         StringWriter w = new StringWriter();
   250         try {
   251             b.write(w);
   252         } catch (IOException ex) {
   253             return ex.toString();
   254         }
   255         return w.toString();
   256     }
   257 
   258     @GET
   259     @Path("games/{id}/move")
   260     @Produces(MediaType.TEXT_HTML)
   261     public Response move(
   262         @PathParam("id") String id,
   263         @QueryParam("type") String type,
   264         @QueryParam("direction") String direction,
   265         @QueryParam("direction-next") @DefaultValue("") String directionNext,
   266         @QueryParam("column") @DefaultValue("") String column,
   267         @QueryParam("row") @DefaultValue("") String row
   268     ) {
   269         Viewable v = checkLogin();
   270         if (v != null) {
   271             return Response.ok().entity(v).build();
   272         }
   273         WebResource wr = base.path("games").path(id).
   274             queryParam("loginID", uuid).
   275             queryParam("player", user.getId());
   276         try {
   277             if (type.equals("resign")) {
   278                 wr.queryParam("move", "RESIGN").put();
   279                 return board(id);
   280             }
   281             if (type.equals("fence")) {
   282                 wr.queryParam("move", direction.charAt(0) + column + row).put();
   283                 return board(id);
   284             }
   285             if (type.equals("move")) {
   286                 wr.queryParam("move", direction + directionNext).put();
   287                 return board(id);
   288             }
   289         } catch (UniformInterfaceException ex) {
   290             return board(id, "WRONG_MOVE", "-1");
   291         }
   292         return board(id);
   293     }
   294 
   295     @GET
   296     @Path("games/{id}/comment")
   297     @Produces(MediaType.TEXT_HTML)
   298     public Response comment(
   299         @PathParam("id") String id,
   300         @QueryParam("comment") String comment
   301     ) {
   302         Viewable v = checkLogin();
   303         if (v != null) {
   304             return Response.ok().entity(v).build();
   305         }
   306         WebResource wr = base.path("games").path(id).
   307             queryParam("loginID", uuid).
   308             queryParam("player", user.getId()).
   309             queryParam("comment", comment);
   310         wr.put();
   311         
   312         return board(id);
   313     }
   314 
   315     @GET
   316     @Path("games/create")
   317     @Produces(MediaType.TEXT_HTML)
   318     public Response create(
   319         @QueryParam("white") String white,
   320         @QueryParam("black") String black
   321     ) {
   322         Viewable v = checkLogin();
   323         if (v != null) {
   324             return Response.status(Response.Status.FORBIDDEN).entity(v).build();
   325         }
   326 
   327         if (user.getId().equals(white) || user.getId().equals(black)) {
   328             Object obj =
   329                 base.path("games").
   330                 queryParam("loginID", uuid).
   331                 queryParam("white", white).
   332                 queryParam("black", black).accept(MediaType.TEXT_XML).post(Document.class);
   333             return Response.ok(welcomeImpl()).build();
   334         } else {
   335             return Response.status(Response.Status.NOT_FOUND).
   336                 entity(welcomeImpl("message", "You (" + user.getId() + ") must be white or black!")).build();
   337         }
   338     }
   339 
   340     private Viewable welcomeImpl(Object... args) {
   341         final Document got = base.path("games").queryParam("loginID", uuid).accept(MediaType.TEXT_XML).get(Document.class);
   342         return viewable("index.fmt", got, args);
   343     }
   344 
   345 
   346     @GET
   347     @Path("options")
   348     public Response changeOptions(
   349         @QueryParam("email") String email,
   350         @QueryParam("language") String language
   351     ) {
   352         Viewable v = checkLogin();
   353         if (v != null) {
   354             return Response.status(Response.Status.FORBIDDEN).entity(v).build();
   355         }
   356 
   357         if (email != null) {
   358             UserInfo ui = base.path("users/" + user.getId()).
   359                 queryParam("loginID", uuid).
   360                 queryParam("name", "email").
   361                 queryParam("value", email).accept(MediaType.TEXT_XML).post(UserInfo.class);
   362         }
   363 
   364         if (language != null) {
   365             UserInfo ui = base.path("users/" + user.getId()).
   366                 queryParam("loginID", uuid).
   367                 queryParam("name", "language").
   368                 queryParam("value", language).
   369                 accept(MediaType.TEXT_XML).post(UserInfo.class);
   370         }
   371 
   372         return welcome(10);
   373     }
   374 
   375     @GET
   376     @Path("elo")
   377     @Produces(MediaType.TEXT_HTML)
   378     public Response getEloList(
   379             @QueryParam("historyId") @DefaultValue("0") Integer historyId){
   380         Viewable v = checkLogin();
   381         if (v != null) {
   382             return Response.status(Response.Status.FORBIDDEN).entity(v).build();
   383         }
   384         final Document got = stat.path("elo").path("list").path(historyId.toString()).accept(MediaType.TEXT_XML).get(Document.class);
   385         return Response.ok(viewable("elo.fmt", got, "historyId", historyId)).build();
   386     }
   387     
   388     @GET
   389     @Path("openings")
   390     @Produces(MediaType.TEXT_HTML)
   391     public Response getOpeningRoot(){
   392         return getOpeningNode("ROOT");
   393     }
   394 
   395     @GET
   396     @Path("openings/{code}")
   397     @Produces(MediaType.TEXT_HTML)
   398     public Response getOpeningNode(@PathParam("code") String code){
   399         Viewable v = checkLogin();
   400         if (v != null) {
   401             return Response.status(Response.Status.FORBIDDEN).entity(v).build();
   402         }
   403         final Document got = stat.path("openings").path(code).queryParam("loginID", user.getId()).accept(MediaType.TEXT_XML).get(Document.class);
   404         Board b;
   405         try {
   406             b = Board.valueOf(got.getElementsByTagName("nodeCode").item(0).getTextContent());
   407         } catch (Exception ex) {
   408             Exceptions.printStackTrace(ex);
   409             b = Board.empty();
   410         }
   411         return Response.ok(viewable("openings.fmt", got, "whitefences",b.getPlayers().get(0).getFences(),"blackfences",b.getPlayers().get(1).getFences())).build();
   412     }
   413 
   414     @GET
   415     @Path("openings/{code}/{status}")
   416     @Produces(MediaType.TEXT_HTML)
   417     public Response getOpeningNodeGames(@PathParam("code") String code, @PathParam("status") String status){
   418         Viewable v = checkLogin();
   419         if (v != null) {
   420             return Response.status(Response.Status.FORBIDDEN).entity(v).build();
   421         }
   422         final Document got = stat.path("openings").path(code).path(status).queryParam("loginID", user.getId()).accept(MediaType.TEXT_XML).get(Document.class);
   423         return Response.ok(viewable("opening_games.fmt", got,"code",code,"color",status)).build();
   424     }
   425 
   426     @GET
   427     @Path("openings/{code}.png")
   428     @Produces("image/png")
   429     public Response getOpeningBoardImage(
   430         @PathParam("code") String code,
   431         @QueryParam("fieldSize") @DefaultValue("40") int fieldSize
   432     ) throws IllegalPositionException {
   433         Board b = Board.valueOf(code);
   434         ResponseBuilder resp = Response.ok();
   435         CacheControl cc = new CacheControl();
   436         cc.setNoCache(true);
   437         resp.cacheControl(cc);
   438         return resp.entity(BoardImage.draw(b, fieldSize)).build();
   439     }
   440 
   441     //
   442     // start the server
   443     //
   444 
   445     public static void main(String[] args) throws Exception {
   446         int port = 9333;
   447         if (args.length > 1) {
   448             port = Integer.parseInt(args[0]);
   449         }
   450         String remoteAPI = args.length >= 2 ? args[1] : null;
   451         String remoteStatistics = args.length >= 3 ? args[2] : null;
   452 
   453         Locale.setDefault(Locale.ROOT);
   454 
   455         Callable<Void> r = startServers(port, remoteAPI, remoteStatistics);
   456 
   457         if (args.length < 3 || !args[args.length - 1].equals("--kill")) {
   458             System.out.println("Hit enter to stop it...");
   459             System.in.read();
   460         } else {
   461             synchronized (UI.class) {
   462                 UI.class.wait();
   463             }
   464         }
   465         r.call();
   466         System.exit(0);
   467     }
   468 
   469     static Callable<Void> startServers(int port, String remoteAPI, String remoteStatistics) throws Exception {
   470         Client client = new Client();
   471         Client client1 = new Client();
   472 
   473         final HttpServer apiServer;
   474         if (remoteAPI == null) {
   475             throw new IllegalArgumentException("Provide URL to API server"); // NOI18N
   476         } else {
   477             base = client.resource(new URI(remoteAPI));
   478             apiServer = null;
   479         }
   480 
   481         if (remoteStatistics == null) {
   482             throw new IllegalArgumentException("Provide URL to API server"); // NOI18N
   483         } else {
   484             stat = client1.resource(new URI("http://localhost:9444"));
   485         }
   486 
   487         ResourceConfig rc = new PackagesResourceConfig(
   488             "cz.xelfi.quoridor.freemarkerdor"
   489         );
   490 
   491         final String baseUri = "http://localhost:" + port + "/";
   492         final HttpServer server = HttpServerFactory.create(baseUri, rc);
   493         server.start();
   494         System.out.println("Quoridor started at port " + port);
   495 
   496         return new Callable<Void>() {
   497             public Void call() throws Exception {
   498                 if (apiServer != null) {
   499                     apiServer.stop(0);
   500                 }
   501                 server.stop(0);
   502                 return null;
   503             }
   504         };
   505     }
   506 
   507     private Viewable viewable(String page, Document doc, Object... more) {
   508         ResourceBundle rb = null;
   509         String lng = user == null ? null : user.getProperty("language"); // NOI18N
   510         Locale locale = null;
   511         if (lng != null) {
   512                 try {
   513                     Locale l = new Locale(lng);
   514                     rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", l);
   515                     locale = l;
   516                 } catch (MissingResourceException e) {
   517                     // OK
   518                 }
   519         }
   520         if (rb == null) {
   521             for (Locale l : headers.getAcceptableLanguages()) {
   522                 try {
   523                     rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", l);
   524                     locale = l;
   525                     break;
   526                 } catch (MissingResourceException e) {
   527                     // OK
   528                 }
   529             }
   530         }
   531         if (rb == null) {
   532             rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", Locale.ENGLISH);
   533             locale = Locale.ENGLISH;
   534         }
   535 
   536         Map<String,Object> map = new HashMap<String,Object>();
   537         class ConvertToDate extends HashMap<Object,Object> {
   538             @Override
   539             public Object get(Object o) {
   540                 long time = Long.parseLong(o.toString());
   541                 return new Date(time);
   542             }
   543         }
   544 
   545         map.put("locale", locale.toString());
   546         map.put("doc", doc);
   547         if (user != null) {
   548             map.put("user", user.getId());
   549             map.put("email", user.getProperty("email"));
   550         }
   551         map.put("bundle", rb);
   552         map.put("toDate", new ConvertToDate());
   553         map.put("now", System.currentTimeMillis());
   554         map.put("version", version);
   555         for (int i = 0; i < more.length; i += 2) {
   556             map.put((String)more[i],more[i + 1]);
   557         }
   558         return new Viewable(page, map);
   559     }
   560 
   561 
   562     private static boolean isMobile(HttpHeaders headers) {
   563         final String[] keywords = {
   564             "Profile/MIDP",
   565         };
   566         List<String> agent = headers.getRequestHeader(HttpHeaders.USER_AGENT);
   567         if (agent != null) {
   568             for (String a : agent) {
   569                 for (String k : keywords) {
   570                     if (a.contains(k)) {
   571                         return true;
   572                     }
   573                 }
   574             }
   575         }
   576         return false;
   577     }
   578 
   579 }