package org.oliot.epcis.service.query;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.oliot.epcis.configuration.Configuration;
import org.oliot.epcis.security.OAuthUtil;
import org.oliot.epcis.service.query.sql.MysqlQueryService;
import org.oliot.model.epcis.DuplicateSubscriptionException;
import org.oliot.model.epcis.ImplementationException;
import org.oliot.model.epcis.InvalidURIException;
import org.oliot.model.epcis.NoSuchNameException;
import org.oliot.model.epcis.NoSuchSubscriptionException;
import org.oliot.model.epcis.PollParameters;
import org.oliot.model.epcis.QueryParameterException;
import org.oliot.model.epcis.QueryTooComplexException;
import org.oliot.model.epcis.QueryTooLargeException;
import org.oliot.model.epcis.SecurityException;
import org.oliot.model.epcis.SubscribeNotPermittedException;
import org.oliot.model.epcis.SubscriptionControlsException;
import org.oliot.model.epcis.ValidationException;
import org.oliot.model.oliot.Subscription;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ServletContextAware;
import com.restfb.Connection;
import com.restfb.FacebookClient;
import com.restfb.types.User;
/**
* Copyright (C) 2014-2016 Jaewook Byun
*
* This project is part of Oliot open source (http://oliot.org). Oliot EPCIS
* v1.2.x is Java Web Service complying with Electronic Product Code Information
* Service (EPCIS) v1.2.
*
* @author Jaewook Jack Byun, Ph.D student
*
* Korea Advanced Institute of Science and Technology (KAIST)
*
* Real-time Embedded System Laboratory(RESL)
*
* bjw0829@kaist.ac.kr, bjw0829@gmail.com
*
*
* @author Yalew kidane, Ph.D student
*
* Korea Advanced Institute of Science and Technology (KAIST)
*
* Real-time Embedded System Laboratory(RESL)
*
* yalewkidane@gmail.com/@kaist.ac.kr
*/
@Controller
public class RESTLikeQueryService implements ServletContextAware {
@Autowired
ServletContext servletContext;
@SuppressWarnings("unused")
@Autowired
private HttpServletRequest request;
@SuppressWarnings("unused")
@Autowired
private HttpServletResponse response;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* Registers a subscriber for a previously defined query having the
* specified name. The params argument provides the values to be used for
* any named parameters defined by the query. The dest parameter specifies a
* destination where results from the query are to be delivered, via the
* Query Callback Interface. The dest parameter is a URI that both
* identifies a specific binding of the Query Callback Interface to use and
* specifies addressing information. The controls parameter controls how the
* subscription is to be processed; in particular, it specifies the
* conditions under which the query is to be invoked (e.g., specifying a
* periodic schedule). The subscriptionID is an arbitrary string that is
* copied into every response delivered to the specified destination, and
* otherwise not interpreted by the EPCIS service. The client may use the
* subscriptionID to identify from which subscription a given result was
* generated, especially when several subscriptions are made to the same
* destination. The dest argument MAY be null or empty, in which case
* results are delivered to a pre-arranged destination based on the
* authenticated identity of the caller. If the EPCIS implementation does
* not have a destination pre-arranged for the caller, or does not permit
* this usage, it SHALL raise an InvalidURIException.
*
* @throws QueryParameterException
*/
@RequestMapping(value = "/Subscribe/{queryName}/{subscriptionID}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> subscribe(@PathVariable String queryName, @PathVariable String subscriptionID,
@RequestParam String dest, @RequestParam(required = false) String schedule,
@RequestParam(required = false) String trigger, @RequestParam(required = false) String initialRecordTime,
@RequestParam Boolean reportIfEmpty, @RequestParam(required = false) String eventType,
@RequestParam(required = false) String GE_eventTime, @RequestParam(required = false) String LT_eventTime,
@RequestParam(required = false) String GE_recordTime, @RequestParam(required = false) String LT_recordTime,
@RequestParam(required = false) String EQ_action, @RequestParam(required = false) String EQ_bizStep,
@RequestParam(required = false) String EQ_disposition, @RequestParam(required = false) String EQ_readPoint,
@RequestParam(required = false) String WD_readPoint, @RequestParam(required = false) String EQ_bizLocation,
@RequestParam(required = false) String WD_bizLocation,
@RequestParam(required = false) String EQ_transformationID,
@RequestParam(required = false) String MATCH_epc, @RequestParam(required = false) String MATCH_parentID,
@RequestParam(required = false) String MATCH_inputEPC,
@RequestParam(required = false) String MATCH_outputEPC, @RequestParam(required = false) String MATCH_anyEPC,
@RequestParam(required = false) String MATCH_epcClass,
@RequestParam(required = false) String MATCH_inputEPCClass,
@RequestParam(required = false) String MATCH_outputEPCClass,
@RequestParam(required = false) String MATCH_anyEPCClass,
@RequestParam(required = false) Integer EQ_quantity, @RequestParam(required = false) Integer GT_quantity,
@RequestParam(required = false) Integer GE_quantity, @RequestParam(required = false) Integer LT_quantity,
@RequestParam(required = false) Integer LE_quantity, @RequestParam(required = false) String EQ_eventID,
@RequestParam(required = false) Boolean EXISTS_errorDeclaration,
@RequestParam(required = false) String GE_errorDeclarationTime,
@RequestParam(required = false) String LT_errorDeclarationTime,
@RequestParam(required = false) String EQ_errorReason,
@RequestParam(required = false) String EQ_correctiveEventID, @RequestParam(required = false) String orderBy,
@RequestParam(required = false) String orderDirection,
@RequestParam(required = false) Integer eventCountLimit,
@RequestParam(required = false) Integer maxEventCount,
@RequestParam(required = false) String vocabularyName,
@RequestParam(required = false) Boolean includeAttributes,
@RequestParam(required = false) Boolean includeChildren,
@RequestParam(required = false) String attributeNames, @RequestParam(required = false) String EQ_name,
@RequestParam(required = false) String WD_name, @RequestParam(required = false) String HASATTR,
@RequestParam(required = false) Integer maxElementCount, @RequestParam(required = false) String format,
@RequestParam(required = false) String userID, @RequestParam(required = false) String accessToken,
@RequestParam Map<String, String> params)
throws NoSuchNameException, InvalidURIException, DuplicateSubscriptionException, QueryParameterException,
QueryTooComplexException, SubscriptionControlsException, SubscribeNotPermittedException, SecurityException,
ValidationException, ImplementationException {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "text/html; charset=utf-8");
if (initialRecordTime != null) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
sdf.parse(initialRecordTime);
} catch (ParseException e) {
String error = e.toString();
return new ResponseEntity<>(error, responseHeaders, HttpStatus.BAD_REQUEST);
}
}
// Access Control is not mandatory
// However, if fid and accessToken provided, more information provided
FacebookClient fc = null;
List<String> friendList = null;
if (userID != null) {
// Check accessToken
fc = OAuthUtil.isValidatedFacebookClient(accessToken, userID);
if (fc == null) {
return new ResponseEntity<>(new String("Unauthorized Token"), responseHeaders, HttpStatus.UNAUTHORIZED);
}
friendList = new ArrayList<String>();
Connection<User> friendConnection = fc.fetchConnection("me/friends", User.class);
for (List<User> friends : friendConnection) {
for (User friend : friends) {
friendList.add(friend.getId());
}
}
}
//----------------------------
org.oliot.model.oliot.PollParameters pp=new org.oliot.model.oliot.PollParameters(queryName, eventType, GE_eventTime, LT_eventTime, GE_recordTime,
LT_recordTime, EQ_action, EQ_bizStep, EQ_disposition, EQ_readPoint, WD_readPoint, EQ_bizLocation,
WD_bizLocation, EQ_transformationID, MATCH_epc, MATCH_parentID, MATCH_inputEPC, MATCH_outputEPC,
MATCH_anyEPC, MATCH_epcClass, MATCH_inputEPCClass, MATCH_outputEPCClass, MATCH_anyEPCClass, EQ_quantity,
GT_quantity, GE_quantity, LT_quantity, LE_quantity, EQ_eventID, EXISTS_errorDeclaration,
GE_errorDeclarationTime, LT_errorDeclarationTime, EQ_errorReason, EQ_correctiveEventID, orderBy,
orderDirection, eventCountLimit, maxEventCount, vocabularyName, includeAttributes, includeChildren,
attributeNames, EQ_name, WD_name, HASATTR, maxElementCount, format, params);
Subscription sub=new Subscription(subscriptionID, dest, schedule, trigger, initialRecordTime,
reportIfEmpty, pp);
MysqlQueryService mysqlQueryService = new MysqlQueryService();
String result = mysqlQueryService.subscribe(sub, userID, friendList);
return new ResponseEntity<>(result, responseHeaders, HttpStatus.OK);
//----------------------------
// reportIfEmpty
// PollParameters p = new PollParameters(queryName, eventType, GE_eventTime, LT_eventTime, GE_recordTime,
// LT_recordTime, EQ_action, EQ_bizStep, EQ_disposition, EQ_readPoint, WD_readPoint, EQ_bizLocation,
// WD_bizLocation, EQ_transformationID, MATCH_epc, MATCH_parentID, MATCH_inputEPC, MATCH_outputEPC,
// MATCH_anyEPC, MATCH_epcClass, MATCH_inputEPCClass, MATCH_outputEPCClass, MATCH_anyEPCClass, EQ_quantity,
// GT_quantity, GE_quantity, LT_quantity, LE_quantity, EQ_eventID, EXISTS_errorDeclaration,
// GE_errorDeclarationTime, LT_errorDeclarationTime, EQ_errorReason, EQ_correctiveEventID, orderBy,
// orderDirection, eventCountLimit, maxEventCount, vocabularyName, includeAttributes, includeChildren,
// attributeNames, EQ_name, WD_name, HASATTR, maxElementCount, format, params);
// SubscriptionType s = new SubscriptionType(subscriptionID, dest, schedule, trigger, initialRecordTime,
// reportIfEmpty, p);
//
// MongoQueryService mongoQueryService = new MongoQueryService();
// String result = mongoQueryService.subscribe(s, userID, friendList);
// return new ResponseEntity<>(result, responseHeaders, HttpStatus.OK);
}
@RequestMapping(value = "/Unsubscribe/{subscriptionID}", method = RequestMethod.GET)
public ResponseEntity<?> unsubscribe(@PathVariable String subscriptionID)
throws NoSuchSubscriptionException, ValidationException, ImplementationException {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "text/html; charset=utf-8");
MysqlQueryService mysqlQueryService = new MysqlQueryService();
mysqlQueryService.unsubscribe(subscriptionID);
//MongoQueryService mongoQueryService = new MongoQueryService();
//mongoQueryService.unsubscribe(subscriptionID);
return new ResponseEntity<>(new String("Subscription " + subscriptionID + " : Unsubscribed"), responseHeaders,
HttpStatus.OK);
}
@RequestMapping(value = "/GetSubscriptionIDs/{queryName}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> getSubscriptionIDsREST(@PathVariable String queryName)
throws NoSuchNameException, SecurityException, ValidationException, ImplementationException {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "application/json; charset=utf-8");
MysqlQueryService mysqlQueryService = new MysqlQueryService();
String result = mysqlQueryService.getSubscriptionIDsREST(queryName);
//MongoQueryService mongoQueryService = new MongoQueryService();
//String result = mongoQueryService.getSubscriptionIDsREST(queryName);
return new ResponseEntity<>(result, responseHeaders, HttpStatus.OK);
}
/**
* Returns a list of all query names available for use with the subscribe
* and poll methods. This includes all pre-defined queries provided by the
* implementation, including those specified in Section 8.2.7.
*
* No Dependency with Backend
*
* @return JSONArray of query names ( String )
*/
@RequestMapping(value = "/GetQueryNames", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> getQueryNamesREST()
throws SecurityException, ValidationException, ImplementationException {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "application/json; charset=utf-8");
JSONArray jsonArray = new JSONArray();
List<String> queryNames = getQueryNames();
for (int i = 0; i < queryNames.size(); i++) {
jsonArray.put(queryNames.get(i));
}
return new ResponseEntity<>(jsonArray.toString(1), responseHeaders, HttpStatus.OK);
}
/**
* Returns a list of all query names available for use with the subscribe
* and poll methods. This includes all pre-defined queries provided by the
* implementation, including those specified in Section 8.2.7.
*
* @return a list of all query names
*/
public List<String> getQueryNames() throws SecurityException, ValidationException, ImplementationException {
List<String> queryNames = new ArrayList<String>();
queryNames.add("SimpleEventQuery");
queryNames.add("SimpleMasterDataQuery");
return queryNames;
}
/**
* Returns a string that identifies what version of the specification this
* implementation complies with. The possible values for this string are
* defined by GS1. An implementation SHALL return a string corresponding to
* a version of this specification to which the implementation fully
* complies, and SHOULD return the string corresponding to the latest
* version to which it complies. To indicate compliance with this Version
* 1.2 of the EPCIS specification, the implementation SHALL return the
* string 1.2 .
*
* @return 1.2
*/
@RequestMapping(value = "/GetStandardVersion", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> getStandardVersion()
throws SecurityException, ValidationException, ImplementationException {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "text/html; charset=utf-8");
return new ResponseEntity<>(new String("1.2"), responseHeaders, HttpStatus.OK);
}
/**
* Returns a string that identifies what vendor extensions this
* implementation provides. The possible values of this string and their
* meanings are vendor-defined, except that the empty string SHALL indicate
* that the implementation implements only standard functionality with no
* vendor extensions. When an implementation chooses to return a non-empty
* string, the value returned SHALL be a URI where the vendor is the owning
* authority. For example, this may be an HTTP URL whose authority portion
* is a domain name owned by the vendor, a URN having a URN namespace
* identifier issued to the vendor by IANA, an OID URN whose initial path is
* a Private Enterprise Number assigned to the vendor, etc.
*
* @return a string of vendor version
*/
@RequestMapping(value = "/GetVendorVersion", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> getVendorVersion() throws SecurityException, ValidationException, ImplementationException {
// It is not a version of Vendor
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "text/html; charset=utf-8");
return new ResponseEntity<>(new String("org.oliot.epcis-1.2.4"), responseHeaders, HttpStatus.OK);
}
@RequestMapping(value = "/Poll/{queryName}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> poll(@PathVariable String queryName, @RequestParam(required = false) String eventType,
@RequestParam(required = false) String GE_eventTime, @RequestParam(required = false) String LT_eventTime,
@RequestParam(required = false) String GE_recordTime, @RequestParam(required = false) String LT_recordTime,
@RequestParam(required = false) String EQ_action, @RequestParam(required = false) String EQ_bizStep,
@RequestParam(required = false) String EQ_disposition, @RequestParam(required = false) String EQ_readPoint,
@RequestParam(required = false) String WD_readPoint, @RequestParam(required = false) String EQ_bizLocation,
@RequestParam(required = false) String WD_bizLocation,
@RequestParam(required = false) String EQ_transformationID,
@RequestParam(required = false) String MATCH_epc, @RequestParam(required = false) String MATCH_parentID,
@RequestParam(required = false) String MATCH_inputEPC,
@RequestParam(required = false) String MATCH_outputEPC, @RequestParam(required = false) String MATCH_anyEPC,
@RequestParam(required = false) String MATCH_epcClass,
@RequestParam(required = false) String MATCH_inputEPCClass,
@RequestParam(required = false) String MATCH_outputEPCClass,
@RequestParam(required = false) String MATCH_anyEPCClass,
@RequestParam(required = false) Integer EQ_quantity, @RequestParam(required = false) Integer GT_quantity,
@RequestParam(required = false) Integer GE_quantity, @RequestParam(required = false) Integer LT_quantity,
@RequestParam(required = false) Integer LE_quantity,
@RequestParam(required = false) String EQ_eventID,
@RequestParam(required = false) Boolean EXISTS_errorDeclaration,
@RequestParam(required = false) String GE_errorDeclarationTime,
@RequestParam(required = false) String LT_errorDeclarationTime,
@RequestParam(required = false) String EQ_errorReason,
@RequestParam(required = false) String EQ_correctiveEventID,
@RequestParam(required = false) String orderBy, @RequestParam(required = false) String orderDirection,
@RequestParam(required = false) Integer eventCountLimit,
@RequestParam(required = false) Integer maxEventCount,
@RequestParam(required = false) String vocabularyName,
@RequestParam(required = false) Boolean includeAttributes,
@RequestParam(required = false) Boolean includeChildren,
@RequestParam(required = false) String attributeNames, @RequestParam(required = false) String EQ_name,
@RequestParam(required = false) String WD_name, @RequestParam(required = false) String HASATTR,
@RequestParam(required = false) Integer maxElementCount,
@RequestParam(required = false) String format, @RequestParam(required = false) String userID,
@RequestParam(required = false) String accessToken,
@RequestParam(required = false) String accessMode,
@RequestParam Map<String, String> params)
throws QueryParameterException, QueryTooLargeException, QueryTooComplexException, NoSuchNameException,
SecurityException, ValidationException, ImplementationException {
HttpHeaders responseHeaders = new HttpHeaders();
if (format != null && format.equals("JSON")) {
responseHeaders.add("Content-Type", "application/json; charset=utf-8");
} else {
responseHeaders.add("Content-Type", "application/xml; charset=utf-8");
}
// Access Control is not mandatory
// However, if fid and accessToken provided, more information provided
FacebookClient fc = null;
List<String> friendList = null;
if(accessMode==null){
}
else if(accessMode.equals("facebook")){
if (userID != null) {
// Check accessToken
fc = OAuthUtil.isValidatedFacebookClient(accessToken, userID);
if (fc == null) {
return new ResponseEntity<>(new String("Unauthorized Token"), responseHeaders, HttpStatus.UNAUTHORIZED);
}
friendList = new ArrayList<String>();
Connection<User> friendConnection = fc.fetchConnection("me/friends", User.class);
for (List<User> friends : friendConnection) {
for (User friend : friends) {
friendList.add(friend.getId());
}
}
}
}
else if(accessMode.equals("custom")){
//url of ac_api server
String quri = "http://"+Configuration.ac_api_address+"/user/"+userID+"/epcis/"+Configuration.epcis_ID+"/subscribe";
//query to ac_api server
String qurlParameters = "";
String query_result = Configuration.query_access_relation(quri, accessToken, qurlParameters);
//for debug, erase after implementing.
Configuration.logger.info(query_result);
query_result = query_result.replaceAll("[\"{} ]","").split(":")[1];
boolean pass = (query_result.equals("yes"))?true:false;
if(!pass){
return new ResponseEntity<>(new String("no subscribe auth"), HttpStatus.BAD_REQUEST);
}
/* end of example for querying ac_api*/
quri = "http://"+Configuration.ac_api_address+"/user/"+userID+"/access";
//query to ac_api server
qurlParameters = "";
query_result = Configuration.query_access_relation(quri, accessToken, qurlParameters);
//for debug, erase after implementing.
Configuration.logger.info(query_result);
query_result = query_result.replaceAll("[\"{}\\[\\] ]","").split(":")[1];
String[] accessusers = query_result.split(",");
//2. if pass the subscribing test, then get all event owner list
friendList = new ArrayList<String>();
for (String accessuser : accessusers){
Configuration.logger.info("Friends:"+accessuser);
friendList.add(accessuser);
}
}
else{
}
PollParameters pollParams = new PollParameters(queryName, eventType, GE_eventTime, LT_eventTime, GE_recordTime,
LT_recordTime, EQ_action, EQ_bizStep, EQ_disposition, EQ_readPoint, WD_readPoint, EQ_bizLocation,
WD_bizLocation, EQ_transformationID, MATCH_epc, MATCH_parentID, MATCH_inputEPC, MATCH_outputEPC,
MATCH_anyEPC, MATCH_epcClass, MATCH_inputEPCClass, MATCH_outputEPCClass, MATCH_anyEPCClass, EQ_quantity,
GT_quantity, GE_quantity, LT_quantity, LE_quantity, EQ_eventID, EXISTS_errorDeclaration,
GE_errorDeclarationTime, LT_errorDeclarationTime, EQ_errorReason, EQ_correctiveEventID, orderBy,
orderDirection, eventCountLimit, maxEventCount, vocabularyName, includeAttributes, includeChildren,
attributeNames, EQ_name, WD_name, HASATTR, maxElementCount, format, params);
MysqlQueryService mysqlQueryService = new MysqlQueryService();
String result = mysqlQueryService.poll(pollParams, userID, friendList, null);
return new ResponseEntity<>(result, responseHeaders, HttpStatus.OK);
}
}