/* * Copyright 2013- Yan Bonnel * * 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 fr.ybonnel.simpleweb4j; import fr.ybonnel.simpleweb4j.handlers.FunctionnalRoute; import fr.ybonnel.simpleweb4j.handlers.FunctionnalRouteUtil; import fr.ybonnel.simpleweb4j.handlers.FunctionnalRouteWithNoParam; import fr.ybonnel.simpleweb4j.handlers.HttpMethod; import fr.ybonnel.simpleweb4j.handlers.SimpleWeb4JWSHandler; import fr.ybonnel.simpleweb4j.handlers.SimpleWeb4jHandler; import fr.ybonnel.simpleweb4j.handlers.LessCompilerHandler; import fr.ybonnel.simpleweb4j.handlers.Route; import fr.ybonnel.simpleweb4j.handlers.WebSocketRoute; import fr.ybonnel.simpleweb4j.handlers.filter.AbstractFilter; import fr.ybonnel.simpleweb4j.handlers.resource.RestResource; import fr.ybonnel.simpleweb4j.handlers.websocket.WebSocketAdapter; import fr.ybonnel.simpleweb4j.model.SimpleEntityManager; import fr.ybonnel.simpleweb4j.server.SimpleWeb4jServer; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.AbstractHandler; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * <p>This is the entry point for all your uses of SimpleWeb4j.</p> * <p> * Sample to use SimpleWeb4j : * <pre>{@code * public class HelloWorld { * public static void startServer(int port) { * setPort(port); * setPublicResourcesPath("/fr/ybonnel/simpleweb4j/samples/helloworld"); * start(); * } * <p> * public static void main(String[] args) { * startServer(9999); * } * } }</pre> */ public final class SimpleWeb4j { /** * Private constructor to avoid instantiation. */ private SimpleWeb4j() { } /** * Used to know if SimpleWeb4j is already initialized. */ private static boolean initialized = false; /** * Used to know if SimpleWeb4jServer is started. */ private static boolean started = false; /** * The server. */ private static SimpleWeb4jServer server; /** * Path to public resources in classPath. */ private static String publicResourcesPath = "/public"; /** * Path to public resources in external (filesystem). */ private static String externalPublicResourcesPath = null; /** * Default http port. */ public static final int DEFAULT_PORT = 9999; /** * Http port. */ private static int port = DEFAULT_PORT; /** * Handler for all request others than static files. */ private static SimpleWeb4jHandler simpleWeb4jHandler = new SimpleWeb4jHandler(); /** * Handler to compile less. */ private static LessCompilerHandler lessCompilerHandler = new LessCompilerHandler(); /** * Handler for websocket. */ private static SimpleWeb4JWSHandler webSocketHandler = new SimpleWeb4JWSHandler(); /** * List of all internal handlers. */ private static List<AbstractHandler> simpleWeb4jHandlers = Arrays.asList(simpleWeb4jHandler, webSocketHandler, lessCompilerHandler); /** * Test usage. */ protected static void resetDefaultValues() { port = DEFAULT_PORT; publicResourcesPath = "/public"; externalPublicResourcesPath = null; lessCompilerHandler.setPublicResourcePath(publicResourcesPath); initialized = false; handlers = new ArrayList<>(simpleWeb4jHandlers); simpleWeb4jHandler.resetFilters(); setEntitiesClasses(); } /** * Change the port of SimpleWeb4j (default port is 9999). * * @param newPort the port you want. */ public static void setPort(int newPort) { if (initialized) { throw new IllegalStateException("You must set port before settings any route"); } port = newPort; } /** * <p>Change the path to public resources in classPath.</p> * Use : <code>setPublicResourcesPath("/fr/simpleweb4j/mypublicresources");</code> * * @param newPublicResourcesPath the path you want. */ public static void setPublicResourcesPath(String newPublicResourcesPath) { if (initialized) { throw new IllegalStateException("You must set public resources path before settings any route"); } publicResourcesPath = newPublicResourcesPath; lessCompilerHandler.setPublicResourcePath(publicResourcesPath); } /** * <p>Change the path to public resources external (in filesystem).</p> * Use : <code>setPublicResourcesPath("/var/www/mysite");</code> * * @param newExternalPublicResourcesPath the path you want. */ public static void setExternalPublicResourcesPath(String newExternalPublicResourcesPath) { if (initialized) { throw new IllegalStateException("You must set public resources path before settings any route"); } externalPublicResourcesPath = newExternalPublicResourcesPath; } /** * Change the path to your hibernate config file. * Simple web have his default hibernate config file which is "fr/ybonnel/simpleweb4j/model/hibernate.cfg.xml". * * @param hibernateCfgPath the path you want. */ public static void setHibernateCfgPath(String hibernateCfgPath) { if (initialized) { throw new IllegalStateException("You must set hibernate cfg path resources path before settings any route"); } SimpleEntityManager.setCfgPath(hibernateCfgPath); } /** * Set entities classes for hibernate configuration. * * @param entitiesClasses all entities. */ public static void setEntitiesClasses(Class<?>... entitiesClasses) { if (initialized) { throw new IllegalStateException("You must set entities classes before settings any route"); } SimpleEntityManager.setEntitiesClasses(Arrays.asList(entitiesClasses)); } /** * Handlers for jetty server. */ private static List<Handler> handlers = new ArrayList<Handler>(simpleWeb4jHandlers); /** * Add you specific handler. * * @param handler your handler. */ public static void addSpecificHandler(Handler handler) { if (initialized) { throw new IllegalStateException("You must add your handlers before settings any route"); } handlers.add(handler); } /** * Add a filter. * Filters are called in the add order. * * @param filter filter to add. */ public static void addFilter(AbstractFilter filter) { simpleWeb4jHandler.addFilter(filter); } /** * Initialize the server. */ protected static void init() { if (!initialized) { server = new SimpleWeb4jServer(port, publicResourcesPath, externalPublicResourcesPath, handlers); initialized = true; } } /** * Start the server. * This method wait the stop of server to finish. */ public static void start() { start(true); } /** * Start the server. * * @param waitStop true if you want wait the stop of server, false otherwise. */ public static void start(boolean waitStop) { init(); started = true; server.start(waitStop); } /** * Stop the server. */ public static void stop() { if (!started) { throw new IllegalStateException("You must start server before stop it!"); } server.stop(); initialized = false; started = false; } /** * Add a new route for GET method. * Use : * <pre>{@code * get(new Route<Void, String>("/resource", Void.class) { * public Response<String> handle(Void param, RouteParameters routeParams) { * return new Response<>("Hello World"); * } * }); }</pre> * * @param route your route. */ private static void get(Route route) { simpleWeb4jHandler.addRoute(HttpMethod.GET, route); } /** * Add a new route for GET method. * Use : * <pre>{@code * get("/resource", (param, routeParams) -> new Response<>("Hello World")); * }</pre> * * @param routePath path of route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void get(String routePath, FunctionnalRoute<Void, R> route) { get(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, Void.class)); } /** * Add a new route for GET method. * Use : * <pre>{@code * get("/resource", () -> new Response<>("Hello World")); * }</pre> * * @param routePath path of route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void get(String routePath, FunctionnalRouteWithNoParam<R> route) { get(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath)); } /** * Add a new route for GET method with jsonp support. * Use : * <pre>{@code * jsonp("CALLBACK", new Route<Void, String>("/resource", Void.class) { * public Response<String> handle(Void param, RouteParameters routeParams) { * return new Response<>("Hello World"); * } * }); }</pre> * * @param callbackName name of query param with callback function name * @param route your route. */ private static void jsonp(String callbackName, Route route) { simpleWeb4jHandler.addJsonpRoute(route, callbackName); } /** * Add a new route for GET method with jsonp support. * Use : * <pre>{@code * jsonp("CALLBACK", "/resource", (param, routeParams) -> new Response<>("Hello World")); * }</pre> * * @param callbackName name of query param with callback function name * @param routePath path of route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void jsonp(String callbackName, String routePath, FunctionnalRoute<Void, R> route) { jsonp(callbackName, FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, Void.class)); } /** * Add a new route for GET method with jsonp support. * Use : * <pre>{@code * jsonp("CALLBACK", "/resource", () -> new Response<>("Hello World")); * }</pre> * * @param callbackName name of query param with callback function name * @param routePath path of route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void jsonp(String callbackName, String routePath, FunctionnalRouteWithNoParam<R> route) { jsonp(callbackName, FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath)); } /** * Add a new route for POST method. * The request body is transform from json to object and path to param. * Use : * <pre>{@code * post(new Route<String, String>("/resource", String.class) { * public Response<String> handle(String param, RouteParameters routeParams) { * return new Response<>(param); * } * }); }</pre> * * @param route your route. */ private static void post(Route route) { simpleWeb4jHandler.addRoute(HttpMethod.POST, route); } /** * Add a new route for POST method. * The request body is transform from json to object and path to param. * Use : * <pre>{@code * post("/resource", String.class, (param, routeParams) -> new Response<>(param)); * }</pre> * * @param routePath routePath of the route. * @param paramType class of the object in request's body. * @param route your handle method. * @param <P> type of the object in request's body. * @param <R> type of the object to serialize in response body. */ public static <P, R> void post(String routePath, Class<P> paramType, FunctionnalRoute<P, R> route) { post(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, paramType)); } /** * Add a new route for POST method. * Use : * <pre>{@code * post("/resource", (param, routeParams) -> new Response<>("Hello World")); * }</pre> * * @param routePath routePath of the route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void post(String routePath, FunctionnalRoute<Void, R> route) { post(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, Void.class)); } /** * Add a new route for POST method. * Use : * <pre>{@code * post("/resource", () -> new Response<>("Hello World")); * }</pre> * * @param routePath routePath of the route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void post(String routePath, FunctionnalRouteWithNoParam<R> route) { post(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath)); } /** * Add a new route for PUT method. * The request body is transform from json to object and path to param. * Use : * <pre>{@code * put(new Route<String, String>("/resource", String.class) { * public Response<String> handle(String param, RouteParameters routeParams) { * return new Response<>(param); * } * }); }</pre> * * @param route your route. */ private static void put(Route route) { simpleWeb4jHandler.addRoute(HttpMethod.PUT, route); } /** * Add a new route for PUT method. * The request body is transform from json to object and path to param. * Use : * <pre>{@code * put("/resource", String.class, (param, routeParams) -> new Response<>(param)); * }</pre> * * @param routePath routePath of the route. * @param paramType class of the object in request's body. * @param route your handle method. * @param <P> type of the object in request's body. * @param <R> type of the object to serialize in response body. */ public static <P, R> void put(String routePath, Class<P> paramType, FunctionnalRoute<P, R> route) { put(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, paramType)); } /** * Add a new route for PUT method. * Use : * <pre>{@code * put("/resource", (param, routeParams) -> new Response<>("Hello world")); * }</pre> * * @param routePath routePath of the route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void put(String routePath, FunctionnalRoute<Void, R> route) { put(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, Void.class)); } /** * Add a new route for PUT method. * Use : * <pre>{@code * put("/resource", () -> new Response<>("Hello world")); * }</pre> * * @param routePath routePath of the route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void put(String routePath, FunctionnalRouteWithNoParam<R> route) { put(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath)); } /** * Add a new route for DELETE method. * The request body is transform from json to object and path to param. * Use : * <pre>{@code * delete(new Route<Void, String>("/resource", Void.class) { * public Response<String> handle(Void param, RouteParameters routeParams) { * return new Response<>("deleted"); * } * }); }</pre> * * @param route your route. */ private static void delete(Route route) { simpleWeb4jHandler.addRoute(HttpMethod.DELETE, route); } /** * Add a new route for DELETE method. * The request body is transform from json to object and path to param. * Use : * <pre>{@code * delete("/resource", String.class, (param, routeParams) -> new Response<>(param)); * }</pre> * * @param routePath routePath of the route. * @param paramType class of the object in request's body. * @param route your handle method. * @param <P> type of the object in request's body. * @param <R> type of the object to serialize in response body. */ public static <P, R> void delete(String routePath, Class<P> paramType, FunctionnalRoute<P, R> route) { delete(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, paramType)); } /** * Add a new route for DELETE method. * Use : * <pre>{@code * delete("/resource", (param, routeParams) -> new Response<>("DELETED")); * }</pre> * * @param routePath routePath of the route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void delete(String routePath, FunctionnalRoute<Void, R> route) { delete(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath, Void.class)); } /** * Add a new route for DELETE method. * Use : * <pre>{@code * delete("/resource", () -> new Response<>("DELETED")); * }</pre> * * @param routePath routePath of the route. * @param route your handle method. * @param <R> type of the object to serialize in response body. */ public static <R> void delete(String routePath, FunctionnalRouteWithNoParam<R> route) { delete(FunctionnalRouteUtil.functionnalRouteToRoute(route, routePath)); } /** * Add a route for a WebSocket. * * @param routePath routePath of the route. * @param adapter the WebSocketAdapter, SimpleWeb4j will call * {@link fr.ybonnel.simpleweb4j.handlers.websocket.WebSocketAdapter#createListenner( *fr.ybonnel.simpleweb4j.handlers.RouteParameters)} for each new WebSocket. * @param <I> type of the object sent by client to server. * @param <O> type of the object sent by server to client. */ public static <I, O> void websocket(String routePath, WebSocketAdapter<I, O> adapter) { webSocketHandler.addRoute(new WebSocketRoute<>(routePath, adapter)); } /** * Add a new RestResource. * Use : * <pre>{@code * resource(new RestResource<String>("string", String.class) { * {@literal @Override} * public String getById(String id) throws HttpErrorException { * return "myResource"; * } * <p> * {@literal @Override} * public List<String> getAll() throws HttpErrorException { * return new ArrayList<String>(); * } * <p> * {@literal @Override} * public void update(String id, String resource) throws HttpErrorException { * } * <p> * {@literal @Override} * public void create(String resource) throws HttpErrorException { * } * <p> * {@literal @Override} * public void delete(String id) throws HttpErrorException { * } * }); }</pre> * * @param restResource your REST resource. */ public static void resource(RestResource restResource) { get(restResource.routeGetById()); get(restResource.routeGetAll()); post(restResource.routeCreate()); delete(restResource.routeDelete()); put(restResource.routeUpdate()); } }