raimund@441: /* Copyright (C) 2013 by Bundesamt fuer Strahlenschutz raimund@441: * Software engineering by Intevation GmbH raimund@441: * rrenkert@628: * This file is Free Software under the GNU GPL (v>=3) rrenkert@628: * and comes with ABSOLUTELY NO WARRANTY! Check out rrenkert@628: * the documentation coming with IMIS-Labordaten-Application for details. raimund@441: */ raimund@441: package de.intevation.lada.rest; raimund@441: raimund@450: import java.util.List; raimund@572: import java.util.Map; raimund@450: raimund@700: import javax.ejb.EJBTransactionRolledbackException; raimund@441: import javax.enterprise.context.RequestScoped; raimund@441: import javax.inject.Inject; raimund@455: import javax.json.JsonObject; raimund@700: import javax.persistence.TransactionRequiredException; raimund@582: import javax.servlet.http.HttpServletRequest; raimund@450: import javax.ws.rs.DELETE; raimund@441: import javax.ws.rs.GET; raimund@450: import javax.ws.rs.POST; raimund@450: import javax.ws.rs.PUT; raimund@441: import javax.ws.rs.Path; raimund@441: import javax.ws.rs.PathParam; raimund@441: import javax.ws.rs.Produces; raimund@441: import javax.ws.rs.core.Context; raimund@441: import javax.ws.rs.core.HttpHeaders; raimund@441: import javax.ws.rs.core.MediaType; raimund@455: import javax.ws.rs.core.MultivaluedMap; raimund@455: import javax.ws.rs.core.UriInfo; raimund@441: raimund@657: import de.intevation.lada.factory.ProbeFactory; raimund@595: import de.intevation.lada.lock.LockConfig; raimund@595: import de.intevation.lada.lock.LockType; raimund@595: import de.intevation.lada.lock.ObjectLocker; tom@1072: import de.intevation.lada.model.land.Messprogramm; tom@1097: import de.intevation.lada.model.land.Probe; raimund@455: import de.intevation.lada.query.QueryTools; raimund@441: import de.intevation.lada.util.annotation.AuthorizationConfig; raimund@441: import de.intevation.lada.util.annotation.RepositoryConfig; raimund@441: import de.intevation.lada.util.auth.Authorization; raimund@441: import de.intevation.lada.util.auth.AuthorizationType; raimund@450: import de.intevation.lada.util.data.QueryBuilder; raimund@441: import de.intevation.lada.util.data.Repository; raimund@441: import de.intevation.lada.util.data.RepositoryType; raimund@582: import de.intevation.lada.util.rest.RequestMethod; raimund@441: import de.intevation.lada.util.rest.Response; raimund@543: import de.intevation.lada.validation.Validator; raimund@543: import de.intevation.lada.validation.Violation; raimund@543: import de.intevation.lada.validation.annotation.ValidationConfig; raimund@441: raimund@451: /** rrenkert@628: * REST service for Probe objects. rrenkert@628: *

rrenkert@628: * The services produce data in the application/json media type. rrenkert@628: * All HTTP methods use the authorization module to determine if the user is rrenkert@628: * allowed to perform the requested action. rrenkert@628: * A typical response holds information about the action performed and the data. rrenkert@628: *

rrenkert@628:  * 
rrenkert@628:  * {
rrenkert@628:  *  "success": [boolean];
rrenkert@628:  *  "message": [string],
rrenkert@628:  *  "data":[{
rrenkert@628:  *      "id":[number],
rrenkert@628:  *      "baId": [string],
rrenkert@628:  *      "datenbasisId": [number],
rrenkert@628:  *      "letzteAenderung": [timestamp],
rrenkert@628:  *      "media": [string],
rrenkert@628:  *      "mediaDesk": [string],
rrenkert@628:  *      "mittelungsdauer": [number],
rrenkert@628:  *      "mstId": [string],
rrenkert@628:  *      "netzbetreiberId":[string],
rrenkert@628:  *      "probeentnahmeBeginn": [timestamp],
rrenkert@628:  *      "probeentnahmeEnde": [timestamp],
rrenkert@628:  *      "probenartId": [number],
rrenkert@628:  *      "test": [boolean],
rrenkert@628:  *      "umwId": [string],
rrenkert@628:  *      "hauptprobenNr": [string],
rrenkert@628:  *      "erzeugerId": [string],
rrenkert@628:  *      "mpKat": [string],
rrenkert@628:  *      "mplId": [number],
rrenkert@628:  *      "mprId": [number],
rrenkert@628:  *      "probeNehmerId": [number],
rrenkert@628:  *      "solldatumBeginn": [timestamp],
rrenkert@628:  *      "solldatumEnde": [timestamp],
rrenkert@628:  *      "treeModified": [timestamp],
rrenkert@628:  *      "readonly": [boolean],
rrenkert@628:  *      "owner": [boolean],
rrenkert@628:  *      "probeIdAlt": [string]
rrenkert@628:  *  }],
rrenkert@628:  *  "errors": [object],
rrenkert@628:  *  "warnings": [object],
rrenkert@628:  *  "readonly": [boolean],
rrenkert@628:  *  "totalCount": [number]
rrenkert@628:  * }
rrenkert@628:  * 
rrenkert@628:  * 
raimund@451: * raimund@451: * @author Raimund Renkert raimund@451: */ raimund@827: @Path("rest/probe") raimund@441: @RequestScoped raimund@441: public class ProbeService { raimund@441: rrenkert@628: /** rrenkert@628: * The data repository granting read/write access. rrenkert@628: */ raimund@441: @Inject raimund@441: @RepositoryConfig(type=RepositoryType.RW) raimund@848: private Repository repository; raimund@441: rrenkert@628: /** rrenkert@628: * The authorization module. rrenkert@628: */ raimund@441: @Inject raimund@722: @AuthorizationConfig(type=AuthorizationType.HEADER) raimund@441: private Authorization authorization; raimund@441: rrenkert@628: /** rrenkert@628: * The object lock mechanism. rrenkert@628: */ raimund@543: @Inject raimund@595: @LockConfig(type=LockType.TIMESTAMP) raimund@595: private ObjectLocker lock; raimund@595: rrenkert@628: /** raimund@657: * The validator used for Probe objects. rrenkert@628: */ raimund@595: @Inject raimund@543: @ValidationConfig(type="Probe") raimund@543: private Validator validator; raimund@543: raimund@451: /** raimund@657: * The factory to create Probe objects. raimund@657: * Used for messprogramm. raimund@657: */ raimund@657: @Inject raimund@657: private ProbeFactory factory; raimund@657: raimund@848: @Inject raimund@848: private QueryTools queryTools; raimund@848: raimund@657: /** rrenkert@628: * Get all Probe objects. rrenkert@628: *

rrenkert@628: * The requested objects can be filtered using the following URL rrenkert@628: * parameters:
rrenkert@628: * * qid: The id of the query.
rrenkert@628: * * page: The page to display in a paginated result grid.
rrenkert@628: * * start: The first Probe item.
rrenkert@628: * * limit: The count of Probe items.
rrenkert@628: * * sort: Sort the result ascending(ASC) or descenting (DESC).
rrenkert@628: *
rrenkert@628: * The response data contains a stripped set of Probe objects. The returned fields rrenkert@628: * are defined in the query used in the request. rrenkert@628: *

rrenkert@628: * Example: rrenkert@628: * http://example.com/probe?qid=[ID]&page=[PAGE]&start=[START]&limit=[LIMIT]&sort=[{"property":"probeId","direction":"ASC"}] raimund@451: * rrenkert@628: * @return Response object containing all Probe objects. raimund@451: */ raimund@455: @SuppressWarnings("unchecked") raimund@441: @GET raimund@441: @Path("/") raimund@441: @Produces("application/json") raimund@455: public Response get( raimund@455: @Context HttpHeaders headers, raimund@582: @Context UriInfo info, raimund@582: @Context HttpServletRequest request raimund@455: ) { raimund@455: MultivaluedMap params = info.getQueryParameters(); raimund@455: if (params.isEmpty() || !params.containsKey("qid")) { tom@1097: return repository.getAll(Probe.class, "land"); raimund@455: } raimund@848: Integer id = null; raimund@455: try { raimund@848: id = Integer.valueOf(params.getFirst("qid")); raimund@455: } raimund@848: catch (NumberFormatException e) { raimund@848: return new Response(false, 603, "Not a valid filter id"); raimund@455: } raimund@572: List> result = raimund@848: queryTools.getResultForQuery(params, id, "probe"); raimund@848: raimund@848: int size = result.size(); raimund@572: if (params.containsKey("start") && params.containsKey("limit")) { raimund@572: int start = Integer.valueOf(params.getFirst("start")); raimund@572: int limit = Integer.valueOf(params.getFirst("limit")); raimund@587: int end = limit + start; raimund@587: if (start + limit > result.size()) { raimund@587: end = result.size(); raimund@587: } raimund@848: result = result.subList(start, end); raimund@848: } raimund@848: tom@1097: QueryBuilder pBuilder = new QueryBuilder( tom@1097: repository.entityManager("land"), Probe.class); raimund@848: for (Map entry: result) { raimund@848: pBuilder.or("id", (Integer)entry.get("id")); raimund@848: } raimund@848: Response r = repository.filter(pBuilder.getQuery(), "land"); tom@1097: r = authorization.filter(request, r, Probe.class); tom@1097: List proben = (List)r.getData(); raimund@848: for (Map entry: result) { raimund@848: Integer pId = Integer.valueOf(entry.get("id").toString()); raimund@848: setAuthData(proben, entry, pId); raimund@848: } raimund@848: return new Response(true, 200, result, size); raimund@848: } raimund@848: raimund@848: private void setAuthData( tom@1097: List proben, raimund@848: Map entry, raimund@848: Integer id raimund@848: ) { raimund@848: for (int i = 0; i < proben.size(); i++) { raimund@848: if (id.equals(proben.get(i).getId())) { tom@1097: entry.put("readonly", proben.get(i).isReadonly()); tom@1097: entry.put("owner", proben.get(i).isOwner()); raimund@848: return; raimund@587: } raimund@572: } raimund@441: } raimund@441: raimund@451: /** rrenkert@628: * Get a single Probe object by id. rrenkert@628: *

rrenkert@628: * The id is appended to the URL as a path parameter. rrenkert@628: *

rrenkert@628: * Example: http://example.com/probe/{id} raimund@451: * rrenkert@628: * @return Response object containing a single Probe. raimund@451: */ raimund@441: @GET raimund@441: @Path("/{id}") raimund@441: @Produces(MediaType.APPLICATION_JSON) raimund@441: public Response getById( raimund@441: @Context HttpHeaders headers, raimund@582: @PathParam("id") String id, raimund@582: @Context HttpServletRequest request raimund@441: ) { raimund@543: Response response = tom@1097: repository.getById(Probe.class, Integer.valueOf(id), "land"); raimund@543: Violation violation = validator.validate(response.getData()); raimund@543: if (violation.hasWarnings()) { raimund@543: response.setWarnings(violation.getWarnings()); raimund@543: } tom@1097: return this.authorization.filter(request, response, Probe.class); raimund@441: } raimund@441: raimund@451: /** rrenkert@628: * Create a new Probe object. rrenkert@628: *

rrenkert@628: * The new object is embedded in the post data as JSON formatted string. rrenkert@628: *

rrenkert@628: *

rrenkert@628:      * 
rrenkert@628:      * {
rrenkert@628:      *  "probeIdAlt": [string],
rrenkert@628:      *  "hauptprobenNr": [string],
rrenkert@628:      *  "test": [boolean],
rrenkert@628:      *  "netzbetreiberId": [string],
rrenkert@628:      *  "mstId": [string],
rrenkert@628:      *  "datenbasisId": [number],
rrenkert@628:      *  "baId": [string],
rrenkert@628:      *  "probenartId": [number],
rrenkert@628:      *  "mediaDesk": [string],
rrenkert@628:      *  "media": [string],
rrenkert@628:      *  "umwId": [string],
rrenkert@628:      *  "mittelungsdauer": [number],
rrenkert@628:      *  "erzeugerId":[string],
rrenkert@628:      *  "probeNehmerId": [number],
rrenkert@628:      *  "mpKat": [string],
rrenkert@628:      *  "mplId": [number],
rrenkert@628:      *  "mprId": [number],
rrenkert@628:      *  "treeModified":null,
rrenkert@628:      *  "probeentnahmeBeginn": [date],
rrenkert@628:      *  "probeentnahmeEnde": [date],
rrenkert@628:      *  "letzteAenderung": [date],
rrenkert@628:      *  "solldatumBeginn": [date],
rrenkert@628:      *  "solldatumEnde": [date]
rrenkert@628:      * }
rrenkert@628:      * 
rrenkert@628:      * 
raimund@451: * raimund@451: * @return Response object containing the new probe object. raimund@451: */ raimund@450: @POST raimund@450: @Path("/") raimund@450: @Produces(MediaType.APPLICATION_JSON) raimund@582: public Response create( raimund@582: @Context HttpHeaders headers, raimund@582: @Context HttpServletRequest request, tom@1097: Probe probe raimund@582: ) { raimund@582: if (!authorization.isAuthorized( raimund@582: request, raimund@582: probe, raimund@582: RequestMethod.POST, tom@1097: Probe.class) raimund@582: ) { raimund@450: return new Response(false, 699, null); raimund@450: } raimund@543: Violation violation = validator.validate(probe); raimund@543: if (violation.hasErrors()) { raimund@543: Response response = new Response(false, 604, probe); raimund@543: response.setErrors(violation.getErrors()); raimund@543: response.setWarnings(violation.getWarnings()); raimund@543: return response; raimund@543: } raimund@695: if (probe.getUmwId() == null || probe.getUmwId().equals("")) { raimund@697: probe = factory.findUmweltId(probe); raimund@695: } mstanko@738: probe = factory.findMediaDesk(probe); tom@1290: raimund@450: /* Persist the new probe object*/ raimund@848: Response newProbe = repository.create(probe, "land"); tom@1290: raimund@543: if(violation.hasWarnings()) { tom@1290: newProbe.setWarnings(violation.getWarnings()); raimund@543: } raimund@601: return authorization.filter( raimund@601: request, tom@1290: newProbe, tom@1097: Probe.class); raimund@450: } raimund@450: raimund@450: /** raimund@657: * Create new Probe objects from a messprogramm. raimund@657: *

raimund@657: *

raimund@657: *

raimund@657:      * 
raimund@657:      * {
raimund@711:      *  "id": [number],
raimund@711:      *  "start": [timestamp],
raimund@711:      *  "end": [timestamp]
raimund@657:      * }
raimund@657:      * 
raimund@657:      * 
raimund@657: * raimund@657: * @return Response object containing the new probe object. raimund@657: */ raimund@657: @POST raimund@657: @Path("/messprogramm") raimund@657: @Produces(MediaType.APPLICATION_JSON) raimund@657: public Response createFromMessprogramm( raimund@657: @Context HttpHeaders headers, raimund@657: @Context HttpServletRequest request, raimund@657: JsonObject object raimund@657: ) { tom@1072: int id = object.getInt("id"); tom@1072: Messprogramm messprogramm = repository.getByIdPlain( tom@1072: Messprogramm.class, id, "land"); tom@1072: if (messprogramm == null) { tom@1072: return new Response(false, 600, null); tom@1072: } tom@1072: tom@1072: /* Allow generation of Probe objects only for a Messprogramm tom@1072: * that would be allowed to be changed. */ tom@1072: if (!authorization.isAuthorized( tom@1072: request, tom@1072: messprogramm, tom@1072: RequestMethod.PUT, tom@1072: Messprogramm.class) tom@1072: ) { tom@1072: return new Response(false, 699, null); tom@1072: } tom@1072: tom@1068: long start = 0; tom@1068: long end = 0; tom@1068: try { tom@1068: start = object.getJsonNumber("start").longValue(); tom@1068: end = object.getJsonNumber("end").longValue(); tom@1068: } catch (ClassCastException e) { tom@1068: // Catch invalid (i.e. too high) time values tom@1068: return new Response(false, 612, null); tom@1068: } tom@1067: if (start > end) { tom@1067: return new Response(false, 662, null); tom@1067: } tom@1097: List proben = factory.create( tom@1072: messprogramm, raimund@657: start, raimund@657: end); raimund@657: return new Response(true, 200, proben); raimund@657: } raimund@657: raimund@657: /** rrenkert@628: * Update an existing Probe object. rrenkert@628: *

rrenkert@628: * The object to update should come as JSON formatted string. rrenkert@628: *

rrenkert@628:      * 
rrenkert@628:      * {
rrenkert@628:      *  "id": [number],
rrenkert@628:      *  "probeIdAlt": [string],
rrenkert@628:      *  "hauptprobenNr": [string],
rrenkert@628:      *  "test": [boolean],
rrenkert@628:      *  "netzbetreiberId": [string],
rrenkert@628:      *  "mstId": [string],
rrenkert@628:      *  "datenbasisId": [number],
rrenkert@628:      *  "baId": [string],
rrenkert@628:      *  "probenartId": [number],
rrenkert@628:      *  "mediaDesk": [string],
rrenkert@628:      *  "media": [string],
rrenkert@628:      *  "umwId": [string],
rrenkert@628:      *  "mittelungsdauer": [number],
rrenkert@628:      *  "erzeugerId": [number],
rrenkert@628:      *  "probeNehmerId": [number],
rrenkert@628:      *  "mpKat": [string],
rrenkert@628:      *  "mplId": [number],
rrenkert@628:      *  "mprId": [number],
rrenkert@628:      *  "treeModified": [timestamp],
rrenkert@628:      *  "probeentnahmeBeginn": [date],
rrenkert@628:      *  "probeentnahmeEnde": [date],
rrenkert@628:      *  "letzteAenderung": [date],
rrenkert@628:      *  "solldatumBeginn": [date],
rrenkert@628:      *  "solldatumEnde":[date]
rrenkert@628:      * }
rrenkert@628:      * 
rrenkert@628:      * 
raimund@450: * rrenkert@628: * @return Response object containing the updated Probe object. raimund@450: */ raimund@450: @PUT raimund@557: @Path("/{id}") raimund@450: @Produces(MediaType.APPLICATION_JSON) raimund@582: public Response update( raimund@582: @Context HttpHeaders headers, raimund@582: @Context HttpServletRequest request, tom@1097: @PathParam("id") String id, tom@1097: Probe probe raimund@582: ) { raimund@582: if (!authorization.isAuthorized( raimund@582: request, raimund@582: probe, raimund@582: RequestMethod.PUT, tom@1097: Probe.class) raimund@582: ) { raimund@450: return new Response(false, 699, null); raimund@450: } raimund@595: if (lock.isLocked(probe)) { raimund@595: return new Response(false, 697, null); raimund@595: } raimund@840: if (probe.getMediaDesk() == null || probe.getMediaDesk() == "") { raimund@840: probe = factory.findMediaDesk(probe); raimund@840: } raimund@543: Violation violation = validator.validate(probe); raimund@543: if (violation.hasErrors()) { raimund@561: Response response = new Response(false, 604, null); raimund@543: response.setErrors(violation.getErrors()); raimund@543: response.setWarnings(violation.getWarnings()); raimund@543: return response; raimund@543: } raimund@840: if (probe.getUmwId() == null || probe.getUmwId() == "") { raimund@840: factory.findUmweltId(probe); raimund@840: } raimund@848: Response response = repository.update(probe, "land"); tom@949: if (!response.getSuccess()) { tom@949: return response; tom@949: } raimund@848: Response updated = repository.getById( tom@1097: Probe.class, tom@1097: ((Probe)response.getData()).getId(), "land"); raimund@543: if (violation.hasWarnings()) { raimund@543: updated.setWarnings(violation.getWarnings()); raimund@543: } raimund@601: return authorization.filter( raimund@601: request, raimund@601: updated, tom@1097: Probe.class); raimund@450: } raimund@450: raimund@450: /** rrenkert@628: * Delete an existing Probe object by id. rrenkert@628: *

rrenkert@628: * The id is appended to the URL as a path parameter. rrenkert@628: *

rrenkert@628: * Example: http://example.com/probe/{id} raimund@450: * raimund@450: * @return Response object. raimund@450: */ raimund@450: @DELETE raimund@450: @Path("/{id}") raimund@450: @Produces(MediaType.APPLICATION_JSON) raimund@450: public Response delete( raimund@450: @Context HttpHeaders headers, raimund@582: @Context HttpServletRequest request, raimund@450: @PathParam("id") String id raimund@450: ) { raimund@450: /* Get the probe object by id*/ raimund@450: Response probe = tom@1097: repository.getById(Probe.class, Integer.valueOf(id), "land"); tom@1125: if (!probe.getSuccess()) { tom@1125: return probe; tom@1125: } tom@1097: Probe probeObj = (Probe)probe.getData(); raimund@582: if (!authorization.isAuthorized( raimund@582: request, raimund@582: probeObj, raimund@582: RequestMethod.DELETE, tom@1097: Probe.class) raimund@582: ) { raimund@582: return new Response(false, 699, null); raimund@582: } raimund@450: /* Delete the probe object*/ raimund@700: try { raimund@848: Response response = repository.delete(probeObj, "land"); raimund@700: return response; raimund@700: } raimund@700: catch(IllegalArgumentException | EJBTransactionRolledbackException | raimund@700: TransactionRequiredException e) { raimund@700: return new Response(false, 600, ""); raimund@700: } raimund@441: } raimund@441: }