Write eventlog.write requests to the database, and validate incoming PCBIDs for database existence

This commit is contained in:
skogaby 2019-01-12 01:57:50 -06:00
parent cd12a94d50
commit b94e4d44ce
10 changed files with 143 additions and 65 deletions

View File

@ -53,7 +53,7 @@ public abstract class AbstractHibernateDao<T extends Serializable> {
this.currentSession.close();
}
public void closeCurrentSessionwithTransaction() {
public void closeCurrentSessionWithTransaction() {
this.currentTransaction.commit();
this.currentSession.close();
}
@ -99,7 +99,7 @@ public abstract class AbstractHibernateDao<T extends Serializable> {
operation.call(entities[i]);
}
this.closeCurrentSessionwithTransaction();
this.closeCurrentSessionWithTransaction();
} catch (Exception e) {
this.currentTransaction.rollback();
}

View File

@ -1,24 +0,0 @@
package com.buttongames.butterfly.hibernate.dao.impl;
import com.buttongames.butterfly.hibernate.dao.AbstractHibernateDao;
import com.buttongames.butterfly.model.ButterflyPcb;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
/**
* DAO for interacting with <code>ButterflyPcb</code> objects in the database.
* @author skogaby (skogabyskogaby@gmail.com)
*/
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Repository
public class ButterflyPcbDao extends AbstractHibernateDao<ButterflyPcb> {
@Autowired
public ButterflyPcbDao(final SessionFactory sessionFactory) {
super(sessionFactory);
setClazz(ButterflyPcb.class);
}
}

View File

@ -0,0 +1,41 @@
package com.buttongames.butterfly.hibernate.dao.impl;
import com.buttongames.butterfly.hibernate.dao.AbstractHibernateDao;
import com.buttongames.butterfly.model.Machine;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
/**
* DAO for interacting with <code>Machine</code> objects in the database.
* @author skogaby (skogabyskogaby@gmail.com)
*/
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Repository
public class MachineDao extends AbstractHibernateDao<Machine> {
@Autowired
public MachineDao(final SessionFactory sessionFactory) {
super(sessionFactory);
setClazz(Machine.class);
}
/**
* Finds a machine by its PCBID.
* @param pcbId The PCBID to query for.
* @return The matching Machine, or null if none are found.
*/
public Machine findByPcbId(final String pcbId) {
this.openCurrentSession();
final Query<Machine> query = this.currentSession.createQuery("from Machine where pcbid = :pcbid");
query.setParameter("pcbid", pcbId);
final Machine result = query.uniqueResult();
this.closeCurrentSession();
return result;
}
}

View File

