1010import static org .epics .pva .PVASettings .logger ;
1111
1212import java .util .BitSet ;
13- import java .util .Map .Entry ;
1413import java .util .concurrent .ConcurrentHashMap ;
1514import java .util .concurrent .ConcurrentHashMap .KeySetView ;
15+ import java .util .concurrent .atomic .AtomicBoolean ;
1616import java .util .concurrent .atomic .AtomicInteger ;
1717import java .util .logging .Level ;
1818
19+ import org .epics .pva .common .AccessRightsChange ;
1920import org .epics .pva .common .PVAHeader ;
2021import org .epics .pva .data .PVAString ;
2122import org .epics .pva .data .PVAStructure ;
@@ -74,9 +75,12 @@ public class ServerPV implements AutoCloseable
7475 /** Handler for write access. May be READONLY_WRITE_HANDLER */
7576 private final WriteEventHandler write_handler ;
7677
77- /** Map of TCP handlers,
78- * i.e. TCP connections to clients that access this PV,
79- * by client ID
78+ /** Is the PV writable? */
79+ private final AtomicBoolean writable ;
80+
81+ /** Map of TCP handlers and client IDs.
82+ * PV has one server ID.
83+ * Client ID is provided by client for each TCP connection.
8084 */
8185 private final ConcurrentHashMap <ServerTCPHandler , Integer > cid_by_client = new ConcurrentHashMap <>();
8286
@@ -96,6 +100,7 @@ public class ServerPV implements AutoCloseable
96100 this .data = data .cloneData ();
97101 rpc = DEFAULT_RPC_SERVICE ;
98102 this .write_handler = write_handler ;
103+ writable = new AtomicBoolean (write_handler != READONLY_WRITE_HANDLER );
99104 }
100105
101106 /** Create PV for handling RPC calls
@@ -110,6 +115,7 @@ public class ServerPV implements AutoCloseable
110115 this .data = RPC_SERVICE_VALUE ;
111116 this .rpc = rpc ;
112117 write_handler = READONLY_WRITE_HANDLER ;
118+ writable = new AtomicBoolean (false );
113119 }
114120
115121
@@ -131,7 +137,7 @@ public int getSID()
131137 */
132138 void addClient (final ServerTCPHandler tcp , final int cid )
133139 {
134- // A client should create a PV just once.
140+ // Each client should create a PV just once.
135141 // If client creates PV several times, we only track the last CID
136142 // and issue a warning.
137143 final Integer other = cid_by_client .put (tcp , cid );
@@ -226,9 +232,26 @@ PVAStructure getData()
226232 }
227233 }
228234
229- boolean isWritable ()
235+ /** @return Is the PV writable? */
236+ public boolean isWritable ()
237+ {
238+ return writable .get ();
239+ }
240+
241+ /** Update write access
242+ *
243+ * To enable write access, PV must have been created with {@link WriteEventHandler}
244+ *
245+ * @param writable Should the PV be writable?
246+ */
247+ public void setWritable (final boolean writable )
230248 {
231- return write_handler != READONLY_WRITE_HANDLER ;
249+ if (write_handler != READONLY_WRITE_HANDLER && this .writable .compareAndSet (!writable , writable ))
250+ {
251+ logger .log (Level .FINE , () -> "Update ACL " + this + (writable ? " to writable" : " to read-only" ));
252+ cid_by_client .forEach ((tcp , cid ) ->
253+ tcp .submit ((version , buffer ) -> AccessRightsChange .encode (buffer , cid , writable )));
254+ }
232255 }
233256
234257 /** Notification that a client wrote to the PV
@@ -256,18 +279,14 @@ PVAStructure call(final PVAStructure parameters) throws Exception
256279 @ Override
257280 public void close ()
258281 {
259- for (Entry <ServerTCPHandler , Integer > client : cid_by_client .entrySet ())
260- {
261- final ServerTCPHandler tcp = client .getKey ();
262- final int cid = client .getValue ();
282+ cid_by_client .forEach ((tcp , cid ) ->
263283 tcp .submit ( (version , buffer ) ->
264- {
284+ { // Send CMD_DESTROY_CHANNEL for this PV to all clients
265285 logger .log (Level .FINE , () -> "Sending destroy channel command for SID " + sid + ", CID " + cid );
266286 PVAHeader .encodeMessageHeader (buffer , PVAHeader .FLAG_SERVER , PVAHeader .CMD_DESTROY_CHANNEL , 4 +4 );
267287 buffer .putInt (sid );
268288 buffer .putInt (cid );
269- });
270- }
289+ }));
271290
272291 server .deletePV (this );
273292 }
0 commit comments