Prechádzať zdrojové kódy

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)

Marc Mültin 8 rokov pred
rodič
commit
6f6be89a09

+ 1 - 1
RISE-V2G-EVCC/EVCCConfig.properties

@@ -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

+ 1 - 1
RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/session/V2GCommunicationSessionHandlerEVCC.java

@@ -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 ? 

+ 3 - 3
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/backend/DummyBackendInterface.java

@@ -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();

+ 1 - 1
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/session/V2GCommunicationSessionHandlerSECC.java

@@ -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;

+ 3 - 3
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/session/V2GCommunicationSessionSECC.java

@@ -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 {

+ 2 - 1
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/transportLayer/ConnectionHandler.java

@@ -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());
 	

+ 28 - 35
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/messageHandling/MessageHandler.java

@@ -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));
 			

+ 1 - 0
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/misc/State.java

@@ -95,6 +95,7 @@ public abstract class State {
 		
 		@SuppressWarnings({"unchecked"})
 		V2GMessage v2gMessage = getMessageHandler().getV2GMessage(
+				getCommSessionContext().getSessionID(),
 				getXMLSignatureRefElements(), 
 				getSignaturePrivateKey(),
 				getCommSessionContext().getMessageHandler().getJaxbElement(message)

+ 1 - 1
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/misc/V2GCommunicationSession.java

@@ -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);
 	}

+ 1 - 1
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/utils/SecurityUtils.java

@@ -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());