Projet

Général

Profil

« Précédent | Suivant » 

Révision 96454bcd

Ajouté par Sylvain Sauvage il y a plus de 11 ans

Version 1.9-mock

This is version 1.9-mock. 1.9 because the API still have changes pending
(principally relative to the Discovery Services). “mock” because TLS
configuration is not yet available and the signatures (SigMa) are not fully
implemented.

  • All:
    - code cleaned and refactored
    - lots of bugs fixed
    - dependencies checked and trimmed
    - documentation added
    - Identity handling added
  • New library modules (Mu, Nu)
  • New signature modules (SigMa)
  • Access Layer and User interfaces (ALfA and OMeGa):
    - code refactored
    - new, better APIs
    - Identity handling added
    - use EPCglobal and DS events (no proxy types anymore)
  • New tempororay DSeTa web service (pending new DS)
  • ETa corrected and added to the IoTa-Installer
    - ETa-Callback modules are now available as web applications
    - filtering rules: if a part of an event is not allowed, now the whole
    event is deleted from the result (before only the rejectd part was)
  • CaPPa: overall refactoring of XACML handling
    - new temporary User web service
    - new Xi module: XACML Interrogation web service (was two modules: TCP and
    servlet)
  • PSi now signs its events
  • Installer, now also installs or configures:
    - ETa and its Callback modules
    - ActiveMQ
    - SigMa
    - certificate/signing key
  • Greyc letters figures:
    - new simplified figures (sans IoTa and simplified IoTa)
    - new figure for ETa modules
    - show 3rd party clients
    - data flows specified
    - TLS and link security added
    - IDs and trusted IDs added
    - color adjusted for printing
    - GREYC logo added

Voir les différences:

ETa/ETa-Callback/ETa-Callback-Filter/src/main/java/fr/unicaen/iota/eta/callback/filter/CallbackOperationsModule.java
/*
* This program is a part of the IoTa Project.
*
* Copyright © 2008-2012 Université de Caen Basse-Normandie, GREYC
* Copyright © 2011-2012 Université de Caen Basse-Normandie, GREYC
* Copyright © 2011 Orange Labs
*
* This program is free software: you can redistribute it and/or modify
......
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jms.*;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
......
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fosstrak.epcis.repository.InvalidFormatException;
import org.fosstrak.epcis.repository.model.*;
import org.fosstrak.epcis.utils.TimeParser;
import org.w3c.dom.*;
import org.fosstrak.epcis.model.AggregationEventType;
import org.fosstrak.epcis.model.EPCISEventType;
import org.fosstrak.epcis.model.ObjectEventType;
import org.fosstrak.epcis.model.QuantityEventType;
import org.fosstrak.epcis.model.TransactionEventType;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
......
*/
public class CallbackOperationsModule {
private final String PROPERTY_FILE = "/application.properties";
private Properties properties;
private Schema schema;
private static final String PROP_EPCIS_SCHEMA_FILE = "/wsdl/EPCglobal-epcis-query-1_0.xsd";
private final String PROP_MSG_CONS_QUEUENAME = "messagebroker.consQueueName";
private final String PROP_MSG_SEND_QUEUENAME = "messagebroker.sendQueueName";
private final String PROP_MSG_USER = "messagebroker.user";
private final String PROP_MSG_PASSWORD = "messagebroker.password";
private final String PROP_MSG_URL = "messagebroker.url";
private String consummerQueueName = "queueToFilter";
private String senderQueueName = "queueToSender";
private String msgUser;
private String msgPassword;
private String msgUrl;
private final String PROP_USERNAME = "database.username";
private final String DEFAULT_USERNAME = "eta_usr";
private final String PROP_PASSWORD = "database.password";
private final String DEFAULT_PASSWORD = "eta_pwd";
private final String PROP_URL = "database.url";
private final String DEFAULT_URL = "jdbc:mysql://localhost:3306/eta_db?autoReconnect=true";
private java.sql.Connection dbConnection;
/**
* The CallbackOperationsBackendSQL used for database
*/
private CallbackOperationsBackendSQL backend;
/**
* The CallbackCheck used to check access
*/
private CallbackCheck callbackCheck;
private static final Log LOG = LogFactory.getLog(CallbackOperationsModule.class);
public CallbackOperationsModule() {
this.properties = loadProperties(PROPERTY_FILE);
this.schema = initEpcisSchema(PROP_EPCIS_SCHEMA_FILE);
consummerQueueName = properties.getProperty(PROP_MSG_CONS_QUEUENAME, consummerQueueName);
senderQueueName = properties.getProperty(PROP_MSG_SEND_QUEUENAME, senderQueueName);
msgUrl = properties.getProperty(PROP_MSG_URL, ActiveMQConnection.DEFAULT_BROKER_URL);
msgUser = properties.getProperty(PROP_MSG_USER, ActiveMQConnection.DEFAULT_USER);
msgPassword = properties.getProperty(PROP_MSG_PASSWORD, ActiveMQConnection.DEFAULT_PASSWORD);
this.schema = initEpcisSchema(Constants.EPCIS_SCHEMA_PATH);
callbackCheck = new CallbackCheck();
backend = new CallbackOperationsBackendSQL();
dbConnection = loadDatabaseConnection();
}
/**
* Gets the consummer queue name from JMS message broker.
*
* @return The consummer queue name.
*/
public String getConsummerQueueName() {
return consummerQueueName;
}
/**
* Sets the consummer queue name from the JMS message broker.
*
* @param consummerQueueName The consummer queue name.
*/
public void setConsummerQueueName(String consummerQueueName) {
this.consummerQueueName = consummerQueueName;
}
/**
* Gets sender queue name from JMS message broker.
*
* @return The sender queue name.
*/
public String getSenderQueueName() {
return senderQueueName;
}
/**
* Sets the sender queue name from the JMS message broker.
*
* @param senderQueueName The sender queue name.
*/
public void setSenderQueueName(String senderQueueName) {
this.senderQueueName = senderQueueName;
}
/**
* Gets the ActiveMQ password.
*
* @return The ActiveMQ password.
*/
public String getMsgPassword() {
return msgPassword;
}
/**
* Gets the destination URL.
*
* @return The destination URL.
*/
public String getMsgUrl() {
return msgUrl;
}
/**
* Gets the user name.
*
* @return The user name.
*/
public String getMsgUser() {
return msgUser;
}
/**
* Initializes the EPCIS schema from a XSD file.
*
......
return null;
}
/**
* Loads the application's properties file from the class path.
*
* @return A populated Properties instance.
*/
private Properties loadProperties(String file) {
// read application properties from classpath
InputStream is = this.getClass().getResourceAsStream(file);
Properties prop = new Properties();
try {
prop.load(is);
is.close();
} catch (IOException e) {
LOG.error("Unable to load application properties from classpath:" + file + " ("
+ this.getClass().getResource(file) + ")", e);
}
return prop;
}
/**
* Loads the connection to the database.
*
* @return The connection to the database.
*/
private java.sql.Connection loadDatabaseConnection() {
String username = properties.getProperty(PROP_USERNAME, DEFAULT_USERNAME);
String password = properties.getProperty(PROP_PASSWORD, DEFAULT_PASSWORD);
String url = properties.getProperty(PROP_URL, DEFAULT_URL);
java.sql.Connection c = null;
try {
Class.forName("com.mysql.jdbc.Driver");
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("user", Constants.DATABASE_LOGIN);
props.setProperty("password", Constants.DATABASE_PASSWORD);
props.setProperty("autoReconnect", "true");
c = DriverManager.getConnection(url, props);
c = DriverManager.getConnection(Constants.DATABASE_URL, props);
c.setAutoCommit(false);
LOG.debug("MySQL connection established");
} catch (ClassNotFoundException e) {
......
* @throws JMSException If a receiving or sending message error occurred.
* @throws SQLException If an error involving the database occurred
* @throws SAXException If an error processing the XML document occurred.
* @throws InvalidFormatException If an error parsing the XML contents
* occurred.
* @throws IOException If an I/O error occured.
* @throws Exception If an unexpected error occurred.
*/
public void receiveFilterSend() throws JMSException, SQLException, SAXException,
InvalidFormatException, IOException, Exception {
public void receiveFilterSend() throws JMSException, SQLException, SAXException, IOException, JAXBException, Exception {
ActiveMQConnectionFactory factory;
if (msgUser != null && msgPassword != null && !msgUser.isEmpty() && !msgPassword.isEmpty()) {
factory = new ActiveMQConnectionFactory(msgUser, msgPassword, msgUrl);
if (Constants.ACTIVEMQ_LOGIN != null && Constants.ACTIVEMQ_PASSWORD != null
&& !Constants.ACTIVEMQ_LOGIN.isEmpty() && !Constants.ACTIVEMQ_PASSWORD.isEmpty()) {
factory = new ActiveMQConnectionFactory(Constants.ACTIVEMQ_LOGIN, Constants.ACTIVEMQ_PASSWORD, Constants.ACTIVEMQ_URL);
} else {
factory = new ActiveMQConnectionFactory(msgUrl);
factory = new ActiveMQConnectionFactory(Constants.ACTIVEMQ_URL);
}
javax.jms.Connection msgConnection = factory.createConnection();
Session session = msgConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Destination destination = session.createQueue(consummerQueueName);
Destination destination = session.createQueue(Constants.CONSUMMER_QUEUE_NAME);
MessageConsumer consumer = session.createConsumer(destination);
msgConnection.start();
......
if (isPermitted(docText)) {
// --- Sending authorized message to the queue
Session prodS = msgConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Destination sendDest = prodS.createQueue(senderQueueName);
Destination sendDest = prodS.createQueue(Constants.SENDER_QUEUE_NAME);
MessageProducer producer = prodS.createProducer(sendDest);
TextMessage messageToSend = prodS.createTextMessage();
messageToSend.setText(docText);
producer.send(messageToSend);
LOG.info("Event transferred to the next queue.");
} else {
LOG.debug("Event deleted");
LOG.info("Event deleted.");
}
}
message.acknowledge();
}
} finally {
session.close();
msgConnection.close();
}
}
/**
* Checks if the user is authorized to receive events.
*
* @param docString The events to check.
* @return
* <code>true</code> if the user is authorized to receive events.
* @return <code>true</code> if the user is authorized to receive events.
* @throws SQLException If an error involving the database occurred.
* @throws SAXException If an error processing the XML document occurred.
* @throws InvalidFormatException If an error parsing the XML contents
* occurred.
* @throws IOException If an I/O error occured.
* @throws JAXBException If an error parsing from the XML document to objects occurred.
* @throws SQLException If an error involving the database occurred.
* @throws IOException
*/
private boolean isPermitted(String docString) throws SQLException, SAXException,
InvalidFormatException, IOException {
private boolean isPermitted(String docString) throws SAXException, IOException, JAXBException, SQLException {
Document doc = getDocumentFromString(docString);
List<BaseEvent> callbackEventList = getCallbackEventList(doc);
List<EPCISEventType> callbackEventList = extractsCallbackEventList(doc);
String subscriptionId = getSubIDInDoc(doc);
String user = fetchUser(subscriptionId);
if (callbackCheck.xacmlCheck(callbackEventList, user)) {
......
/**
* Parses a XML document and returns the associated callback events list.
*
* @param document The xml document to parse.
* @return The callback events list.
* @throws DOMException If a DOM operation error occurred.
* @param document The XML document to parse.
* @return The callback events list from the document.
* @throws SAXException If an error processing the XML document occurred.
* @throws InvalidFormatException If an error parsing the XML contents
* occurred.
* @throws JAXBException If an error parsing from the XML document to objects occurred.
*/
private List<BaseEvent> getCallbackEventList(Document document)
throws DOMException, SAXException, InvalidFormatException {
private List<EPCISEventType> extractsCallbackEventList(Document document)
throws SAXException, JAXBException {
NodeList eventList = document.getElementsByTagName("EventList");
NodeList events = eventList.item(0).getChildNodes();
List<BaseEvent> callbackEventList = new ArrayList<BaseEvent>();
List<EPCISEventType> callbackEventList = new ArrayList<EPCISEventType>();
for (int i = 0; i < events.getLength(); i++) {
Node eventNode = events.item(i);
String nodeName = eventNode.getNodeName();
if (EpcisConstants.OBJECT_EVENT.equals(nodeName)
|| EpcisConstants.AGGREGATION_EVENT.equals(nodeName)
|| EpcisConstants.QUANTITY_EVENT.equals(nodeName)
|| EpcisConstants.TRANSACTION_EVENT.equals(nodeName)) {
LOG.debug("processing event " + i + ": '" + nodeName + "'.");
BaseEvent callbackEvent = getCallbackEvent(eventNode, nodeName);
EPCISEventType callbackEvent = extractsCaptureEvent(eventNode, nodeName);
if (callbackEvent != null) {
callbackEventList.add(callbackEvent);
}
......
}
/**
* Parses the XML node and returns a callback event for XACML check.
* Parses the XML node and returns callback event.
*
* @param eventNode The XML node.
* @param eventType The type of the event.
* @return The callback event.
* @throws DOMException If a DOM operation error occurred.
* @throws SAXException If an error processing the XML document occurred.
* @throws InvalidFormatException If an error parsing the XML contents
* occurred (EPC or eventTimeZoneOffset is invalid).
* @throws SAXException If an error parsing the XML document occurred.
* @throws JAXBException If an error parsing the XML document to object occurred.
* occurred.
*/
private BaseEvent getCallbackEvent(Node eventNode, String eventType)
throws DOMException, SAXException, InvalidFormatException {
private EPCISEventType extractsCaptureEvent(Node eventNode, String eventType) throws SAXException, JAXBException {
if (eventNode == null) {
// nothing to do
return null;
} else if (eventNode.getChildNodes().getLength() == 0) {
throw new SAXException("Event element '" + eventNode.getNodeName() + "' has no children elements.");
}
Node curEventNode = null;
Timestamp eventTime = null;
Timestamp recordTime = new Timestamp(System.currentTimeMillis());
String eventTimeZoneOffset = null;
String action = null;
String parentId = null;
Long quantity = null;
String bizStep = null;
String disposition = null;
String readPoint = null;
String bizLocation = null;
String epcClassStr = null;
List<String> epcs = null;
List<BusinessTransaction> bizTransList = null;
List<EventFieldExtension> fieldNameExtList = new ArrayList<EventFieldExtension>();
for (int i = 0; i < eventNode.getChildNodes().getLength(); i++) {
curEventNode = eventNode.getChildNodes().item(i);
String nodeName = curEventNode.getNodeName();
if ("#text".equals(nodeName) || "#comment".equals(nodeName)) {
// ignore text or comments
LOG.debug(" ignoring text or comment: '" + curEventNode.getTextContent().trim() + "'");
continue;
}
LOG.debug(" handling event field: '" + nodeName + "'");
if ("eventTime".equals(nodeName)) {
String xmlTime = curEventNode.getTextContent();
LOG.debug(" eventTime in xml is '" + xmlTime + "'");
try {
eventTime = TimeParser.parseAsTimestamp(xmlTime);
} catch (ParseException e) {
throw new SAXException("Invalid date/time (must be ISO8601).", e);
}
LOG.debug(" eventTime parsed as '" + eventTime + "'");
} else if ("recordTime".equals(nodeName)) {
// ignore recordTime
} else if ("eventTimeZoneOffset".equals(nodeName)) {
eventTimeZoneOffset = checkEventTimeZoneOffset(curEventNode.getTextContent());
} else if ("epcList".equals(nodeName) || "childEPCs".equals(nodeName)) {
epcs = handleEpcs(eventType, curEventNode);
} else if ("bizTransactionList".equals(nodeName)) {
bizTransList = handleBizTransactions(curEventNode);
} else if ("action".equals(nodeName)) {
action = curEventNode.getTextContent();
if (!"ADD".equals(action) && !"OBSERVE".equals(action) && !"DELETE".equals(action)) {
throw new SAXException("Encountered illegal 'action' value: " + action);
}
} else if ("bizStep".equals(nodeName)) {
bizStep = curEventNode.getTextContent();
} else if ("disposition".equals(nodeName)) {
disposition = curEventNode.getTextContent();
} else if ("readPoint".equals(nodeName)) {
Element attrElem = (Element) curEventNode;
Node id = attrElem.getElementsByTagName("id").item(0);
readPoint = id.getTextContent();
} else if ("bizLocation".equals(nodeName)) {
Element attrElem = (Element) curEventNode;
Node id = attrElem.getElementsByTagName("id").item(0);
bizLocation = id.getTextContent();
} // TODO epc class filter?
else if ("epcClass".equals(nodeName)) {
epcClassStr = curEventNode.getTextContent();
} else if ("quantity".equals(nodeName)) {
quantity = Long.valueOf(curEventNode.getTextContent());
} else if ("parentID".equals(nodeName)) {
checkEpcOrUri(curEventNode.getTextContent(), false);
parentId = curEventNode.getTextContent();
} else {
String[] parts = nodeName.split(":");
if (parts.length == 2) {
LOG.debug(" treating unknown event field as extension.");
String prefix = parts[0];
String localname = parts[1];
// String namespace =
// document.getDocumentElement().getAttribute("xmlns:" +
// prefix);
String namespace = curEventNode.lookupNamespaceURI(prefix);
String value = curEventNode.getTextContent();
EventFieldExtension evf = new EventFieldExtension(prefix, namespace, localname, value);
fieldNameExtList.add(evf);
} else {
// this is not a valid extension
throw new SAXException(" encountered unknown event field: '" + nodeName + "'.");
}
}
}
EPCISEventType callbackEvent;
if (EpcisConstants.AGGREGATION_EVENT.equals(eventType)) {
// for AggregationEvents, the parentID is only optional for
// action=OBSERVE
if (parentId == null && ("ADD".equals(action) || "DELETE".equals(action))) {
throw new InvalidFormatException("'parentID' is required if 'action' is ADD or DELETE");
}
}
String nodeName = eventNode.getNodeName();
BaseEvent callbackEvent;
BusinessStepId bizStepId = new BusinessStepId();
bizStepId.setUri(bizStep);
DispositionId dispositionId = new DispositionId();
dispositionId.setUri(disposition);
BusinessLocationId bizLocationId = new BusinessLocationId();
bizLocationId.setUri(bizLocation);
ReadPointId readPointId = new ReadPointId();
readPointId.setUri(readPoint);
EPCClass epcClass = new EPCClass();
epcClass.setUri(epcClassStr);
if (EpcisConstants.AGGREGATION_EVENT.equals(nodeName)) {
AggregationEvent ae = new AggregationEvent();
ae.setParentId(parentId);
ae.setChildEpcs(epcs);
ae.setAction(Action.valueOf(action));
callbackEvent = ae;
} else if (EpcisConstants.OBJECT_EVENT.equals(nodeName)) {
ObjectEvent oe = new ObjectEvent();
oe.setAction(Action.valueOf(action));
if (epcs != null && epcs.size() > 0) {
oe.setEpcList(epcs);
}
callbackEvent = oe;
} else if (EpcisConstants.QUANTITY_EVENT.equals(nodeName)) {
QuantityEvent qe = new QuantityEvent();
qe.setEpcClass(epcClass);
if (quantity != null) {
qe.setQuantity(quantity);
}
callbackEvent = qe;
} else if (EpcisConstants.TRANSACTION_EVENT.equals(nodeName)) {
TransactionEvent te = new TransactionEvent();
te.setParentId(parentId);
te.setEpcList(epcs);
te.setAction(Action.valueOf(action));
callbackEvent = te;
JAXBContext context = JAXBContext.newInstance(AggregationEventType.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<AggregationEventType> jElement = unmarshaller.unmarshal(eventNode, AggregationEventType.class);
callbackEvent = jElement.getValue();
} else if (EpcisConstants.OBJECT_EVENT.equals(eventType)) {
JAXBContext context = JAXBContext.newInstance(ObjectEventType.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<ObjectEventType> jElement = unmarshaller.unmarshal(eventNode, ObjectEventType.class);
callbackEvent = jElement.getValue();
} else if (EpcisConstants.QUANTITY_EVENT.equals(eventType)) {
JAXBContext context = JAXBContext.newInstance(QuantityEventType.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<QuantityEventType> jElement = unmarshaller.unmarshal(eventNode, QuantityEventType.class);
callbackEvent = jElement.getValue();
} else if (EpcisConstants.TRANSACTION_EVENT.equals(eventType)) {
JAXBContext context = JAXBContext.newInstance(TransactionEventType.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<TransactionEventType> jElement = unmarshaller.unmarshal(eventNode, TransactionEventType.class);
callbackEvent = jElement.getValue();
} else {
throw new SAXException("Encountered unknown event element '" + nodeName + "'.");
}
callbackEvent.setEventTime(eventTime);
callbackEvent.setRecordTime(recordTime);
callbackEvent.setEventTimeZoneOffset(eventTimeZoneOffset);
callbackEvent.setBizStep(bizStepId);
callbackEvent.setDisposition(dispositionId);
callbackEvent.setBizLocation(bizLocationId);
callbackEvent.setReadPoint(readPointId);
if (bizTransList != null && bizTransList.size() > 0) {
callbackEvent.setBizTransList(bizTransList);
}
if (!fieldNameExtList.isEmpty()) {
callbackEvent.setExtensions(fieldNameExtList);
throw new SAXException("Encountered unknown event element '" + eventType + "'.");
}
return callbackEvent;
}
/**
* Parses the xml tree for epc nodes and returns a List of BizTransaction
* URIs with their corresponding type.
*
* @param bizNode The parent Node from which BizTransaction URIs should be
* extracted.
* @return A List of BizTransaction.
* @throws SAXException If an unknown tag (no &lt;epc&gt;) is encountered.
*/
private List<BusinessTransaction> handleBizTransactions(Node bizNode) throws SAXException {
List<BusinessTransaction> bizTransactionList = new ArrayList<BusinessTransaction>();
for (int i = 0; i < bizNode.getChildNodes().getLength(); i++) {
Node curNode = bizNode.getChildNodes().item(i);
if ("bizTransaction".equals(curNode.getNodeName())) {
String bizTransTypeUri = curNode.getAttributes().item(0).getTextContent();
String bizTransUri = curNode.getTextContent();
BusinessTransactionId bizTransId = new BusinessTransactionId();
bizTransId.setUri(bizTransUri);
BusinessTransactionTypeId bizTransTypeId = new BusinessTransactionTypeId();
bizTransTypeId.setUri(bizTransTypeUri);
BusinessTransaction bizTransaction = new BusinessTransaction();
bizTransaction.setBizTransaction(bizTransId);
bizTransaction.setType(bizTransTypeId);
bizTransactionList.add(bizTransaction);
} else {
if (!"#text".equals(curNode.getNodeName()) && !"#comment".equals(curNode.getNodeName())) {
throw new SAXException("Unknown XML tag: " + curNode.getNodeName(), null);
}
}
}
return bizTransactionList;
}
/**
* Parses the xml tree for epc nodes and returns a list of EPC URIs.
*
* @param eventType
* @param epcNode The parent Node from which EPC URIs should be extracted.
* @return An array of vocabularies containing all the URIs found in the
* given node.
* @throws SAXException If an unknown tag (no &lt;epc&gt;) is encountered.
* @throws InvalidFormatException If a 'pure identity' EPC format is
* invalid.
* @throws DOMException If a DOM operation error occurred.
*/
private List<String> handleEpcs(final String eventType, final Node epcNode)
throws SAXException, DOMException, InvalidFormatException {
List<String> epcList = new ArrayList<String>();
boolean isEpc = false;
boolean epcRequired = false;
boolean atLeastOneNonEpc = false;
for (int i = 0; i < epcNode.getChildNodes().getLength(); i++) {
Node curNode = epcNode.getChildNodes().item(i);
if ("epc".equals(curNode.getNodeName())) {
isEpc = checkEpcOrUri(curNode.getTextContent(), epcRequired);
if (isEpc) {
// if one of the values is an EPC, then all of them must be
// valid EPCs
epcRequired = true;
} else {
atLeastOneNonEpc = true;
}
epcList.add(curNode.getTextContent());
} else {
if ("#text".equals(curNode.getNodeName()) && "#comment".equals(curNode.getNodeName())) {
throw new SAXException("Unknown XML tag: " + curNode.getNodeName(), null);
}
}
}
if (atLeastOneNonEpc && isEpc) {
throw new InvalidFormatException(
"One of the provided EPCs was a 'pure identity' EPC, so all of them must be 'pure identity' EPCs");
}
return epcList;
}
/**
* Checks the event time zone.
*
* @param textContent The event time zone to check.
* @return The checked event time zone.
* @throws InvalidFormatException If the given
* <code>eventTimeZoneOffset</code> format is invalid.
*/
private String checkEventTimeZoneOffset(String textContent) throws InvalidFormatException {
// first check the provided String against the expected pattern
Pattern p = Pattern.compile("[+-]\\d\\d:\\d\\d");
Matcher m = p.matcher(textContent);
boolean matches = m.matches();
if (matches) {
// second check the values (hours and minutes)
int h = Integer.parseInt(textContent.substring(1, 3));
int min = Integer.parseInt(textContent.substring(4, 6));
if ((h < 0) || (h > 14)) {
matches = false;
} else if (h == 14 && min != 0) {
matches = false;
} else if ((min < 0) || (min > 59)) {
matches = false;
}
}
if (matches) {
return textContent;
} else {
throw new InvalidFormatException("'eventTimeZoneOffset' has invalid format: " + textContent);
}
}
/**
* Checks an EPC or an URI.
*
* @param epcOrUri The EPC or URI to check.
* @param epcRequired<code>true</code> if an EPC is required (will throw an
* InvalidFormatException if the given
* <code>epcOrUri</code> is an invalid EPC, but might be a valid URI),
* <code>false</code> otherwise.
* @return
* <code>true</code> if the given
* <code>epcOrUri</code> is a valid EPC,
* <code>false</code> otherwise.
* @throws InvalidFormatException If the 'pure identity' EPC format is
* invalid.
*/
private boolean checkEpcOrUri(String epcOrUri, boolean epcRequired) throws InvalidFormatException {
boolean isEpc = false;
if (epcOrUri.startsWith("urn:epc:id:")) {
// check if it is a valid EPC
checkEpc(epcOrUri);
isEpc = true;
} else {
// childEPCs in AggregationEvents, and epcList in
// TransactionEvents might also be simple URIs
if (epcRequired) {
throw new InvalidFormatException(
"One of the provided EPCs was a 'pure identity' EPC, so all of them must be 'pure identity' EPCs");
}
checkUri(epcOrUri);
}
return isEpc;
}
/**
* Checks an EPC according to 'pure identity' URI as specified in Tag Data
* Standard.
*
* @param textContent The EPC to check.
* @throws InvalidFormatException If the 'pure identity' EPC format is
* invalid.
*/
private void checkEpc(String textContent) throws InvalidFormatException {
String uri = textContent;
if (!uri.startsWith("urn:epc:id:")) {
throw new InvalidFormatException("Invalid 'pure identity' EPC format: must start with \"urn:epc:id:\"");
}
uri = uri.substring("urn:epc:id:".length());
// check the patterns for the different EPC types
String epcType = uri.substring(0, uri.indexOf(":"));
uri = uri.substring(epcType.length() + 1);
LOG.debug("Checking pattern for EPC type " + epcType + ": " + uri);
Pattern p;
if ("gid".equals(epcType)) {
p = Pattern.compile("((0|[1-9][0-9]*)\\.){2}(0|[1-9][0-9]*)");
} else if ("sgtin".equals(epcType) || "sgln".equals(epcType) || "grai".equals(epcType)) {
p = Pattern.compile("([0-9]+\\.){2}([0-9]|[A-Z]|[a-z]|[\\!\\(\\)\\*\\+\\-',:;=_]|(%(([0-9]|[A-F])|[a-f]){2}))+");
} else if ("sscc".equals(epcType)) {
p = Pattern.compile("[0-9]+\\.[0-9]+");
} else if ("giai".equals(epcType)) {
p = Pattern.compile("[0-9]+\\.([0-9]|[A-Z]|[a-z]|[\\!\\(\\)\\*\\+\\-',:;=_]|(%(([0-9]|[A-F])|[a-f]){2}))+");
} else {
throw new InvalidFormatException("Invalid 'pure identity' EPC format: unknown EPC type: " + epcType);
}
Matcher m = p.matcher(uri);
if (!m.matches()) {
throw new InvalidFormatException("Invalid 'pure identity' EPC format: pattern \"" + uri
+ "\" is invalid for EPC type \"" + epcType + "\" - check with Tag Data Standard");
}
// check the number of digits for the different EPC types
boolean exceeded = false;
int count1 = uri.indexOf(".");
if ("sgtin".equals(epcType)) {
int count2 = uri.indexOf(".", count1 + 1) - (count1 + 1);
if (count1 + count2 > 13) {
exceeded = true;
}
} else if ("sgln".equals(epcType)) {
int count2 = uri.indexOf(".", count1 + 1) - (count1 + 1);
if (count1 + count2 > 12) {
exceeded = true;
}
} else if ("grai".equals(epcType)) {
int count2 = uri.indexOf(".", count1 + 1) - (count1 + 1);
if (count1 + count2 > 12) {
exceeded = true;
}
} else if ("sscc".equals(epcType)) {
int count2 = uri.length() - (count1 + 1);
if (count1 + count2 > 17) {
exceeded = true;
}
} else if ("giai".equals(epcType)) {
int count2 = uri.length() - (count1 + 1);
if (count1 + count2 > 30) {
exceeded = true;
}
} else {
// nothing to count
}
if (exceeded) {
throw new InvalidFormatException(
"Invalid 'pure identity' EPC format: check allowed number of characters for EPC type '"
+ epcType + "'");
}
}
/**
* Checks the URI.
*
* @param textContent The URI to check.
* @return
* <code>true</code> if the URI is valid.
* @throws InvalidFormatException If the URI is invalid.
*/
private boolean checkUri(String textContent) throws InvalidFormatException {
try {
new URI(textContent);
} catch (URISyntaxException e) {
throw new InvalidFormatException(e.getMessage(), e);
}
return true;
}
/**
* Returns the subcription ID from a XML document.
*

Formats disponibles : Unified diff