Skip to content

Commit 574fc3a

Browse files
committed
clear model cache when datasource state is changed to STATE_AVAILABLE
1 parent 1b5faa8 commit 574fc3a

File tree

7 files changed

+190
-71
lines changed

7 files changed

+190
-71
lines changed

visualvm/application/src/org/graalvm/visualvm/application/Application.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public abstract class Application extends DataSource implements Stateful {
5959
private String id;
6060
private Host host;
6161
private int state = STATE_AVAILABLE;
62+
private int modCount;
6263

6364

6465
/**
@@ -116,9 +117,16 @@ public final synchronized int getState() {
116117
return state;
117118
}
118119

120+
public final int getModCount() {
121+
return modCount;
122+
}
123+
119124
protected final synchronized void setState(final int newState) {
120125
final int oldState = state;
121126
state = newState;
127+
if (oldState != newState && newState == STATE_AVAILABLE) {
128+
modCount++;
129+
}
122130
if (DataSource.EVENT_QUEUE.isRequestProcessorThread())
123131
getChangeSupport().firePropertyChange(PROPERTY_STATE, oldState, newState);
124132
else DataSource.EVENT_QUEUE.post(new Runnable() {

visualvm/core/src/org/graalvm/visualvm/core/datasupport/Stateful.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public interface Stateful {
6060
*/
6161
public int getState();
6262

63+
public int getModCount();
6364

6465
/**
6566
* Add a PropertyChangeListener to the listener list.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.visualvm.core.model;
26+
27+
import java.beans.PropertyChangeEvent;
28+
import java.beans.PropertyChangeListener;
29+
import java.lang.ref.Reference;
30+
import java.lang.ref.SoftReference;
31+
import java.lang.ref.WeakReference;
32+
import java.util.Collections;
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
import java.util.logging.Logger;
36+
import org.graalvm.visualvm.core.datasource.DataSource;
37+
import org.graalvm.visualvm.core.datasupport.Stateful;
38+
39+
/**
40+
*
41+
* @author Tomas Hurka
42+
*/
43+
final class ModelCache<D extends DataSource, M extends Model> {
44+
45+
private final static Logger LOGGER = Logger.getLogger(ModelFactory.class.getName());
46+
47+
/**
48+
* special marker for null model
49+
*/
50+
final ModelReference<M> NULL_MODEL;
51+
private final Map<DataSourceKey<D>, ModelReference<M>> modelCache;
52+
53+
ModelCache() {
54+
modelCache = Collections.synchronizedMap(new HashMap());
55+
NULL_MODEL = new ModelReference(null, 0);
56+
}
57+
58+
Reference<M> get(DataSourceKey<D> key) {
59+
ModelReference<M> valueRef = modelCache.get(key);
60+
if (valueRef != null && valueRef.modCount != key.modCount) {
61+
Reference<M> removed = modelCache.remove(key);
62+
LOGGER.finer("Invalid mod count " + key + " " + (removed != null ? "removed" : "not removed"));
63+
return null;
64+
}
65+
return valueRef;
66+
}
67+
68+
Reference<M> put(DataSourceKey<D> key, M value) {
69+
ModelReference<M> ref;
70+
DataSource ds = key.weakReference.get();
71+
if (ds instanceof Stateful && value != null) {
72+
ds.addPropertyChangeListener(Stateful.PROPERTY_STATE, new StateListener(key));
73+
LOGGER.finer("Registered listener for " + key + " val " + value.getClass());
74+
}
75+
if (value == null) {
76+
ref = NULL_MODEL;
77+
} else {
78+
ref = new ModelReference(value, key.modCount);
79+
}
80+
return modelCache.put(key, ref);
81+
}
82+
83+
void clear() {
84+
modelCache.clear();
85+
}
86+
87+
/**
88+
* DataSource wrapper object, which weakly reference datasource and uses
89+
* reference-equality of DataSources when implementing hashCode and equals
90+
* this class is used as keys in modelCache
91+
*/
92+
static class DataSourceKey<D extends DataSource> {
93+
94+
Reference<D> weakReference;
95+
int modCount;
96+
97+
DataSourceKey(D ds) {
98+
weakReference = new WeakReference(ds);
99+
if (ds instanceof Stateful) {
100+
modCount = ((Stateful) ds).getModCount();
101+
}
102+
}
103+
104+
public int hashCode() {
105+
D ds = weakReference.get();
106+
if (ds != null) {
107+
return ds.hashCode();
108+
}
109+
return 0;
110+
}
111+
112+
public boolean equals(Object obj) {
113+
if (obj == null) {
114+
return false;
115+
}
116+
if (obj instanceof DataSourceKey) {
117+
D ds = weakReference.get();
118+
D otherDs = ((DataSourceKey<D>) obj).weakReference.get();
119+
120+
return ds != null && ds == otherDs;
121+
}
122+
throw new IllegalArgumentException(obj.getClass().getName());
123+
}
124+
125+
public String toString() {
126+
DataSource ds = weakReference.get();
127+
return "DataSourceKey for " + System.identityHashCode(this) + " for " + ds == null ? "NULL" : ds.toString(); // NOI18N
128+
}
129+
}
130+
131+
private static class ModelReference<T> extends SoftReference<T> {
132+
133+
private int modCount;
134+
135+
private ModelReference(T ref, int count) {
136+
super(ref);
137+
modCount = count;
138+
}
139+
}
140+
141+
private class StateListener implements PropertyChangeListener {
142+
143+
DataSourceKey<D> key;
144+
145+
StateListener(DataSourceKey<D> k) {
146+
key = k;
147+
}
148+
149+
@Override
150+
public void propertyChange(PropertyChangeEvent evt) {
151+
int newState = (Integer) evt.getNewValue();
152+
if (newState == Stateful.STATE_AVAILABLE) {
153+
DataSource ds = key.weakReference.get();
154+
if (ds != null) {
155+
ds.removePropertyChangeListener(Stateful.PROPERTY_STATE, this);
156+
}
157+
Reference<M> removed = modelCache.remove(key);
158+
LOGGER.finer(key + " " + (removed != null ? "removed" : "not removed"));
159+
}
160+
}
161+
}
162+
}

visualvm/core/src/org/graalvm/visualvm/core/model/ModelFactory.java

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,15 @@
3030
import org.graalvm.visualvm.core.datasupport.DataChangeListener;
3131
import org.graalvm.visualvm.core.datasupport.DataChangeSupport;
3232
import java.lang.ref.Reference;
33-
import java.lang.ref.SoftReference;
34-
import java.lang.ref.WeakReference;
3533
import java.util.Collections;
3634
import java.util.Comparator;
37-
import java.util.HashMap;
38-
import java.util.Map;
3935
import java.util.SortedSet;
4036
import java.util.TreeSet;
4137
import java.util.concurrent.locks.Lock;
4238
import java.util.concurrent.locks.ReadWriteLock;
4339
import java.util.concurrent.locks.ReentrantReadWriteLock;
4440
import java.util.logging.Logger;
41+
import org.graalvm.visualvm.core.model.ModelCache.DataSourceKey;
4542

4643
/**
4744
* This is abstract factory class for getting model
@@ -58,21 +55,18 @@
5855
public abstract class ModelFactory<M extends Model,D extends DataSource> {
5956
final protected static Logger LOGGER = Logger.getLogger(ModelFactory.class.getName());
6057

61-
/** special marker for null model */
62-
private final Reference<M> NULL_MODEL;
6358
/** set of registered providers */
6459
private SortedSet<ModelProvider<M, D>> providers;
6560
/** providers cannot be changed, when getModel() is running */
6661
private ReadWriteLock providersLock;
6762
/** model cache */
68-
private Map<DataSourceKey<D>,Reference<M>> modelCache;
63+
private ModelCache<D,M> modelCache;
6964
/** asynchronous change support */
7065
private DataChangeSupport<ModelProvider<M, D>> factoryChange;
7166

7267
protected ModelFactory() {
73-
NULL_MODEL = new SoftReference(null);
7468
providers = new TreeSet(new ModelProviderComparator());
75-
modelCache = Collections.synchronizedMap(new HashMap());
69+
modelCache = new ModelCache();
7670
factoryChange = new DataChangeSupport();
7771
providersLock = new ReentrantReadWriteLock();
7872
}
@@ -98,7 +92,7 @@ public final M getModel(D dataSource) {
9892
M model = null;
9993

10094
if (modelRef != null) {
101-
if (modelRef == NULL_MODEL) { // cached null model, return null
95+
if (modelRef == modelCache.NULL_MODEL) { // cached null model, return null
10296
return null;
10397
}
10498
model = modelRef.get(); // if model is in cache return it,
@@ -110,12 +104,12 @@ public final M getModel(D dataSource) {
110104
for (ModelProvider<M, D> factory : providers) {
111105
model = factory.createModelFor(dataSource);
112106
if (model != null) { // we have model, put it into cache
113-
modelCache.put(key,new SoftReference(model));
107+
modelCache.put(key,model);
114108
break;
115109
}
116110
}
117111
if (model == null) { // model was not found - cache null model
118-
modelCache.put(key,NULL_MODEL);
112+
modelCache.put(key,null);
119113
}
120114
return model;
121115
}
@@ -138,7 +132,7 @@ public final boolean registerProvider(ModelProvider<M, D> newProvider) {
138132
LOGGER.finer("Registering " + newProvider.getClass().getName()); // NOI18N
139133
boolean added = providers.add(newProvider);
140134
if (added) {
141-
clearCache();
135+
modelCache.clear();
142136
factoryChange.fireChange(providers,Collections.singleton(newProvider),null);
143137
}
144138
return added;
@@ -160,7 +154,7 @@ public final boolean unregisterProvider(ModelProvider<M, D> oldProvider) {
160154
LOGGER.finer("Unregistering " + oldProvider.getClass().getName()); // NOI18N
161155
boolean removed = providers.remove(oldProvider);
162156
if (removed) {
163-
clearCache();
157+
modelCache.clear();
164158
factoryChange.fireChange(providers,null,Collections.singleton(oldProvider));
165159
}
166160
return removed;
@@ -169,10 +163,6 @@ public final boolean unregisterProvider(ModelProvider<M, D> oldProvider) {
169163
}
170164
}
171165

172-
public final void clearModel(D dataSource) {
173-
modelCache.remove(new DataSourceKey(dataSource));
174-
}
175-
176166
/**
177167
* Add data change listener. Data change is fired when
178168
* {@link ModelProvider} is registered/unregister.
@@ -202,10 +192,6 @@ public int priority() {
202192
return -1;
203193
}
204194

205-
private void clearCache() {
206-
modelCache.clear();
207-
}
208-
209195
/** compare ModelProvider-s using priority. Providers with higher priority
210196
* gets precedence over those with lower priority
211197
*/
@@ -225,41 +211,4 @@ public int compare(ModelProvider<M, D> provider1, ModelProvider<M, D> provider2)
225211
return ClassNameComparator.INSTANCE.compare(provider1, provider2);
226212
}
227213
}
228-
229-
/**
230-
* DataSource wrapper object, which weakly reference datasource and uses
231-
* reference-equality of DataSources when implementing hashCode and equals
232-
* this class is used as keys in modelCache
233-
*/
234-
private static class DataSourceKey<D extends DataSource> {
235-
Reference<D> weakReference;
236-
237-
DataSourceKey(D ds) {
238-
weakReference = new WeakReference(ds);
239-
}
240-
241-
public int hashCode() {
242-
D ds = weakReference.get();
243-
if (ds != null) {
244-
return ds.hashCode();
245-
}
246-
return 0;
247-
}
248-
249-
public boolean equals(Object obj) {
250-
if (obj == null) return false;
251-
if (obj instanceof DataSourceKey) {
252-
D ds = weakReference.get();
253-
D otherDs = ((DataSourceKey<D>)obj).weakReference.get();
254-
255-
return ds != null && ds == otherDs;
256-
}
257-
throw new IllegalArgumentException(obj.getClass().getName());
258-
}
259-
260-
public String toString() {
261-
DataSource ds = weakReference.get();
262-
return "DataSourceKey for "+System.identityHashCode(this)+" for "+ds==null?"NULL":ds.toString(); // NOI18N
263-
}
264-
}
265214
}

visualvm/host/src/org/graalvm/visualvm/host/Host.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public abstract class Host extends DataSource implements Stateful {
5151
private final String hostName;
5252
private InetAddress inetAddress;
5353
private int state = STATE_AVAILABLE;
54+
private int modCount;
5455

5556

5657
/**
@@ -100,9 +101,16 @@ public synchronized int getState() {
100101
return state;
101102
}
102103

104+
public int getModCount() {
105+
return modCount;
106+
}
107+
103108
protected final synchronized void setState(int newState) {
104109
int oldState = state;
105110
state = newState;
111+
if (oldState != newState && newState == STATE_AVAILABLE) {
112+
modCount++;
113+
}
106114
getChangeSupport().firePropertyChange(PROPERTY_STATE, oldState, newState);
107115
}
108116

visualvm/jmx/src/org/graalvm/visualvm/jmx/impl/JmxApplicationProvider.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -380,16 +380,11 @@ public void run() {
380380
if (model == null || model.getConnectionState() != JmxModel.ConnectionState.CONNECTED) {
381381
synchronized (unavailableApps) { unavailableApps.add(app); }
382382
} else {
383-
JmxModelFactory.getDefault().clearModel(app);
384-
JvmJvmstatModelFactory.getDefault().clearModel(app);
385-
JvmstatModelFactory.getDefault().clearModel(app);
386-
JvmFactory.getDefault().clearModel(app);
387-
383+
app.setStateImpl(Stateful.STATE_AVAILABLE);
384+
388385
model = JmxModelFactory.getJmxModelFor(app);
389386
app.jvm = JvmFactory.getJVMFor(app);
390387

391-
app.setStateImpl(Stateful.STATE_AVAILABLE);
392-
393388
model.addPropertyChangeListener(new PropertyChangeListener() {
394389
public void propertyChange(PropertyChangeEvent evt) {
395390
if (evt.getNewValue() == ConnectionState.CONNECTED) {

0 commit comments

Comments
 (0)