Java tutorial
/* * Copyright (C) 2007 ETH Zurich * * This file is part of Accada (www.accada.org). * * Accada is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * Accada is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Accada; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ package org.accada.epcis.repository.capture; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.Principal; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.accada.epcis.repository.EpcisConstants; import org.accada.epcis.repository.InvalidFormatException; import org.accada.epcis.repository.model.Action; import org.accada.epcis.repository.model.AggregationEvent; import org.accada.epcis.repository.model.BaseEvent; import org.accada.epcis.repository.model.BusinessLocationId; import org.accada.epcis.repository.model.BusinessStepId; import org.accada.epcis.repository.model.BusinessTransaction; import org.accada.epcis.repository.model.BusinessTransactionId; import org.accada.epcis.repository.model.BusinessTransactionTypeId; import org.accada.epcis.repository.model.DispositionId; import org.accada.epcis.repository.model.EPCClass; import org.accada.epcis.repository.model.EventFieldExtension; import org.accada.epcis.repository.model.ObjectEvent; import org.accada.epcis.repository.model.QuantityEvent; import org.accada.epcis.repository.model.ReadPointId; import org.accada.epcis.repository.model.TransactionEvent; import org.accada.epcis.repository.model.VocabularyElement; import org.accada.epcis.utils.TimeParser; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.Criteria; import org.hibernate.ObjectNotFoundException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.criterion.Restrictions; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * CaptureOperationsModule implements the core capture operations. Converts XML * events delivered by HTTP POST into SQL and inserts them into the database. * <p> * TODO: the parsing of the xml inputstream should be done in the * CaptureOperationsServlet; this class should implement EpcisCaptureInterface * such that CaptureOperationsServlet can call its capture method and provide it * with the parsed events. * * @author David Gubler * @author Alain Remund * @author Marco Steybe */ public class CaptureOperationsModule { private static final Log LOG = LogFactory.getLog(CaptureOperationsModule.class); private static final Map<String, Class<?>> vocClassMap = new HashMap<String, Class<?>>(); static { vocClassMap.put(EpcisConstants.BUSINESS_LOCATION_ID, BusinessLocationId.class); vocClassMap.put(EpcisConstants.BUSINESS_STEP_ID, BusinessStepId.class); vocClassMap.put(EpcisConstants.BUSINESS_TRANSACTION_ID, BusinessTransactionId.class); vocClassMap.put(EpcisConstants.BUSINESS_TRANSACTION_TYPE_ID, BusinessTransactionTypeId.class); vocClassMap.put(EpcisConstants.DISPOSITION_ID, DispositionId.class); vocClassMap.put(EpcisConstants.EPC_CLASS_ID, EPCClass.class); vocClassMap.put(EpcisConstants.READ_POINT_ID, ReadPointId.class); } /** * The XSD schema which validates the incoming messages. */ private Schema schema; /** * Whether we should insert new vocabulary or throw an error message. */ private boolean insertMissingVoc = true; /** * Whether the dbReset operation is allowed or not. */ private boolean dbResetAllowed = false; /** * The name of the SQL script used to clean and refill the database with * test data. */ private File dbResetScript = null; /** * Interface to the database. */ private SessionFactory sessionFactory; /** * Initializes the EPCIS schema used for validating incoming capture * requests. Loads the WSDL and XSD files from the classpath (the schema is * bundled with epcis-commons.jar). * * @return An instantiated schema validation object. */ private Schema initEpcisSchema(String xsdFile) { InputStream is = this.getClass().getResourceAsStream(xsdFile); if (is != null) { try { SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Source schemaSrc = new StreamSource(is); schemaSrc.setSystemId(CaptureOperationsServlet.class.getResource(xsdFile).toString()); Schema schema = schemaFactory.newSchema(schemaSrc); LOG.debug("EPCIS schema file initialized and loaded successfully"); return schema; } catch (Exception e) { LOG.warn("Unable to load or parse the EPCIS schema", e); } } else { LOG.error("Unable to load the EPCIS schema file from classpath: cannot find resource " + xsdFile); } LOG.warn("Schema validation will not be available!"); return null; } /** * Resets the database. * * @throws SQLException * If something goes wrong resetting the database. * @throws IOException * If something goes wrong reading the reset script. * @throws UnsupportedOperationsException * If database resets are not allowed. */ public void doDbReset() throws SQLException, IOException { if (dbResetAllowed) { Session session = null; try { session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Connection connection = session.connection(); LOG.info("Running db reset script from file " + dbResetScript); Statement stmt = connection.createStatement(); BufferedReader reader = new BufferedReader(new FileReader(dbResetScript)); String line; while ((line = reader.readLine()) != null) { if (!line.startsWith("--")) { LOG.debug("SQL: " + line); stmt.addBatch(line); } } stmt.executeBatch(); tx.commit(); } catch (Exception e) { LOG.error("dbReset failed: " + e.toString(), e); if (tx != null) { tx.rollback(); } throw new SQLException(e.toString()); } } finally { if (session != null) { session.close(); } } } else { throw new UnsupportedOperationException(); } } /** * Implements the EPCIS capture operation. Takes an input stream, extracts * the payload into an XML document, validates the document against the * EPCIS schema, and captures the EPCIS events given in the document. * * @throws IOException * If an error occurred while validating the request or writing * the response. * @throws ParserConfigurationException * @throws SAXException * If the XML document is malformed or invalid * @throws InvalidFormatException */ public void doCapture(InputStream in, Principal principal) throws SAXException, IOException, InvalidFormatException { // parse the payload as XML document Document document; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.parse(in); LOG.debug("Payload successfully parsed as XML document"); if (LOG.isDebugEnabled()) { try { TransformerFactory tfFactory = TransformerFactory.newInstance(); Transformer transformer = tfFactory.newTransformer(); StringWriter writer = new StringWriter(); transformer.transform(new DOMSource(document), new StreamResult(writer)); String xml = writer.toString(); if (xml.length() > 100 * 1024) { // too large, do not log xml = null; } else { LOG.debug("Incoming contents:\n\n" + writer.toString() + "\n"); } } catch (Exception e) { // never mind ... do not log } } // validate the XML document against the EPCISDocument schema if (schema != null) { Validator validator = schema.newValidator(); try { validator.validate(new DOMSource(document), null); } catch (SAXParseException e) { // TODO: we need to ignore XML element order, the following // is only a hack to pass some of the conformance tests if (e.getMessage().contains("parentID")) { LOG.warn("Ignoring XML validation exception: " + e.getMessage()); } else { throw e; } } LOG.info("Incoming capture request was successfully validated against the EPCISDocument schema"); } else { LOG.warn("Schema validator unavailable. Unable to validate EPCIS capture event against schema!"); } } catch (ParserConfigurationException e) { throw new SAXException(e); } // handle the capture operation Session session = null; try { session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); LOG.debug("DB connection opened."); handleDocument(session, document); tx.commit(); // return OK LOG.info("EPCIS Capture Interface request succeeded"); } catch (SAXException e) { LOG.error("EPCIS Capture Interface request failed: " + e.toString()); if (tx != null) { tx.rollback(); } throw e; } catch (InvalidFormatException e) { LOG.error("EPCIS Capture Interface request failed: " + e.toString()); if (tx != null) { tx.rollback(); } throw e; } catch (Exception e) { // Hibernate throws RuntimeExceptions, so don't let them // (or anything else) escape without clean up LOG.error("EPCIS Capture Interface request failed: " + e.toString(), e); if (tx != null) { tx.rollback(); } throw new IOException(e.toString()); } } finally { if (session != null) { session.close(); } // sessionFactory.getStatistics().logSummary(); LOG.debug("DB connection closed"); } } /** * Parses the entire document and handles the supplied events. * * @throws Exception * @throws DOMException */ private void handleDocument(Session session, Document document) throws DOMException, SAXException, InvalidFormatException { NodeList eventList = document.getElementsByTagName("EventList"); NodeList events = eventList.item(0).getChildNodes(); // walk through all supplied events int eventCount = 0; for (int i = 0; i < events.getLength(); i++) { Node eventNode = events.item(i); String nodeName = eventNode.getNodeName(); if (nodeName.equals(EpcisConstants.OBJECT_EVENT) || nodeName.equals(EpcisConstants.AGGREGATION_EVENT) || nodeName.equals(EpcisConstants.QUANTITY_EVENT) || nodeName.equals(EpcisConstants.TRANSACTION_EVENT)) { LOG.debug("processing event " + i + ": '" + nodeName + "'."); handleEvent(session, eventNode, nodeName); eventCount++; if (eventCount % 50 == 0) { session.flush(); session.clear(); } } else if (!nodeName.equals("#text") && !nodeName.equals("#comment")) { throw new SAXException("Encountered unknown event '" + nodeName + "'."); } } } /** * Takes an XML document node, parses it as EPCIS event and inserts the data * into the database. The parse routine is generic for all event types; the * query generation part has some if/elses to take care of different event * parameters. * * @param eventNode * The current event node. * @param eventType * The current event type. * @throws Exception * @throws DOMException */ private void handleEvent(Session session, final Node eventNode, final String eventType) throws DOMException, SAXException, InvalidFormatException { if (eventNode == null) { // nothing to do return; } else if (eventNode.getChildNodes().getLength() == 0) { throw new SAXException("Event element '" + eventNode.getNodeName() + "' has no children elements."); } Node curEventNode = null; // A lot of the initialized variables have type URI. This type isn't to // compare with the URI-Type of the standard. In fact, most of the // variables having type URI are declared as Vocabularies in the // Standard. Commonly, we use String for the Standard-Type URI. Timestamp eventTime = null; Timestamp recordTime = new Timestamp(System.currentTimeMillis()); String eventTimeZoneOffset = null; String action = null; String parentId = null; Long quantity = null; String bizStepUri = null; String dispositionUri = null; String readPointUri = null; String bizLocationUri = null; String epcClassUri = 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 (nodeName.equals("#text") || nodeName.equals("#comment")) { // ignore text or comments LOG.debug(" ignoring text or comment: '" + curEventNode.getTextContent().trim() + "'"); continue; } LOG.debug(" handling event field: '" + nodeName + "'"); if (nodeName.equals("eventTime")) { 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 (nodeName.equals("recordTime")) { // ignore recordTime } else if (nodeName.equals("eventTimeZoneOffset")) { eventTimeZoneOffset = checkEventTimeZoneOffset(curEventNode.getTextContent()); } else if (nodeName.equals("epcList") || nodeName.equals("childEPCs")) { epcs = handleEpcs(eventType, curEventNode); } else if (nodeName.equals("bizTransactionList")) { bizTransList = handleBizTransactions(session, curEventNode); } else if (nodeName.equals("action")) { action = curEventNode.getTextContent(); if (!action.equals("ADD") && !action.equals("OBSERVE") && !action.equals("DELETE")) { throw new SAXException("Encountered illegal 'action' value: " + action); } } else if (nodeName.equals("bizStep")) { bizStepUri = curEventNode.getTextContent(); } else if (nodeName.equals("disposition")) { dispositionUri = curEventNode.getTextContent(); } else if (nodeName.equals("readPoint")) { Element attrElem = (Element) curEventNode; Node id = attrElem.getElementsByTagName("id").item(0); readPointUri = id.getTextContent(); } else if (nodeName.equals("bizLocation")) { Element attrElem = (Element) curEventNode; Node id = attrElem.getElementsByTagName("id").item(0); bizLocationUri = id.getTextContent(); } else if (nodeName.equals("epcClass")) { epcClassUri = curEventNode.getTextContent(); } else if (nodeName.equals("quantity")) { quantity = Long.valueOf(curEventNode.getTextContent()); } else if (nodeName.equals("parentID")) { 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 + "'."); } } } if (eventType.equals(EpcisConstants.AGGREGATION_EVENT)) { // 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(); VocabularyElement bizStep = bizStepUri != null ? getOrInsertVocabularyElement(session, EpcisConstants.BUSINESS_STEP_ID, String.valueOf(bizStepUri)) : null; VocabularyElement disposition = dispositionUri != null ? getOrInsertVocabularyElement(session, EpcisConstants.DISPOSITION_ID, String.valueOf(dispositionUri)) : null; VocabularyElement readPoint = readPointUri != null ? getOrInsertVocabularyElement(session, EpcisConstants.READ_POINT_ID, String.valueOf(readPointUri)) : null; VocabularyElement bizLocation = bizLocationUri != null ? getOrInsertVocabularyElement(session, EpcisConstants.BUSINESS_LOCATION_ID, String.valueOf(bizLocationUri)) : null; VocabularyElement epcClass = epcClassUri != null ? getOrInsertVocabularyElement(session, EpcisConstants.EPC_CLASS_ID, String.valueOf(epcClassUri)) : null; BaseEvent be; if (nodeName.equals(EpcisConstants.AGGREGATION_EVENT)) { AggregationEvent ae = new AggregationEvent(); ae.setParentId(parentId); ae.setChildEpcs(epcs); ae.setAction(Action.valueOf(action)); be = ae; } else if (nodeName.equals(EpcisConstants.OBJECT_EVENT)) { ObjectEvent oe = new ObjectEvent(); oe.setAction(Action.valueOf(action)); if (epcs != null && epcs.size() > 0) { oe.setEpcList(epcs); } be = oe; } else if (nodeName.equals(EpcisConstants.QUANTITY_EVENT)) { QuantityEvent qe = new QuantityEvent(); qe.setEpcClass((EPCClass) epcClass); if (quantity != null) { qe.setQuantity(quantity.longValue()); } be = qe; } else if (nodeName.equals(EpcisConstants.TRANSACTION_EVENT)) { TransactionEvent te = new TransactionEvent(); te.setParentId(parentId); te.setEpcList(epcs); te.setAction(Action.valueOf(action)); be = te; } else { throw new SAXException("Encountered unknown event element '" + nodeName + "'."); } be.setEventTime(eventTime); be.setRecordTime(recordTime); be.setEventTimeZoneOffset(eventTimeZoneOffset); be.setBizStep((BusinessStepId) bizStep); be.setDisposition((DispositionId) disposition); be.setBizLocation((BusinessLocationId) bizLocation); be.setReadPoint((ReadPointId) readPoint); if (bizTransList != null && bizTransList.size() > 0) { be.setBizTransList(bizTransList); } if (!fieldNameExtList.isEmpty()) { be.setExtensions(fieldNameExtList); } session.save(be); } /** * 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 <epc>) is encountered. * @throws InvalidFormatException * @throws DOMException */ 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 (curNode.getNodeName().equals("epc")) { 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 (curNode.getNodeName() != "#text" && curNode.getNodeName() != "#comment") { 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; } /** * @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 */ protected 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; } /** * 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 <epc>) is encountered. */ private List<BusinessTransaction> handleBizTransactions(Session session, 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 (curNode.getNodeName().equals("bizTransaction")) { String bizTransTypeUri = curNode.getAttributes().item(0).getTextContent(); String bizTransUri = curNode.getTextContent(); BusinessTransactionId bizTrans = (BusinessTransactionId) getOrInsertVocabularyElement(session, EpcisConstants.BUSINESS_TRANSACTION_ID, bizTransUri.toString()); BusinessTransactionTypeId type = (BusinessTransactionTypeId) getOrInsertVocabularyElement(session, EpcisConstants.BUSINESS_TRANSACTION_TYPE_ID, bizTransTypeUri.toString()); Criteria c0 = session.createCriteria(BusinessTransaction.class); c0.add(Restrictions.eq("bizTransaction", bizTrans)); c0.add(Restrictions.eq("type", type)); BusinessTransaction bizTransaction = (BusinessTransaction) c0.uniqueResult(); if (bizTransaction == null) { bizTransaction = new BusinessTransaction(); bizTransaction.setBizTransaction(bizTrans); bizTransaction.setType(type); session.save(bizTransaction); } bizTransactionList.add(bizTransaction); } else { if (!curNode.getNodeName().equals("#text") && !curNode.getNodeName().equals("#comment")) { throw new SAXException("Unknown XML tag: " + curNode.getNodeName(), null); } } } return bizTransactionList; } /** * Inserts vocabulary into the database by searching for already existing * entries; if found, the corresponding ID is returned. If not found, the * vocabulary is extended if "insertmissingvoc" is true; otherwise an * SQLException is thrown * * @param tableName * The name of the vocabulary table. * @param uri * The vocabulary adapting the URI to be inserted into the * vocabulary table. * @return The ID of an already existing vocabulary table with the given * uri. * @throws UnsupportedOperationException * If we are not allowed to insert a missing vocabulary. */ public VocabularyElement getOrInsertVocabularyElement(Session session, String vocabularyType, String vocabularyElement) throws SAXException { Class<?> c = vocClassMap.get(vocabularyType); Criteria c0 = session.createCriteria(c); c0.setCacheable(true); c0.add(Restrictions.eq("uri", vocabularyElement)); VocabularyElement ve; try { ve = (VocabularyElement) c0.uniqueResult(); } catch (ObjectNotFoundException e) { ve = null; } if (ve == null) { // the uri does not yet exist: insert it if allowed. According to // the specs, some vocabulary is not allowed to be extended; this is // currently ignored here if (!insertMissingVoc) { throw new UnsupportedOperationException( "Not allowed to add new vocabulary - use existing vocabulary"); } else { // VocabularyElement subclasses should always have public // zero-arg constructor to avoid problems here try { ve = (VocabularyElement) c.newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } ve.setUri(vocabularyElement); session.save(ve); session.flush(); } } return ve; } /** * TODO: javadoc! * * @param textContent * @return * @throws InvalidFormatException */ protected 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); } } /** * TODO: javadoc! * * @param textContent * @return * @throws InvalidFormatException */ private boolean checkUri(String textContent) throws InvalidFormatException { try { new URI(textContent); } catch (URISyntaxException e) { throw new InvalidFormatException(e.getMessage(), e); } return true; } /** * Check EPC according to 'pure identity' URI as specified in Tag Data * Standard. * * @param textContent * @throws InvalidFormatException */ protected 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 + "'"); } } public SessionFactory getSessionFactory() { return sessionFactory; } public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public boolean isDbResetAllowed() { return dbResetAllowed; } public void setDbResetAllowed(boolean dbResetAllowed) { this.dbResetAllowed = dbResetAllowed; } public void setDbResetScript(String dbResetScript) { URL url = this.getClass().getResource(dbResetScript); this.dbResetScript = new File(url.getFile()); } public boolean isInsertMissingVoc() { return insertMissingVoc; } public void setInsertMissingVoc(boolean insertMissingVoc) { this.insertMissingVoc = insertMissingVoc; } public Schema getSchema() { return schema; } public void setSchema(Schema schema) { this.schema = schema; } public void setEpcisSchemaFile(String epcisSchemaFile) { Schema schema = initEpcisSchema(epcisSchemaFile); setSchema(schema); } }