@ -2,6 +2,8 @@ package com.buttongames.butterfly.http;
import com.buttongames.butterfly.compression.Lz77;
import com.buttongames.butterfly.encryption.Rc4;
import com.buttongames.butterfly.hibernate.dao.impl.MachineDao;
import com.buttongames.butterfly.http.exception.InvalidPcbIdException;
import com.buttongames.butterfly.http.exception.InvalidRequestException;
import com.buttongames.butterfly.http.exception.InvalidRequestMethodException;
import com.buttongames.butterfly.http.exception.InvalidRequestModelException;
@ -91,6 +93,9 @@ public class ButterflyHttpServer {
/** Handler for requests for the <code>tax</code> module. */
private final TaxRequestHandler taxRequestHandler;
/** DAO for interacting with <code>Machine</code> objects in the database. */
private final MachineDao machineDao;
/**
* Constructor.
*/
@ -102,7 +107,8 @@ public class ButterflyHttpServer {
final FacilityRequestHandler facilityRequestHandler,
final PackageRequestHandler packageRequestHandler,
final EventLogRequestHandler eventLogRequestHandler,
final TaxRequestHandler taxRequestHandler) {
final TaxRequestHandler taxRequestHandler,
final MachineDao machineDao) {
this.servicesRequestHandler = servicesRequestHandler;
this.pcbEventRequestHandler = pcbEventRequestHandler;
this.pcbTrackerRequestHandler = pcbTrackerRequestHandler;
@ -111,6 +117,7 @@ public class ButterflyHttpServer {
this.packageRequestHandler = packageRequestHandler;
this.eventLogRequestHandler = eventLogRequestHandler;
this.taxRequestHandler = taxRequestHandler;
this.machineDao = machineDao;
}
/**
@ -182,8 +189,12 @@ public class ButterflyHttpServer {
}));
exception(MismatchedRequestUriException.class, (((exception, request, response) -> {
response.status(400);
response.body("Request URI does not match request body");
response.body("Request URI does not match request body.");
})));
exception(InvalidPcbIdException.class, (((exception, request, response) -> {
response.status(403);
response.body("PCBID is not valid or nonexistent.");
})));
}
/**
@ -214,7 +225,7 @@ public class ButterflyHttpServer {
throw new InvalidRequestModuleException();
}
// 3) validate that the request URI matches the request body;
// 3) validate that the PCBID exists in the database
final String encryptionKey = request.headers(CRYPT_KEY_HEADER);
final String compressionScheme = request.headers(COMPRESSION_HEADER);
byte[] reqBody = request.bodyAsBytes();
@ -235,8 +246,7 @@ public class ButterflyHttpServer {
reqBody = BinaryXmlUtils.binaryToXml(reqBody);
}
// read the request body into an XML document and check its properties
// to verify it matches the request URI
// read the request body into an XML document
Element rootNode = XmlUtils.byteArrayToXmlFile(reqBody);
if (rootNode == null ||
@ -250,6 +260,12 @@ public class ButterflyHttpServer {
final String requestBodyModule = moduleNode.getNodeName();
final String requestBodyMethod = moduleNode.getAttribute("method");
// check if the PCB exists in the database
if (this.machineDao.findByPcbId(requestBodyPcbId) == null) {
throw new InvalidPcbIdException();
}
// 4) validate that the request URI matches the request body
if (StringUtils.isBlank(requestBodyModel) ||
StringUtils.isBlank(requestBodyModule) ||
StringUtils.isBlank(requestBodyMethod) ||
@ -266,9 +282,7 @@ public class ButterflyHttpServer {
request.attribute("module", requestBodyModule);
request.attribute("method", requestBodyMethod);
// TODO: Verify the PCBID
// 4) return the node corresponding to the actual call
// 5) return the node corresponding to the actual call
return moduleNode;
}
}

View File

@ -0,0 +1,8 @@
package com.buttongames.butterfly.http.exception;
/**
* Exception thrown when a request is made with a PCBID that's not in the database.
* @author skogaby (skogabyskogaby@gmail.com)
*/
public class InvalidPcbIdException extends RuntimeException {
}

View File

@ -1,8 +1,12 @@
package com.buttongames.butterfly.http.handlers.impl;
import com.buttongames.butterfly.hibernate.dao.impl.Ddr16GameplayEventLogDao;
import com.buttongames.butterfly.http.exception.InvalidRequestMethodException;
import com.buttongames.butterfly.http.handlers.BaseRequestHandler;
import com.buttongames.butterfly.model.Ddr16GameplayEventLog;
import com.buttongames.butterfly.util.TimeUtils;
import com.buttongames.butterfly.xml.KXmlBuilder;
import com.buttongames.butterfly.xml.XmlUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
@ -10,6 +14,9 @@ import org.w3c.dom.Element;
import spark.Request;
import spark.Response;
import java.time.LocalDateTime;
import java.util.Base64;
/**
* Handler for any requests that come to the <code>eventlog</code> module.
* @author skogaby (skogabyskogaby@gmail.com)
@ -19,6 +26,16 @@ public class EventLogRequestHandler extends BaseRequestHandler {
private final Logger LOG = LogManager.getLogger(EventLogRequestHandler.class);
/**
* The DAO for creating gameplay event logs in the database.
*/
private final Ddr16GameplayEventLogDao gameplayEventLogDao;
public EventLogRequestHandler(final Ddr16GameplayEventLogDao gameplayEventLogDao) {
this.gameplayEventLogDao = gameplayEventLogDao;
}
/**
* Handles an incoming request for the <code>eventlog</code> module.
* @param requestBody The XML document of the incoming request.
@ -31,7 +48,7 @@ public class EventLogRequestHandler extends BaseRequestHandler {
final String requestMethod = request.attribute("method");
if (requestMethod.equals("write")) {
return handleWriteRequest(request, response);
return handleWriteRequest(requestBody, request, response);
} else {
throw new InvalidRequestMethodException();
}
@ -39,12 +56,31 @@ public class EventLogRequestHandler extends BaseRequestHandler {
/**
* Handles an incoming request for <code>eventlog.write</code>
* @param requestBody The XML document of the incoming request.
* @param request The Spark request
* @param response The Spark response
* @return A response object for Spark
*/
private Object handleWriteRequest(final Request request, final Response response) {
// TODO: actually store the events coming in from the client
private Object handleWriteRequest(final Element requestBody, final Request request, final Response response) {
// save the event to the database
final String reqModel = request.attribute("model");
final String reqPcbId = request.attribute("pcbid");
final int retryCount = XmlUtils.intValueAtPath(requestBody, "/eventlog/retrycnt");
final String eventId = XmlUtils.strValueAtPath(requestBody, "/eventlog/data/eventid");
final int eventOrder = XmlUtils.intValueAtPath(requestBody, "/eventlog/data/eventorder");
final LocalDateTime pcbTime = TimeUtils.timeFromEpoch(XmlUtils.longValueAtPath(requestBody, "/eventlog/data/pcbtime"));
final long gameSession = XmlUtils.longValueAtPath(requestBody, "/eventlog/data/gamesession");
final String stringData1 = new String(Base64.getDecoder().decode(XmlUtils.strValueAtPath(requestBody, "/eventlog/data/strdata1")));
final String stringData2 = new String(Base64.getDecoder().decode(XmlUtils.strValueAtPath(requestBody, "/eventlog/data/strdata2")));
final long numData1 = XmlUtils.longValueAtPath(requestBody, "/eventlog/data/numdata1");
final long numData2 = XmlUtils.longValueAtPath(requestBody, "/eventlog/data/numdata2");
final String locationId = XmlUtils.strValueAtPath(requestBody, "/eventlog/data/locationid");
final Ddr16GameplayEventLog event = new Ddr16GameplayEventLog(reqPcbId, reqModel, retryCount, eventId,
eventOrder, pcbTime, gameSession, stringData1, stringData2, numData1, numData2, locationId);
this.gameplayEventLogDao.create(event);
// send the response
KXmlBuilder respBuilder = KXmlBuilder.create("response")
.e("eventlog")
.s64("gamesession", 0).up()

View File

@ -11,6 +11,7 @@ import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.time.LocalDateTime;
/**
* Model class that represents a gameplay event from a DDR 16 machine.
@ -50,7 +51,7 @@ public class Ddr16GameplayEventLog implements Externalizable {
/** The PCB time of the event. */
@Column(name = "pcb_time")
private long pcbTime;
private LocalDateTime pcbTime;
/** The game session of the event. */
@Column(name = "game_session")
@ -73,16 +74,15 @@ public class Ddr16GameplayEventLog implements Externalizable {
private long numData2;
/** The location ID for the facility of the machine. */
@ManyToOne
@JoinColumn(name = "facility_id")
private Ddr16Facility facility;
@Column(name = "location_id")
private String locationId;
public Ddr16GameplayEventLog() { }
public Ddr16GameplayEventLog(final String pcbId, final String model, final int retryCount,
final String eventId, final int eventOrder, final long pcbTime, final long gameSession,
final String eventId, final int eventOrder, final LocalDateTime pcbTime, final long gameSession,
final String stringData1, final String stringData2, final long numData1, final long numData2,
final Ddr16Facility facility) {
final String locationId) {
this.pcbId = pcbId;
this.model = model;
this.retryCount = retryCount;
@ -94,7 +94,7 @@ public class Ddr16GameplayEventLog implements Externalizable {
this.stringData2 = stringData2;
this.numData1 = numData1;
this.numData2 = numData2;
this.facility = facility;
this.locationId = locationId;
}
@Override
@ -105,13 +105,13 @@ public class Ddr16GameplayEventLog implements Externalizable {
out.writeInt(this.retryCount);
out.writeUTF(this.eventId);
out.writeInt(this.eventOrder);
out.writeLong(this.pcbTime);
out.writeObject(this.pcbTime);
out.writeLong(this.gameSession);
out.writeUTF(this.stringData1);
out.writeUTF(this.stringData2);
out.writeLong(this.numData1);
out.writeLong(this.numData2);
out.writeObject(this.facility);
out.writeUTF(this.locationId);
}
@Override
@ -122,13 +122,13 @@ public class Ddr16GameplayEventLog implements Externalizable {
this.setRetryCount(in.readInt());
this.setEventId(in.readUTF());
this.setEventOrder(in.readInt());
this.setPcbTime(in.readLong());
this.setPcbTime((LocalDateTime) in.readObject());
this.setGameSession(in.readLong());
this.setStringData1(in.readUTF());
this.setStringData2(in.readUTF());
this.setNumData1(in.readLong());
this.setNumData2(in.readLong());
this.setFacility((Ddr16Facility) in.readObject());
this.setLocationId(in.readUTF());
}
public long getId() {
@ -179,11 +179,11 @@ public class Ddr16GameplayEventLog implements Externalizable {
this.eventOrder = eventOrder;
}
public long getPcbTime() {
public LocalDateTime getPcbTime() {
return pcbTime;
}
public void setPcbTime(long pcbTime) {
public void setPcbTime(LocalDateTime pcbTime) {
this.pcbTime = pcbTime;
}
@ -227,11 +227,11 @@ public class Ddr16GameplayEventLog implements Externalizable {
this.numData2 = numData2;
}
public Ddr16Facility getFacility() {
return facility;
public String getLocationId() {
return locationId;
}
public void setFacility(Ddr16Facility facility) {
this.facility = facility;
public void setLocationId(String locationId) {
this.locationId = locationId;
}
}

View File

@ -19,7 +19,7 @@ import java.time.LocalDateTime;
*/
@Entity
@Table(name = "machines")
public class ButterflyPcb implements Externalizable {
public class Machine implements Externalizable {
private static final long serialVersionUID = 1L;
@ -62,10 +62,10 @@ public class ButterflyPcb implements Externalizable {
@Column(name = "port")
private int port;
public ButterflyPcb() { }
public Machine() { }
public ButterflyPcb(final ButterflyUser user, final String pcbId, final LocalDateTime registerTime,
final boolean enabled, final int port) {
public Machine(final ButterflyUser user, final String pcbId, final LocalDateTime registerTime,
final boolean enabled, final int port) {
this.user = user;
this.pcbId = pcbId;
this.registerTime = registerTime;

View File

@ -1,6 +1,6 @@
package com.buttongames.butterfly.spring.configuration;
import com.buttongames.butterfly.hibernate.dao.impl.ButterflyPcbDao;
import com.buttongames.butterfly.hibernate.dao.impl.MachineDao;
import com.buttongames.butterfly.hibernate.dao.impl.ButterflyUserDao;
import com.buttongames.butterfly.hibernate.dao.impl.Ddr16FacilityDao;
import com.buttongames.butterfly.hibernate.dao.impl.Ddr16GameplayEventLogDao;
@ -87,8 +87,8 @@ public class HibernateConfiguration {
}
@Bean
public ButterflyPcbDao butterflyPcbDao(final SessionFactory sessionFactory) {
return new ButterflyPcbDao(sessionFactory);
public MachineDao butterflyPcbDao(final SessionFactory sessionFactory) {
return new MachineDao(sessionFactory);
}
@Bean

View File

@ -1,6 +1,8 @@
package com.buttongames.butterfly.spring.configuration;
import com.buttongames.butterfly.hibernate.dao.impl.Ddr16GameplayEventLogDao;
import com.buttongames.butterfly.hibernate.dao.impl.Ddr16PcbEventLogDao;
import com.buttongames.butterfly.hibernate.dao.impl.MachineDao;
import com.buttongames.butterfly.http.ButterflyHttpServer;
import com.buttongames.butterfly.http.handlers.impl.EventLogRequestHandler;
import com.buttongames.butterfly.http.handlers.impl.FacilityRequestHandler;
@ -30,15 +32,16 @@ public class HttpConfiguration {
final FacilityRequestHandler facilityRequestHandler,
final PackageRequestHandler packageRequestHandler,
final EventLogRequestHandler eventLogRequestHandler,
final TaxRequestHandler taxRequestHandler) {
final TaxRequestHandler taxRequestHandler,
final MachineDao machineDao) {
return new ButterflyHttpServer(servicesRequestHandler, pcbEventRequestHandler, pcbTrackerRequestHandler,
messageRequestHandler, facilityRequestHandler, packageRequestHandler, eventLogRequestHandler,
taxRequestHandler);
taxRequestHandler, machineDao);
}
@Bean
public EventLogRequestHandler eventLogRequestHandler() {
return new EventLogRequestHandler();
public EventLogRequestHandler eventLogRequestHandler(final Ddr16GameplayEventLogDao gameplayEventLogDao) {
return new EventLogRequestHandler(gameplayEventLogDao);
}
@Bean