11package de .rwth .idsg .steve .ocpp .ws ;
22
3- import com .google .common .base .Strings ;
43import com .google .common .collect .ImmutableMap ;
4+ import com .google .common .util .concurrent .Striped ;
55import de .rwth .idsg .steve .SteveException ;
66import de .rwth .idsg .steve .ocpp .ws .custom .WsSessionSelectStrategy ;
77import de .rwth .idsg .steve .ocpp .ws .data .SessionContext ;
1717import java .util .NoSuchElementException ;
1818import java .util .concurrent .ConcurrentHashMap ;
1919import java .util .concurrent .ScheduledFuture ;
20+ import java .util .concurrent .locks .Lock ;
2021
2122/**
2223 * @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
@@ -31,6 +32,8 @@ public class SessionContextStoreImpl implements SessionContextStore {
3132 */
3233 private final ConcurrentHashMap <String , Deque <SessionContext >> lookupTable = new ConcurrentHashMap <>();
3334
35+ private final Striped <Lock > locks = Striped .lock (16 );
36+
3437 private final WsSessionSelectStrategy wsSessionSelectStrategy ;
3538
3639 public SessionContextStoreImpl (WsSessionSelectStrategy wsSessionSelectStrategy ) {
@@ -39,48 +42,77 @@ public SessionContextStoreImpl(WsSessionSelectStrategy wsSessionSelectStrategy)
3942
4043 @ Override
4144 public void add (String chargeBoxId , WebSocketSession session , ScheduledFuture pingSchedule ) {
42- SessionContext context = new SessionContext (session , pingSchedule , DateTime .now ());
45+ Lock l = locks .get (chargeBoxId );
46+ l .lock ();
47+ try {
48+ SessionContext context = new SessionContext (session , pingSchedule , DateTime .now ());
4349
44- Deque <SessionContext > endpointDeque = lookupTable .computeIfAbsent (chargeBoxId , str -> new ArrayDeque <>());
45- endpointDeque .addLast (context ); // Adding at the end
50+ Deque <SessionContext > endpointDeque = lookupTable .computeIfAbsent (chargeBoxId , str -> new ArrayDeque <>());
51+ endpointDeque .addLast (context ); // Adding at the end
4652
47- log .debug ("A new SessionContext is stored for chargeBoxId '{}'. Store size: {}" ,
48- chargeBoxId , endpointDeque .size ());
53+ log .debug ("A new SessionContext is stored for chargeBoxId '{}'. Store size: {}" ,
54+ chargeBoxId , endpointDeque .size ());
55+ } finally {
56+ l .unlock ();
57+ }
4958 }
5059
5160 @ Override
5261 public void remove (String chargeBoxId , WebSocketSession session ) {
53- Deque <SessionContext > endpointDeque = lookupTable .get (chargeBoxId );
54- if (endpointDeque == null ) {
55- log .debug ("No session context to remove for chargeBoxId '{}'" , chargeBoxId );
56- return ;
57- }
62+ Lock l = locks .get (chargeBoxId );
63+ l .lock ();
64+ try {
65+ Deque <SessionContext > endpointDeque = lookupTable .get (chargeBoxId );
66+ if (endpointDeque == null ) {
67+ log .debug ("No session context to remove for chargeBoxId '{}'" , chargeBoxId );
68+ return ;
69+ }
5870
59- // Prevent "java.util.ConcurrentModificationException: null"
60- // Reason: Cannot modify the set (remove the item) we are iterating
61- // Solution: Iterate the set, find the item, remove the item after the for-loop
62- //
63- SessionContext toRemove = null ;
64- for (SessionContext context : endpointDeque ) {
65- if (context .getSession ().getId ().equals (session .getId ())) {
66- toRemove = context ;
67- break ;
71+ // Prevent "java.util.ConcurrentModificationException: null"
72+ // Reason: Cannot modify the set (remove the item) we are iterating
73+ // Solution: Iterate the set, find the item, remove the item after the for-loop
74+ //
75+ SessionContext toRemove = null ;
76+ for (SessionContext context : endpointDeque ) {
77+ if (context .getSession ().getId ().equals (session .getId ())) {
78+ toRemove = context ;
79+ break ;
80+ }
6881 }
69- }
7082
71- if (toRemove != null ) {
72- // 1. Cancel the ping task
73- toRemove .getPingSchedule ().cancel (true );
74- // 2. Delete from collection
75- if (endpointDeque .remove (toRemove )) {
76- log .debug ("A SessionContext is removed for chargeBoxId '{}'. Store size: {}" ,
77- chargeBoxId , endpointDeque .size ());
83+ if (toRemove != null ) {
84+ // 1. Cancel the ping task
85+ toRemove .getPingSchedule ().cancel (true );
86+ // 2. Delete from collection
87+ if (endpointDeque .remove (toRemove )) {
88+ log .debug ("A SessionContext is removed for chargeBoxId '{}'. Store size: {}" ,
89+ chargeBoxId , endpointDeque .size ());
90+ }
91+ // 3. Delete empty collection from lookup table in order to correctly calculate
92+ // the number of connected chargeboxes with getNumberOfChargeBoxes()
93+ if (endpointDeque .size () == 0 ) {
94+ lookupTable .remove (chargeBoxId );
95+ }
7896 }
79- // 3. Delete empty collection from lookup table in order to correctly calculate
80- // the number of connected chargeboxes with getNumberOfChargeBoxes()
81- if (endpointDeque .size () == 0 ) {
82- lookupTable .remove (chargeBoxId );
97+ } finally {
98+ l .unlock ();
99+ }
100+ }
101+
102+ @ Override
103+ public WebSocketSession getSession (String chargeBoxId ) {
104+ Lock l = locks .get (chargeBoxId );
105+ l .lock ();
106+ try {
107+ Deque <SessionContext > endpointDeque = lookupTable .get (chargeBoxId );
108+ if (endpointDeque == null ) {
109+ throw new NoSuchElementException ();
83110 }
111+ return wsSessionSelectStrategy .getSession (endpointDeque );
112+ } catch (NoSuchElementException e ) {
113+ throw new SteveException ("No session context for chargeBoxId '%s'" , chargeBoxId , e );
114+ } finally {
115+ l .unlock ();
84116 }
85117 }
86118
@@ -94,6 +126,11 @@ public int getSize(String chargeBoxId) {
94126 }
95127 }
96128
129+ @ Override
130+ public int getNumberOfChargeBoxes () {
131+ return lookupTable .size ();
132+ }
133+
97134 @ Override
98135 public List <String > getChargeBoxIdList () {
99136 return Collections .list (lookupTable .keys ());
@@ -103,27 +140,4 @@ public List<String> getChargeBoxIdList() {
103140 public Map <String , Deque <SessionContext >> getACopy () {
104141 return ImmutableMap .copyOf (lookupTable );
105142 }
106-
107- @ Override
108- public int getNumberOfChargeBoxes () {
109- return lookupTable .size ();
110- }
111-
112- @ Override
113- public WebSocketSession getSession (String chargeBoxId ) {
114- if (Strings .isNullOrEmpty (chargeBoxId )) {
115- throw new SteveException ("Invalid chargeBoxId (null or empty)" );
116- }
117-
118- try {
119- Deque <SessionContext > endpointDeque = lookupTable .get (chargeBoxId );
120- if (endpointDeque == null ) {
121- throw new NoSuchElementException ();
122- }
123- return wsSessionSelectStrategy .getSession (endpointDeque );
124- } catch (NoSuchElementException e ) {
125- throw new SteveException ("No session context for chargeBoxId '%s'" , chargeBoxId , e );
126- }
127- }
128-
129143}
0 commit comments