1717
1818import java .net .URI ;
1919import java .util .*;
20+ import java .util .function .Consumer ;
2021import org .eclipse .equinox .p2 .core .IAgentLocation ;
2122import org .eclipse .equinox .p2 .core .IProvisioningAgent ;
2223import org .eclipse .equinox .p2 .core .spi .IAgentService ;
3031 */
3132public class ProvisioningAgent implements IProvisioningAgent {
3233
33- private final Map <String , Object > agentServices = Collections .synchronizedMap (new HashMap <>());
34+ private final Map <String , Object > agentServices = Collections .synchronizedMap (new LinkedHashMap <>());
3435 private BundleContext context ;
3536 private volatile boolean stopped = false ;
3637 private ServiceRegistration <IProvisioningAgent > reg ;
3738 private final Map <String , ServiceTracker <IAgentServiceFactory , Object >> trackers = Collections
38- .synchronizedMap (new HashMap <>());
39+ .synchronizedMap (new LinkedHashMap <>());
3940
4041 /**
4142 * Instantiates a provisioning agent.
@@ -50,7 +51,6 @@ public ProvisioningAgent() {
5051 public Object getService (String serviceName ) {
5152 //synchronize so concurrent gets always obtain the same service
5253 synchronized (agentServices ) {
53- checkRunning ();
5454 Object service = agentServices .get (serviceName );
5555 if (service != null ) {
5656 return service ;
@@ -59,6 +59,9 @@ public Object getService(String serviceName) {
5959 ServiceTracker <IAgentServiceFactory , Object > tracker = trackers .computeIfAbsent (serviceName ,
6060 ref -> {
6161 try {
62+ if (stopped ) {
63+ return null ;
64+ }
6265 Filter filter = context .createFilter (
6366 String .format ("(&(%s=%s)(|(%s=%s)(p2.agent.servicename=%s)))" , //$NON-NLS-1$
6467 Constants .OBJECTCLASS , IAgentServiceFactory .class .getName (), //
@@ -69,6 +72,9 @@ public Object getService(String serviceName) {
6972 throw new AssertionError (e );
7073 }
7174 });
75+ if (tracker == null ) {
76+ return null ;
77+ }
7278 tracker .open ();
7379 return tracker .getService ();
7480 }
@@ -118,9 +124,6 @@ public void setLocation(URI location) {
118124
119125 @ Override
120126 public void unregisterService (String serviceName , Object service ) {
121- if (stopped ) {
122- return ;
123- }
124127 agentServices .remove (serviceName , service );
125128 if (service instanceof IAgentService ) {
126129 ((IAgentService ) service ).stop ();
@@ -129,29 +132,20 @@ public void unregisterService(String serviceName, Object service) {
129132
130133 @ Override
131134 public void stop () {
132- List <Object > toStop ;
133- synchronized (agentServices ) {
134- toStop = new ArrayList <>(agentServices .values ());
135- }
136- //give services a chance to do their own shutdown
137- for (Object service : toStop ) {
138- if (service instanceof IAgentService ) {
139- if (service != this ) {
140- ((IAgentService ) service ).stop ();
141- }
142- }
143- }
144135 stopped = true ;
145- //close all service trackers
146- synchronized (trackers ) {
147- for (ServiceTracker <IAgentServiceFactory , Object > t : trackers .values ()) {
148- t .close ();
136+ try {
137+ disposeInTurns (agentServices .entrySet (), entry -> {
138+ unregisterService (entry .getKey (), entry .getValue ());
139+ });
140+ } finally {
141+ try {
142+ disposeInTurns (trackers .values (), ServiceTracker ::close );
143+ } finally {
144+ if (reg != null ) {
145+ reg .unregister ();
146+ reg = null ;
147+ }
149148 }
150- trackers .clear ();
151- }
152- if (reg != null ) {
153- reg .unregister ();
154- reg = null ;
155149 }
156150 }
157151
@@ -182,10 +176,52 @@ public void removedService(ServiceReference<IAgentServiceFactory> reference, Obj
182176 if (service instanceof IAgentService agentService ) {
183177 agentService .stop ();
184178 }
185- context .ungetService (reference );
179+ if (service != null ) {
180+ context .ungetService (reference );
181+ }
186182 }
187183
188184 };
189185
186+ /**
187+ * Given a thread-safe collection, drain all elements one-by-one, disposing each
188+ * without holding locks and allowing concurrent modification form. Other
189+ * threads should not attempt to add elements to collection.<br>
190+ * Usage example:
191+ *
192+ * <pre>{@code
193+ * volatile boolean stopped = false;
194+ * ...
195+ * stopped = true; // prevent other threads from adding to the collection
196+ * disposeInTurns(agentServices.entrySet(), entry -> {
197+ * unregisterService(entry.getKey(), entry.getValue());
198+ * });
199+ * }</pre>
200+ *
201+ */
202+ @ SuppressWarnings ("unchecked" )
203+ private <T > void disposeInTurns (Collection <T > input , Consumer <T > dispose ) {
204+ RuntimeException error = null ;
205+ while (!input .isEmpty ()) {
206+ Object [] toStop = input .toArray (Object []::new );
207+ Collections .reverse (Arrays .asList (toStop )); // Remove in an inverse order of adding
208+ for (Object item : toStop ) {
209+ if (input .remove (item )) {
210+ try {
211+ dispose .accept ((T ) item );
212+ } catch (RuntimeException e ) {
213+ if (error == null ) {
214+ error = e ;
215+ } else {
216+ error .addSuppressed (e );
217+ }
218+ }
219+ }
220+ }
221+ }
222+ if (error != null ) {
223+ throw error ;
224+ }
225+ }
190226
191227}
0 commit comments