/******************************************************************************* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2013,2014 by Peter Pilgrim, Addiscombe, Surrey, XeNoNiQUe UK * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU GPL v3.0 * which accompanies this distribution, and is available at: * http://www.gnu.org/licenses/gpl-3.0.txt * * Developers: * Peter Pilgrim -- design, development and implementation * -- Blog: http://www.xenonique.co.uk/blog/ * -- Twitter: @peter_pilgrim * * Contributors: * *******************************************************************************/ package je7hb.intro.xentracker.control; import je7hb.intro.xentracker.boundary.ProjectTaskService; import je7hb.intro.xentracker.entity.Project; import je7hb.intro.xentracker.entity.Task; import javax.annotation.Resource; import javax.ejb.Stateless; import javax.enterprise.concurrent.ManagedExecutorService; import javax.inject.Inject; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.stream.JsonGenerator; import javax.json.stream.JsonGeneratorFactory; import javax.ws.rs.*; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.Suspended; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import static javax.ws.rs.core.MediaType.*; /** * The type ProjectRESTServerEndpoint * * @author Peter Pilgrim (peter) */ @Path("/projects/") @Stateless public class ProjectRESTServerEndpoint { static JsonGeneratorFactory jsonGeneratorFactory = Json.createGeneratorFactory( new HashMap<String, Object>() {{ put(JsonGenerator.PRETTY_PRINTING, true); }}); @Inject ProjectTaskService service; @GET @Path("/item/{id}") @Produces(APPLICATION_JSON) public String retrieveProject( @PathParam("id") int projectId ) { if (projectId < 1) throw new RuntimeException( "Invalid projectId:["+projectId+"] supplied"); List<Project> projects = service.findProjectById( projectId ); if ( projects.isEmpty() ) { throw new RuntimeException( "No project was found with projectId:["+projectId+"]"); } StringWriter swriter = new StringWriter(); JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, projects.get(0)).close(); return swriter.toString(); } @POST @Path("/item") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public String createProject( JsonObject projectObject ) throws Exception { Project project = new Project( projectObject.getString("name"), projectObject.getString("headline"), projectObject.getString("description")); JsonArray tasksArray = projectObject.getJsonArray("tasks"); if ( tasksArray != null ) { for ( int j=0; j<tasksArray.size(); ++j ) { JsonObject taskObject = tasksArray.getJsonObject(j); Task task = new Task( taskObject.getString("name"), ( taskObject.containsKey("targetDate") ? ProjectHelper.FMT.parse(taskObject.getString("targetDate")) : null ), taskObject.getBoolean("completed")); project.addTask(task); task.setProject(project); } } service.saveProject(project); StringWriter swriter = new StringWriter(); JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, project).close(); return swriter.toString(); } @PUT @Path("/item/{projectId}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public String updateProject( @PathParam("projectId") int projectId, JsonObject projectObject ) throws Exception { if (projectId < 1) throw new RuntimeException( "Invalid projectId:["+projectId+"] supplied"); final List<Project> projects = service.findProjectById( projectId ); if ( projects.isEmpty() ) { throw new RuntimeException( "No project was found with projectId:["+projectId+"]"); } Project project = projects.get(0); project.setName(projectObject.getString("name")); project.setHeadline(projectObject.getString("headline")); project.setDescription(projectObject.getString("description")); service.saveProject(project); final StringWriter swriter = new StringWriter(); final JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, project).close(); return swriter.toString(); } @POST @Path("/item/{projectId}/task") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public String createNewTaskOnProject( @PathParam("projectId") int projectId, JsonObject taskObject ) throws Exception { System.out.printf("createNewTaskOnProject( %d, %s )\n", projectId, taskObject); if (projectId < 1) throw new RuntimeException( "Invalid projectId:["+projectId+"] supplied"); List<Project> projects = service.findProjectById( projectId ); if ( projects.isEmpty() ) { throw new RuntimeException( "No project was found with projectId:["+projectId+"]"); } final Project project = projects.get(0); final Task task = new Task( taskObject.getString("name"), ( taskObject.containsKey("targetDate") ? ProjectHelper.convertToDate(taskObject.getString("targetDate")) : null ), ( taskObject.containsKey("completed")) ? taskObject.getBoolean("completed") : false ); project.addTask(task); service.saveProject(project); final StringWriter swriter = new StringWriter(); JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, project).close(); return swriter.toString(); } @PUT @Path("/item/{projectId}/task/{taskId}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public String updateTaskOnProject( @PathParam("projectId") int projectId, @PathParam("taskId") int taskId, JsonObject taskObject ) throws Exception { System.out.printf("updateTaskOnProject( %d, %d, %s )\n", projectId, taskId, taskObject); if (projectId < 1) throw new RuntimeException( "Invalid projectId:["+projectId+"] supplied"); final List<Project> projects = service.findProjectById( projectId ); if ( projects.isEmpty() ) { throw new RuntimeException( "No project was found with projectId:["+projectId+"]"); } final Project project = projects.get(0); for ( Task task: project.getTasks()) { if ( task.getId().equals(taskId )) { task.setName( taskObject.getString("name") ); task.setTargetDate( taskObject.containsKey("targetDate") ? ProjectHelper.convertToDate(taskObject.getString("targetDate")) : null ); task.setCompleted( taskObject.containsKey("completed") ? taskObject.getBoolean("completed") : false ); } } service.saveProject(project); final StringWriter swriter = new StringWriter(); final JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, project).close(); return swriter.toString(); } @DELETE @Path("/item/{projectId}/task/{taskId}") @Consumes( { APPLICATION_JSON, APPLICATION_XML, TEXT_PLAIN }) @Produces(APPLICATION_JSON) public String removeTaskFromProject( @PathParam("projectId") int projectId, @PathParam("taskId") int taskId, JsonObject taskObject ) throws Exception { // AngularJS requires additional consumption of XML in order to avoid 415 Unsupported Media Type // See this http://stackoverflow.com/questions/17379447/angularjs-and-jersey-rest-delete-operation-fails-with-415-status-code System.out.printf("removeTaskFromProject( %d, %d, %s )\n", projectId, taskId, taskObject); if (projectId < 1) throw new RuntimeException( "Invalid projectId:["+projectId+"] supplied"); final List<Project> projects = service.findProjectById( projectId ); if ( projects.isEmpty() ) { throw new RuntimeException( "No project was found with projectId:["+projectId+"]"); } final Project project = projects.get(0); for ( Task task: project.getTasks()) { if ( task.getId().equals(taskId )) { project.removeTask(task); break; } } service.saveProject(project); final StringWriter swriter = new StringWriter(); final JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, project).close(); return swriter.toString(); } @GET @Path("/item/{projectId}/task/{taskId}/delete") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public String angularRemoveTaskFromProject( @PathParam("projectId") int projectId, @PathParam("taskId") int taskId, JsonObject taskObject ) throws Exception { // AngularJS requires additional consumption of XML in order to avoid 415 Unsupported Media Type // See this http://stackoverflow.com/questions/17379447/angularjs-and-jersey-rest-delete-operation-fails-with-415-status-code System.out.printf("angularRemoveTaskFromProject( %d, %d, %s )\n", projectId, taskId, taskObject); if (projectId < 1) throw new RuntimeException( "Invalid projectId:["+projectId+"] supplied"); final List<Project> projects = service.findProjectById( projectId ); if ( projects.isEmpty() ) { throw new RuntimeException( "No project was found with projectId:["+projectId+"]"); } final Project project = projects.get(0); for ( Task task: project.getTasks()) { if ( task.getId().equals(taskId )) { project.removeTask(task); break; } } service.saveProject(project); final StringWriter swriter = new StringWriter(); final JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); ProjectHelper.writeProjectAsJson(generator, project).close(); return swriter.toString(); } @Resource(name="concurrent/LongRunningTasksExecutor") ManagedExecutorService executor; @GET @Produces(MediaType.APPLICATION_JSON) @Path("/list") public void getProjectList( @Suspended final AsyncResponse asyncResponse) { System.out.printf("=======> %s.getProjectList() %s asyncResponse=%s\n", getClass().getSimpleName(), Thread.currentThread(), asyncResponse ); executor.submit(new Runnable() { @Override public void run() { System.out.printf("========>> %s.getProjectList() Executable Task %s asyncResponse=%s\n", getClass().getSimpleName(), Thread.currentThread(), asyncResponse); final List<Project> projects = service.findAllProjects(); final StringWriter swriter = new StringWriter(); final JsonGenerator generator = jsonGeneratorFactory.createGenerator(swriter); try { ProjectHelper.generateProjectsAsJson(generator, projects).close(); System.out.printf("========>> Sending swriter=[%s]\n", swriter.toString()); final Response response = Response.ok(swriter.toString()).build(); asyncResponse.resume(response); } catch (Throwable t) { t.printStackTrace(); } } }); // We add this slight delay to ensure the Arquillian integration unit test receives the result. try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } // Utility methods }