Improved the performance of RISE V2G by converting the MessageHandler into a Singleton. When instantiating the MessageHandler, the JAXB Context is set which is a pretty time-consuming task. Before the improvement, the MessageHandler was instantiated by the V2GCommunicationSessionHandlerSECC and the V2GCommunicationSessionHandlerSECC class.
Additionally, the MessageHandler was instantiated each time a new communication session was initiated by the EVCC. Now, MessageHandler is initialized only once at startup of RISE V2G, saving a few seconds processing time on a slow embedded controller. Credit goes to Advantics for pointing this out! Thanks. :) Further changes: minor editorial edits and an additional logging message in the ConnectionHandler stating the length of the V2GTP payload as stated by the V2GTP header (helps for debugging purposes)
This commit is contained in:
parent
d31a6446ca
commit
6f6be89a09
|
@ -125,4 +125,4 @@ signature.verification.showlog = true
|
|||
# - exificient
|
||||
# - open_exi
|
||||
# If no correct value is provided here, 'exificient' will be used
|
||||
exi.codec = open_exi
|
||||
exi.codec = exificient
|
||||
|
|
|
@ -65,7 +65,7 @@ public class V2GCommunicationSessionHandlerEVCC implements Observer {
|
|||
private StatefulTransportLayerClient transportLayerClient;
|
||||
|
||||
public V2GCommunicationSessionHandlerEVCC() {
|
||||
setMessageHandler(new MessageHandler());
|
||||
setMessageHandler(MessageHandler.getInstance());
|
||||
|
||||
setSecurity(
|
||||
(MiscUtils.getPropertyValue("tls") != null ?
|
||||
|
|
|
@ -144,9 +144,9 @@ public class DummyBackendInterface implements IBackendInterface {
|
|||
salesTariff.setId("ID1");
|
||||
salesTariff.setSalesTariffID((short) 1);
|
||||
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(0L, (short) 1));
|
||||
// salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(1800L, (short) 4));
|
||||
// salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(3600L, (short) 2));
|
||||
// salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(5400L, (short) 3));
|
||||
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(1800L, (short) 4));
|
||||
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(3600L, (short) 2));
|
||||
salesTariff.getSalesTariffEntry().add(createSalesTariffEntry(5400L, (short) 3));
|
||||
|
||||
// Put 'em all together
|
||||
SAScheduleTupleType saScheduleTuple = new SAScheduleTupleType();
|
||||
|
|
|
@ -53,7 +53,7 @@ public class V2GCommunicationSessionHandlerSECC implements Observer {
|
|||
* Keeps a list of all ConnectionHandlers and their respective running Threads.
|
||||
* The V2GCommunicationSessionHandlerSECC needs a ConnectionHandler (with its TCP/TLS client socket)
|
||||
* in order to associate it with a V2GCommunicationSessionSECC. Handing over a Thread instead brings
|
||||
* up the problem that you can't access the Threads runnable object (ConnectionHandler).
|
||||
* up the problem that you can't access the Thread's runnable object (ConnectionHandler).
|
||||
*/
|
||||
private static HashMap<ConnectionHandler, Thread> connectionHandlerMap;
|
||||
private MessageHandler messageHandler;
|
||||
|
|
|
@ -183,7 +183,7 @@ public class V2GCommunicationSessionSECC extends V2GCommunicationSession impleme
|
|||
|
||||
processReaction(getCurrentState().processIncomingMessage(incomingMessage));
|
||||
} else {
|
||||
terminateSession("Received incoming message is not a valid V2GTPMessage", false);
|
||||
getLogger().warn("Received incoming message is not a valid V2GTPMessage", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,9 +202,9 @@ public class V2GCommunicationSessionSECC extends V2GCommunicationSession impleme
|
|||
((ChangeProcessingState) reactionToIncomingMessage).getPayload()));
|
||||
} else if (reactionToIncomingMessage instanceof TerminateSession) {
|
||||
/*
|
||||
* TODO is this really needed? if sth. goes wrong, a negative response code will be send by
|
||||
* TODO is this really needed? if sth. goes wrong, a negative response code will be sent by
|
||||
* the respective state anyway, the reaction to this negative response code should only
|
||||
* instantiate a TerminateSession object!
|
||||
* instantiate a TerminateSession object.
|
||||
*/
|
||||
terminateSession(((TerminateSession) reactionToIncomingMessage));
|
||||
} else {
|
||||
|
|
|
@ -121,7 +121,8 @@ public class ConnectionHandler extends Observable implements Runnable {
|
|||
break;
|
||||
} else {
|
||||
setPayloadLength(ByteUtils.toIntFromByteArray(Arrays.copyOfRange(getV2gTpHeader(), 4, 8)));
|
||||
setV2gTPPayload(new byte[payloadLength]);
|
||||
getLogger().debug("Length of V2GTP payload in bytes according to V2GTP header: " + getPayloadLength());
|
||||
setV2gTPPayload(new byte[getPayloadLength()]);
|
||||
|
||||
getInStream().read(getV2gTPPayload());
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ import com.v2gclarity.risev2g.shared.enumerations.GlobalValues;
|
|||
import com.v2gclarity.risev2g.shared.exiCodec.EXIficientCodec;
|
||||
import com.v2gclarity.risev2g.shared.exiCodec.ExiCodec;
|
||||
import com.v2gclarity.risev2g.shared.exiCodec.OpenEXICodec;
|
||||
import com.v2gclarity.risev2g.shared.misc.V2GCommunicationSession;
|
||||
import com.v2gclarity.risev2g.shared.misc.V2GTPMessage;
|
||||
import com.v2gclarity.risev2g.shared.utils.ByteUtils;
|
||||
import com.v2gclarity.risev2g.shared.utils.MiscUtils;
|
||||
|
@ -57,25 +56,24 @@ import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.SignedInfoType;
|
|||
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
|
||||
|
||||
|
||||
public class MessageHandler {
|
||||
public final class MessageHandler {
|
||||
// -- BEGIN: SINGLETON DEFINITION --
|
||||
/*
|
||||
* Eager instantiation of the singleton, since a MessageHandler is always needed.
|
||||
* The JVM creates the unique instance when the class is loaded and before any thread tries to
|
||||
* access the instance variable -> thread safe.
|
||||
*/
|
||||
private static final MessageHandler instance = new MessageHandler();
|
||||
|
||||
public static MessageHandler getInstance() {
|
||||
return instance;
|
||||
}
|
||||
// -- END: SINGLETON DEFINITION --
|
||||
|
||||
private Logger logger = LogManager.getLogger(this.getClass().getSimpleName());
|
||||
private ExiCodec exiCodec;
|
||||
private V2GCommunicationSession commSessionContext;
|
||||
private JAXBContext jaxbContext;
|
||||
|
||||
/**
|
||||
* This constructor is used by V2GCommunicationSessionEVCC and -SECC
|
||||
*
|
||||
* @param commSessionContext The respective V2GCommunicationSessionEVCC or -SECC instance
|
||||
*/
|
||||
public MessageHandler(V2GCommunicationSession commSessionContext) {
|
||||
this();
|
||||
setCommSessionContext(commSessionContext);
|
||||
|
||||
// Setting the JAXBContext is a very time-consuming action and should only be done once during startup
|
||||
setJaxbContext(SupportedAppProtocolReq.class, SupportedAppProtocolRes.class, V2GMessage.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is used by V2GCommunicationSessionHandlerEVCC and -SECC
|
||||
|
@ -91,7 +89,7 @@ public class MessageHandler {
|
|||
setJaxbContext(SupportedAppProtocolReq.class, SupportedAppProtocolRes.class, V2GMessage.class);
|
||||
}
|
||||
|
||||
public boolean isV2GTPMessageValid(V2GTPMessage v2gTpMessage) {
|
||||
public synchronized boolean isV2GTPMessageValid(V2GTPMessage v2gTpMessage) {
|
||||
if (isVersionAndInversionFieldCorrect(v2gTpMessage) &&
|
||||
isPayloadTypeCorrect(v2gTpMessage) &&
|
||||
isPayloadLengthCorrect(v2gTpMessage))
|
||||
|
@ -99,7 +97,7 @@ public class MessageHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isVersionAndInversionFieldCorrect(V2GTPMessage v2gTpMessage) {
|
||||
public synchronized boolean isVersionAndInversionFieldCorrect(V2GTPMessage v2gTpMessage) {
|
||||
if (v2gTpMessage.getProtocolVersion() != GlobalValues.V2GTP_VERSION_1_IS.getByteValue()) {
|
||||
getLogger().error("Protocol version (" + ByteUtils.toStringFromByte(v2gTpMessage.getProtocolVersion()) +
|
||||
") is not supported!");
|
||||
|
@ -115,7 +113,7 @@ public class MessageHandler {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean isPayloadTypeCorrect(V2GTPMessage v2gTpMessage) {
|
||||
public synchronized boolean isPayloadTypeCorrect(V2GTPMessage v2gTpMessage) {
|
||||
byte[] payloadType = v2gTpMessage.getPayloadType();
|
||||
|
||||
if (Arrays.equals(payloadType, GlobalValues.V2GTP_PAYLOAD_TYPE_EXI_ENCODED_V2G_MESSAGE.getByteArrayValue()) ||
|
||||
|
@ -127,7 +125,7 @@ public class MessageHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isPayloadLengthCorrect(V2GTPMessage v2gTpMessage) {
|
||||
public synchronized boolean isPayloadLengthCorrect(V2GTPMessage v2gTpMessage) {
|
||||
if (ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) > GlobalValues.V2GTP_HEADER_MAX_PAYLOAD_LENGTH.getLongValue() ||
|
||||
ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) < 0L) {
|
||||
getLogger().error("Payload length (" + ByteUtils.toLongFromByteArray(v2gTpMessage.getPayloadLength()) +
|
||||
|
@ -168,15 +166,17 @@ public class MessageHandler {
|
|||
}
|
||||
|
||||
|
||||
public V2GMessage getV2GMessage(
|
||||
public synchronized V2GMessage getV2GMessage(
|
||||
byte[] sessionID,
|
||||
HashMap<String, byte[]> xmlSignatureRefElements,
|
||||
ECPrivateKey signaturePrivateKey,
|
||||
JAXBElement<? extends BodyBaseType> v2gMessageInstance) {
|
||||
return getV2GMessage(null, xmlSignatureRefElements, signaturePrivateKey, v2gMessageInstance);
|
||||
return getV2GMessage(sessionID, null, xmlSignatureRefElements, signaturePrivateKey, v2gMessageInstance);
|
||||
}
|
||||
|
||||
|
||||
public V2GMessage getV2GMessage(
|
||||
public synchronized V2GMessage getV2GMessage(
|
||||
byte[] sessionID,
|
||||
NotificationType notification,
|
||||
HashMap<String, byte[]> xmlSignatureRefElements,
|
||||
ECPrivateKey signaturePrivateKey,
|
||||
|
@ -185,20 +185,21 @@ public class MessageHandler {
|
|||
body.setBodyElement(v2gMessageInstance);
|
||||
|
||||
V2GMessage v2gMessage = new V2GMessage();
|
||||
v2gMessage.setHeader(getHeader(notification, v2gMessageInstance, xmlSignatureRefElements, signaturePrivateKey));
|
||||
v2gMessage.setHeader(getHeader(sessionID, notification, v2gMessageInstance, xmlSignatureRefElements, signaturePrivateKey));
|
||||
v2gMessage.setBody(body);
|
||||
|
||||
return v2gMessage;
|
||||
}
|
||||
|
||||
|
||||
private MessageHeaderType getHeader(
|
||||
private synchronized MessageHeaderType getHeader(
|
||||
byte[] sessionID,
|
||||
NotificationType notification,
|
||||
JAXBElement<? extends BodyBaseType> v2gMessageInstance,
|
||||
HashMap<String, byte[]> xmlSignatureRefElements,
|
||||
ECPrivateKey signaturePrivateKey) {
|
||||
MessageHeaderType header = new MessageHeaderType();
|
||||
header.setSessionID(getCommSessionContext().getSessionID());
|
||||
header.setSessionID(sessionID);
|
||||
header.setNotification(notification);
|
||||
|
||||
if (xmlSignatureRefElements != null && xmlSignatureRefElements.size() != 0) {
|
||||
|
@ -236,7 +237,7 @@ public class MessageHandler {
|
|||
* @return The JAXBElement of the provided message or field
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public JAXBElement getJaxbElement(Object messageOrField) {
|
||||
public synchronized JAXBElement getJaxbElement(Object messageOrField) {
|
||||
String messageName = messageOrField.getClass().getSimpleName().replace("Type", "");
|
||||
String namespace = "";
|
||||
JAXBElement jaxbElement = null;
|
||||
|
@ -292,14 +293,6 @@ public class MessageHandler {
|
|||
this.exiCodec = exiCodec;
|
||||
SecurityUtils.setExiCodec(exiCodec);
|
||||
}
|
||||
|
||||
public V2GCommunicationSession getCommSessionContext() {
|
||||
return commSessionContext;
|
||||
}
|
||||
|
||||
public void setCommSessionContext(V2GCommunicationSession commSessionContext) {
|
||||
this.commSessionContext = commSessionContext;
|
||||
}
|
||||
|
||||
public JAXBContext getJaxbContext() {
|
||||
return jaxbContext;
|
||||
|
@ -309,7 +302,7 @@ public class MessageHandler {
|
|||
this.jaxbContext = jaxbContext;
|
||||
}
|
||||
|
||||
public void setJaxbContext(Class... classesToBeBound) {
|
||||
public synchronized void setJaxbContext(Class... classesToBeBound) {
|
||||
try {
|
||||
setJaxbContext(JAXBContext.newInstance(classesToBeBound));
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ public abstract class State {
|
|||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
V2GMessage v2gMessage = getMessageHandler().getV2GMessage(
|
||||
getCommSessionContext().getSessionID(),
|
||||
getXMLSignatureRefElements(),
|
||||
getSignaturePrivateKey(),
|
||||
getCommSessionContext().getMessageHandler().getJaxbElement(message)
|
||||
|
|
|
@ -56,7 +56,7 @@ public abstract class V2GCommunicationSession extends Observable {
|
|||
|
||||
public V2GCommunicationSession() {
|
||||
setStates(new HashMap<V2GMessages, State>());
|
||||
setMessageHandler(new MessageHandler(this));
|
||||
setMessageHandler(MessageHandler.getInstance());
|
||||
setSessionID(null);
|
||||
setV2gTpMessage(null);
|
||||
}
|
||||
|
|
|
@ -808,7 +808,7 @@ public final class SecurityUtils {
|
|||
try {
|
||||
pkcs8ByteArray = Files.readAllBytes(fileLocation);
|
||||
|
||||
// The DER encoded private key is encrypted in PKCS#8. So we need to decrypt it first
|
||||
// The DER encoded private key is password-based encrypted and provided in PKCS#8. So we need to decrypt it first
|
||||
PBEKeySpec pbeKeySpec = new PBEKeySpec(GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString().toCharArray());
|
||||
EncryptedPrivateKeyInfo encryptedPrivKeyInfo = new EncryptedPrivateKeyInfo(pkcs8ByteArray);
|
||||
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(encryptedPrivKeyInfo.getAlgName());
|
||||
|
|
Loading…
Reference in New Issue