raimund@1301: /* Copyright (C) 2013 by Bundesamt fuer Strahlenschutz raimund@1301: * Software engineering by Intevation GmbH raimund@1301: * raimund@1301: * This file is Free Software under the GNU GPL (v>=3) raimund@1301: * and comes with ABSOLUTELY NO WARRANTY! Check out raimund@1301: * the documentation coming with IMIS-Labordaten-Application for details. raimund@1301: */ raimund@1301: package de.intevation.lada.rest; raimund@1301: raimund@1309: import java.text.DateFormat; raimund@1309: import java.text.ParseException; raimund@1309: import java.text.SimpleDateFormat; raimund@1309: import java.util.Date; raimund@1301: import java.util.HashMap; raimund@1301: import java.util.Iterator; raimund@1301: import java.util.List; raimund@1301: import java.util.Map; raimund@1301: raimund@1301: import javax.annotation.PostConstruct; raimund@1301: import javax.enterprise.context.RequestScoped; raimund@1301: import javax.inject.Inject; raimund@1301: import javax.persistence.EntityManager; raimund@1301: import javax.servlet.http.HttpServletRequest; raimund@1301: import javax.ws.rs.GET; raimund@1301: import javax.ws.rs.Path; raimund@1301: import javax.ws.rs.PathParam; raimund@1301: import javax.ws.rs.Produces; raimund@1301: import javax.ws.rs.core.Context; raimund@1301: import javax.ws.rs.core.MediaType; raimund@1301: raimund@1301: import org.apache.log4j.Logger; raimund@1301: raimund@1301: import com.fasterxml.jackson.databind.ObjectMapper; raimund@1301: import com.fasterxml.jackson.databind.node.ArrayNode; raimund@1301: import com.fasterxml.jackson.databind.node.ObjectNode; raimund@1301: raimund@1301: import de.intevation.lada.model.land.AuditTrailMessung; raimund@1301: import de.intevation.lada.model.land.AuditTrailProbe; raimund@1301: import de.intevation.lada.model.land.Messung; raimund@1301: import de.intevation.lada.model.land.Probe; raimund@1301: import de.intevation.lada.util.annotation.AuthorizationConfig; raimund@1301: import de.intevation.lada.util.annotation.RepositoryConfig; raimund@1301: import de.intevation.lada.util.auth.Authorization; raimund@1301: import de.intevation.lada.util.auth.AuthorizationType; raimund@1301: import de.intevation.lada.util.data.QueryBuilder; raimund@1301: import de.intevation.lada.util.data.Repository; raimund@1301: import de.intevation.lada.util.data.RepositoryType; raimund@1301: raimund@1301: /** raimund@1301: * REST service for AuditTrail. raimund@1301: *

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

raimund@1301:  * 
raimund@1301:  * {
raimund@1301:  *  "success": [boolean];
raimund@1301:  *  "message": [string],
raimund@1301:  *  "data":[{
raimund@1301:  *      "id": [number],
raimund@1301:  *      "identifier: [string]
raimund@1301:  *      "audit": [array]
raimund@1301:  *  }],
raimund@1301:  * }
raimund@1301:  * 
raimund@1301:  * 
raimund@1301: * raimund@1301: * @author Raimund Renkert raimund@1301: */ raimund@1301: @Path("rest/audit") raimund@1301: @RequestScoped raimund@1301: public class AuditTrailService { raimund@1301: raimund@1301: /** raimund@1301: * Class to store tablename and value field for foreign key mappings. raimund@1301: */ raimund@1301: private class TableMapper { raimund@1301: public String mappingTable; raimund@1301: public String valueField; raimund@1301: raimund@1301: public TableMapper( raimund@1301: String mappingTable, raimund@1301: String valueField raimund@1301: ) { raimund@1301: this.mappingTable = mappingTable; raimund@1301: this.valueField = valueField; raimund@1301: } raimund@1301: } raimund@1301: raimund@1301: @Inject Logger logger; raimund@1301: /** raimund@1301: * The data repository granting read/write access. raimund@1301: */ raimund@1301: @Inject raimund@1301: @RepositoryConfig(type=RepositoryType.RO) raimund@1301: private Repository repository; raimund@1301: raimund@1301: /** raimund@1301: * The authorization module. raimund@1301: */ raimund@1301: @Inject raimund@1301: @AuthorizationConfig(type=AuthorizationType.HEADER) raimund@1301: private Authorization authorization; raimund@1301: raimund@1301: /** raimund@1301: * Map foreign key to their associated table and the display value. raimund@1301: */ raimund@1301: private Map mappings; raimund@1301: raimund@1301: /** raimund@1301: * Initialize the object with key <-> table mappings. raimund@1301: */ raimund@1301: @PostConstruct raimund@1301: public void initialize() { raimund@1301: mappings = new HashMap(); raimund@1301: mappings.put("messgroesse_id", raimund@1301: new TableMapper("messgroesse", "messgroesse")); raimund@1301: mappings.put("meh_id", raimund@1301: new TableMapper("mess_einheit", "einheit")); raimund@1301: mappings.put("ort_id", raimund@1301: new TableMapper("ort", "ort_id")); raimund@1301: mappings.put("datenbasis_id", raimund@1301: new TableMapper("datenbasis", "datenbasis")); raimund@1301: mappings.put("ba_id", raimund@1301: new TableMapper("betriebsart", "name")); raimund@1301: mappings.put("mpl_id", raimund@1301: new TableMapper("messprogramm_kategorie", "code")); raimund@1301: mappings.put("probenart_id", raimund@1301: new TableMapper("probenart", "probenart")); raimund@1301: mappings.put("probe_nehmer_id", raimund@1301: new TableMapper("probenehmer", "prn_id")); raimund@1309: mappings.put("probeentnahme_beginn", raimund@1309: new TableMapper("date", "dd.MM.yy HH:mm")); raimund@1309: mappings.put("probeentnahme_ende", raimund@1309: new TableMapper("date", "dd.MM.yy HH:mm")); raimund@1301: } raimund@1301: raimund@1301: /** raimund@1301: * Service to generate audit trail for probe objects. raimund@1301: */ raimund@1301: @GET raimund@1301: @Path("/probe/{id}") raimund@1301: @Produces(MediaType.APPLICATION_JSON) raimund@1301: public String getProbe( raimund@1301: @Context HttpServletRequest request, raimund@1301: @PathParam("id") String id raimund@1301: ) { raimund@1301: if (id == null || "".equals(id)) { raimund@1301: String ret = "{\"success\": false," + raimund@1301: "\"message\":698,\"data\":null}"; raimund@1301: return ret; raimund@1301: } raimund@1301: Integer pId = null; raimund@1301: try { raimund@1301: pId = Integer.valueOf(id); raimund@1301: } raimund@1301: catch(NumberFormatException nfe) { raimund@1301: String ret = "{\"success\": false," + raimund@1301: "\"message\":600,\"data\":null}"; raimund@1301: return ret; raimund@1301: } raimund@1301: raimund@1301: // Get all entries for the probe and its sub objects. raimund@1301: QueryBuilder builder = raimund@1301: new QueryBuilder( raimund@1301: repository.entityManager("land"), raimund@1301: AuditTrailProbe.class); raimund@1301: builder.and("objectId", id); raimund@1301: builder.and("tableName", "probe"); raimund@1301: builder.or("probeId", id); raimund@1301: builder.orderBy("tstamp", true); raimund@1301: List audit = raimund@1301: repository.filterPlain(builder.getQuery(), "land"); raimund@1301: raimund@1301: // Get the plain probe object to have the hauptproben_nr. raimund@1301: // If only subobjects raimund@1301: Probe probe = repository.getByIdPlain(Probe.class, pId, "land"); raimund@1301: // Create an empty JsonObject raimund@1301: ObjectMapper mapper = new ObjectMapper(); raimund@1301: ObjectNode responseNode = mapper.createObjectNode(); raimund@1301: responseNode.put("success", true); raimund@1301: responseNode.put("message", 200); raimund@1301: ObjectNode auditJson = responseNode.putObject("data"); raimund@1301: ArrayNode entries = auditJson.putArray("audit"); raimund@1301: auditJson.put("id", probe.getId()); raimund@1301: auditJson.put("identifier", probe.getHauptprobenNr()); raimund@1301: for (AuditTrailProbe a : audit) { raimund@1301: entries.add(createEntry(a, mapper)); raimund@1301: } raimund@1301: return responseNode.toString(); raimund@1301: } raimund@1301: raimund@1301: /** raimund@1301: * Create a JSON object for an AuditTrailProbe entry. raimund@1301: * raimund@1301: * @param audit The table entry raimund@1301: * @param mapper JSON object mapper raimund@1301: */ raimund@1301: private ObjectNode createEntry(AuditTrailProbe audit, ObjectMapper mapper) { raimund@1301: ObjectNode node = mapper.createObjectNode(); raimund@1301: node.put("timestamp", audit.getTstamp().getTime()); raimund@1301: node.put("type", audit.getTableName()); raimund@1308: node.put("action", audit.getAction()); raimund@1301: ObjectNode data = (ObjectNode)audit.getChangedFields(); raimund@1309: data = translateValues(data); raimund@1301: node.putPOJO("changedFields", data); raimund@1301: if ("kommentar_p".equals(audit.getTableName())) { raimund@1301: node.put("identifier", audit.getRowData().get("datum").toString()); raimund@1301: } raimund@1301: if ("zusatz_wert".equals(audit.getTableName())) { raimund@1301: node.put("identifier", audit.getRowData().get("pzs_id").toString()); raimund@1301: } raimund@1304: if ("ortszuordnung".equals(audit.getTableName())) { raimund@1301: String value = translateId( raimund@1301: "ort", raimund@1301: "ort_id", raimund@1301: audit.getRowData().get("ort_id").toString(), raimund@1301: "id", raimund@1301: "stamm"); raimund@1301: node.put("identifier", value); raimund@1301: } raimund@1301: if ("messung".equals(audit.getTableName())) { raimund@1301: logger.debug("npr: " + audit.getRowData()); raimund@1301: node.put("identifier", raimund@1301: audit.getRowData() raimund@1301: .get("nebenproben_nr").toString().replaceAll("\"", "")); raimund@1301: } raimund@1301: if (audit.getMessungsId() != null) { raimund@1301: Messung m = repository.getByIdPlain( raimund@1301: Messung.class, audit.getMessungsId(), "land"); raimund@1301: ObjectNode identifier = node.putObject("identifier"); raimund@1301: identifier.put("messung", m.getNebenprobenNr()); raimund@1301: if ("kommentar_m".equals(audit.getTableName())) { raimund@1301: identifier.put("identifier", raimund@1301: audit.getRowData().get("datum").toString()); raimund@1301: } raimund@1301: if ("messwert".equals(audit.getTableName())) { raimund@1301: String value = translateId( raimund@1301: "messgroesse", raimund@1301: "messgroesse", raimund@1301: audit.getRowData().get("messgroesse_id").toString(), raimund@1301: "id", raimund@1301: "stamm"); raimund@1301: identifier.put("identifier", value); raimund@1301: } raimund@1301: } raimund@1301: return node; raimund@1301: } raimund@1301: raimund@1301: /** raimund@1301: * Service to generate audit trail for messung objects. raimund@1301: */ raimund@1301: @GET raimund@1301: @Path("/messung/{id}") raimund@1301: @Produces(MediaType.APPLICATION_JSON) raimund@1301: public String getMessung( raimund@1301: @Context HttpServletRequest request, raimund@1301: @PathParam("id") String id raimund@1301: ) { raimund@1301: if (id == null || "".equals(id)) { raimund@1301: String ret = "{\"success\": false," + raimund@1301: "\"message\":698,\"data\":null}"; raimund@1301: return ret; raimund@1301: } raimund@1301: Integer mId = null; raimund@1301: try { raimund@1301: mId = Integer.valueOf(id); raimund@1301: } raimund@1301: catch(NumberFormatException nfe) { raimund@1301: String ret = "{\"success\": false," + raimund@1301: "\"message\":600,\"data\":null}"; raimund@1301: return ret; raimund@1301: } raimund@1301: QueryBuilder builder = raimund@1301: new QueryBuilder( raimund@1301: repository.entityManager("land"), raimund@1301: AuditTrailMessung.class); raimund@1301: builder.and("objectId", mId); raimund@1301: builder.and("tableName", "messung"); raimund@1301: builder.or("messungsId", mId); raimund@1301: builder.orderBy("tstamp", true); raimund@1301: List audit = raimund@1301: repository.filterPlain(builder.getQuery(), "land"); raimund@1301: raimund@1301: Messung messung = repository.getByIdPlain(Messung.class, mId, "land"); raimund@1301: // Create an empty JsonObject raimund@1301: ObjectMapper mapper = new ObjectMapper(); raimund@1301: ObjectNode responseNode = mapper.createObjectNode(); raimund@1301: responseNode.put("success", true); raimund@1301: responseNode.put("message", 200); raimund@1301: ObjectNode auditJson = responseNode.putObject("data"); raimund@1301: ArrayNode entries = auditJson.putArray("audit"); raimund@1301: auditJson.put("id", messung.getId()); raimund@1301: auditJson.put("identifier", messung.getNebenprobenNr()); raimund@1301: for (AuditTrailMessung a : audit) { raimund@1301: entries.add(createEntry(a, mapper)); raimund@1301: } raimund@1301: return responseNode.toString(); raimund@1301: } raimund@1301: raimund@1301: /** raimund@1301: * Create a JSON object for an AuditTrailMessung entry. raimund@1301: * raimund@1301: * @param audit The table entry raimund@1301: * @param mapper JSON object mapper raimund@1301: */ raimund@1301: private ObjectNode createEntry(AuditTrailMessung audit, ObjectMapper mapper) { raimund@1301: ObjectNode node = mapper.createObjectNode(); raimund@1301: node.put("timestamp", audit.getTstamp().getTime()); raimund@1301: node.put("type", audit.getTableName()); raimund@1308: node.put("action", audit.getAction()); raimund@1301: ObjectNode data = (ObjectNode)audit.getChangedFields(); raimund@1301: node.putPOJO("changedFields", data); raimund@1301: if ("kommentar_m".equals(audit.getTableName())) { raimund@1301: node.put("identifier", audit.getRowData().get("datum").toString()); raimund@1301: } raimund@1301: if ("messwert".equals(audit.getTableName())) { raimund@1301: String value = translateId( raimund@1301: "messgroesse", raimund@1301: "messgroesse", raimund@1301: audit.getRowData().get("messgroesse_id").toString(), raimund@1301: "id", raimund@1301: "stamm"); raimund@1301: node.put("identifier", value); raimund@1301: } raimund@1301: return node; raimund@1301: } raimund@1301: raimund@1301: /** raimund@1301: * Translate a foreign key into the associated value. raimund@1301: */ raimund@1301: private String translateId( raimund@1301: String table, raimund@1301: String field, raimund@1301: String id, raimund@1301: String idField, raimund@1301: String source raimund@1301: ) { raimund@1301: EntityManager manager = repository.entityManager(source); raimund@1301: String sql = "SELECT " + field + " FROM " + table + raimund@1301: " WHERE " + idField + " = " + id + ";"; raimund@1301: javax.persistence.Query query = manager.createNativeQuery(sql); raimund@1301: List result = query.getResultList(); raimund@1301: return result.get(0); raimund@1301: } raimund@1301: raimund@1310: private Long formatDate(String format, String date) { raimund@1309: DateFormat inFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssXXX"); raimund@1309: try { raimund@1310: return inFormat.parse(date).getTime(); raimund@1309: } catch (ParseException e) { raimund@1310: return 0L; raimund@1309: } raimund@1309: } raimund@1309: raimund@1301: /** raimund@1301: * Translate all known foreign keys raimund@1301: */ raimund@1309: private ObjectNode translateValues(ObjectNode node) { raimund@1301: for (Iterator i = node.fieldNames(); i.hasNext();) { raimund@1301: String key = i.next(); raimund@1301: if (mappings.containsKey(key)) { raimund@1301: TableMapper m = mappings.get(key); raimund@1309: if (m.mappingTable.equals("date")) { raimund@1310: Long value = formatDate(m.valueField, node.get(key).asText()); raimund@1310: node.put(key, value); raimund@1309: } raimund@1309: else { raimund@1310: String value = translateId( raimund@1310: m.mappingTable, raimund@1310: m.valueField, raimund@1310: node.get(key).asText(), raimund@1310: "id", raimund@1310: "stamm"); raimund@1310: node.put(key, value); raimund@1309: } raimund@1301: } raimund@1301: } raimund@1301: return node; raimund@1301: } raimund@1301: }