ExiCodec.java 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*******************************************************************************
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2015-207 V2G Clarity (Dr.-Ing. Marc Mültin)
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. *******************************************************************************/
  24. package com.v2gclarity.risev2g.shared.exiCodec;
  25. import java.io.ByteArrayInputStream;
  26. import java.io.ByteArrayOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.StringWriter;
  30. import java.util.Base64;
  31. import javax.xml.bind.JAXBElement;
  32. import javax.xml.bind.JAXBException;
  33. import javax.xml.bind.Marshaller;
  34. import javax.xml.bind.Unmarshaller;
  35. import org.apache.logging.log4j.LogManager;
  36. import org.apache.logging.log4j.Logger;
  37. import com.v2gclarity.risev2g.shared.enumerations.GlobalValues;
  38. import com.v2gclarity.risev2g.shared.utils.ByteUtils;
  39. import com.v2gclarity.risev2g.shared.utils.MiscUtils;
  40. import com.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolReq;
  41. import com.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolRes;
  42. import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
  43. public abstract class ExiCodec {
  44. private Logger logger = LogManager.getLogger(this.getClass().getSimpleName());
  45. private Marshaller marshaller;
  46. private Unmarshaller unmarshaller;
  47. private InputStream inStream;
  48. private Object decodedMessage;
  49. private String decodedExi;
  50. private boolean xmlMsgRepresentation;
  51. private boolean hexAndBase64MsgRepresentation;
  52. public ExiCodec() {
  53. // Check if XML representation of sent messages is to be shown (for debug purposes)
  54. if ((boolean) MiscUtils.getPropertyValue("exi.messages.showxml"))
  55. setXMLMsgRepresentation(true);
  56. else
  57. setXMLMsgRepresentation(false);
  58. // Check if hexadecimal and Base64 representation of sent messages is to be shown (for debug purposes)
  59. if ((boolean) MiscUtils.getPropertyValue("exi.messages.showhex"))
  60. setHexAndBase64MsgRepresentation(true);
  61. else
  62. setHexAndBase64MsgRepresentation(false);
  63. }
  64. public InputStream marshalToInputStream(Object jaxbObject) {
  65. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  66. try {
  67. if (getInStream() != null) getInStream().reset();
  68. getMarshaller().marshal(jaxbObject, baos);
  69. setInStream(new ByteArrayInputStream(baos.toByteArray()));
  70. baos.close();
  71. if (isXMLMsgRepresentation()) showXMLRepresentationOfMessage(jaxbObject);
  72. return getInStream();
  73. } catch (JAXBException | IOException e) {
  74. getLogger().error(e.getClass().getSimpleName() + " occurred while trying to marshal to InputStream from JAXBElement", e);
  75. return null;
  76. }
  77. }
  78. public Object unmarshallToMessage(String decodedExiString) {
  79. try {
  80. if (getInStream() != null) getInStream().reset();
  81. setInStream(new ByteArrayInputStream(decodedExiString.getBytes()));
  82. Object unmarhalledObject = getUnmarshaller().unmarshal(getInStream());
  83. if (isXMLMsgRepresentation()) showXMLRepresentationOfMessage(unmarhalledObject);
  84. return unmarhalledObject;
  85. } catch (IOException | JAXBException | RuntimeException e) {
  86. getLogger().error(e.getClass().getSimpleName() + " occurred while trying to unmarshall decoded message", e);
  87. return null;
  88. }
  89. }
  90. /**
  91. * Shows the XML representation of a marshalled or unmarshalled message object. This is useful for debugging
  92. * purposes.
  93. *
  94. * @param message The (un)marshalled message object
  95. */
  96. @SuppressWarnings("rawtypes")
  97. public void showXMLRepresentationOfMessage(Object message) {
  98. StringWriter sw = new StringWriter();
  99. String className = "";
  100. if (message instanceof V2GMessage) {
  101. className = ((V2GMessage) message).getBody().getBodyElement().getName().getLocalPart();
  102. } else if (message instanceof JAXBElement) {
  103. className = ((JAXBElement) message).getName().getLocalPart();
  104. } else if (message instanceof SupportedAppProtocolReq) {
  105. className = "SupportedAppProtocolReq";
  106. } else if (message instanceof SupportedAppProtocolRes) {
  107. className = "SupportedAppProtocolRes";
  108. } else {
  109. className = "marshalled JAXBElement";
  110. }
  111. try {
  112. getMarshaller().marshal(message, sw);
  113. getLogger().debug("XML representation of " + className + ":\n" + sw.toString());
  114. } catch (JAXBException e) {
  115. getLogger().error(e.getClass().getSimpleName() + " occurred while trying to show XML representation of " + className, e);
  116. }
  117. }
  118. @SuppressWarnings("rawtypes")
  119. public void showHexAndBase64RepresentationOfMessage(Object messageOrField, byte[] exiEncodedObject) {
  120. String className = "";
  121. if (messageOrField instanceof V2GMessage) {
  122. className = ((V2GMessage) messageOrField).getBody().getBodyElement().getName().getLocalPart();
  123. } else if (messageOrField instanceof JAXBElement) {
  124. className = ((JAXBElement) messageOrField).getName().getLocalPart();
  125. } else if (messageOrField instanceof SupportedAppProtocolReq) {
  126. className = "SupportedAppProtocolReq";
  127. } else if (messageOrField instanceof SupportedAppProtocolRes) {
  128. className = "SupportedAppProtocolRes";
  129. } else {
  130. className = " JAXBElement";
  131. }
  132. getLogger().debug("EXI encoded " + className + ": " + ByteUtils.toHexString(exiEncodedObject));
  133. getLogger().debug("Base64 encoded " + className + ": " + Base64.getEncoder().encodeToString(exiEncodedObject));
  134. }
  135. /**
  136. * Provides the EXI encoding of the header's SignedInfo element. The resulting byte array can then be used to
  137. * verify a signature.
  138. *
  139. * @param jaxbSignedInfo The SignedInfo element of the V2GMessage header, given as a JAXB element
  140. * @return The EXI encoding of the SignedInfo element given as a byte array
  141. */
  142. public byte[] getExiEncodedSignedInfo(JAXBElement jaxbSignedInfo) {
  143. // The schema-informed fragment grammar option needs to be used for EXI encodings in the header's signature
  144. setFragment(true);
  145. // The SignedInfo element must be encoded
  146. byte[] encodedSignedInfo = encodeEXI(
  147. jaxbSignedInfo,
  148. GlobalValues.SCHEMA_PATH_XMLDSIG.toString()
  149. );
  150. // Do not use the schema-informed fragment grammar option for other EXI encodings (message bodies)
  151. setFragment(false);
  152. return encodedSignedInfo;
  153. }
  154. public abstract byte[] encodeEXI(Object jaxbXML, String xsdSchemaPath);
  155. public abstract Object decodeEXI(byte[] exiEncodedMessage, boolean supportedAppProtocolHandshake);
  156. public abstract void setFragment(boolean useFragmentGrammar);
  157. public Marshaller getMarshaller() {
  158. return marshaller;
  159. }
  160. public void setMarshaller(Marshaller marshaller) {
  161. this.marshaller = marshaller;
  162. }
  163. public Unmarshaller getUnmarshaller() {
  164. return unmarshaller;
  165. }
  166. public void setUnmarshaller(Unmarshaller unmarshaller) {
  167. this.unmarshaller = unmarshaller;
  168. }
  169. public Logger getLogger() {
  170. return logger;
  171. }
  172. public Object getDecodedMessage() {
  173. return decodedMessage;
  174. }
  175. public void setDecodedMessage(Object decodedMessage) {
  176. this.decodedMessage = decodedMessage;
  177. }
  178. public String getDecodedExi() {
  179. return decodedExi;
  180. }
  181. public void setDecodedExi(String decodedExi) {
  182. this.decodedExi = decodedExi;
  183. }
  184. public InputStream getInStream() {
  185. return inStream;
  186. }
  187. public void setInStream(InputStream inStream) {
  188. this.inStream = inStream;
  189. }
  190. private void setXMLMsgRepresentation(boolean xmlMsgRepresentation) {
  191. this.xmlMsgRepresentation = xmlMsgRepresentation;
  192. }
  193. public boolean isXMLMsgRepresentation() {
  194. return xmlMsgRepresentation;
  195. }
  196. public boolean isHexAndBase64MsgRepresentation() {
  197. return hexAndBase64MsgRepresentation;
  198. }
  199. public void setHexAndBase64MsgRepresentation(boolean hexAndBase64MsgRepresentation) {
  200. this.hexAndBase64MsgRepresentation = hexAndBase64MsgRepresentation;
  201. }
  202. }