File: NetworkList.java

package info (click to toggle)
libglazedlists-java 1.9.1-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 3,012 kB
  • sloc: java: 22,561; xml: 940; makefile: 5
file content (287 lines) | stat: -rw-r--r-- 11,107 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.io;

// the core Glazed Lists packages
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.impl.io.Bufferlo;
import ca.odell.glazedlists.impl.io.ListEventToBytes;
import ca.odell.glazedlists.impl.rbp.Resource;
import ca.odell.glazedlists.impl.rbp.ResourceListener;
import ca.odell.glazedlists.impl.rbp.ResourceStatus;
import ca.odell.glazedlists.impl.rbp.ResourceStatusListener;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * An {@link EventList} that is either published to the network or subscribed from
 * the network. Since list elements must be transmitted over the network, each
 * {@link NetworkList} requires a {@link ByteCoder} to convert {@link Object}s to
 * and from bytes.
 *
 * <p>To instantiate a {@link NetworkList}, use the
 * {@link ListPeer#subscribe(String,int,String,ByteCoder) subscribe()}
 * and {@link ListPeer#publish(EventList,String,ByteCoder) publish()} methods
 * of a started {@link ListPeer}.
 *
 * <p>{@link NetworkList}s may be taken offline and brought back online with the
 * {@link #connect()} and {@link #disconnect()} methods. This allows an application
 * to use a {@link NetworkList} in spite of an unreliable network connection.
 *
 * <p>As a consequence of imperfect networks, {@link NetworkList}s may sometimes go
 * offline on their own. Some causes of this include the server program shutting
 * down or crashing, the local network connection becoming unavailable, or a
 * problem with the physical link, such as an unplugged cable.
 *
 * <p>{@link NetworkList}s use a subset of HTTP/1.1 for transport, including
 * chunked encoding. This protocol brings its own set of advantages:
 *    <li>HTTP is a standard well-understood protocol
 *    <li>Clients may be served even if they are behind NAT or Firewalls
 *    <li>The connection could be proxied by a standard HTTP proxy server, if necessary
 *    <li>In theory, the served port could be shared with another HTTP daemon such as Tomcat
 * 
 * <p>And HTTP brings some disadvantages also:
 *    <li>A persistent connection must be held, even if updates are infrequent
 *    <li>It cannot be differentiated from web traffic for analysis
 *
 * <p><strong><font color="#FF0000">Warning:</font></strong> The protocol used by
 * this version of {@link NetworkList} will be incompatible with future versions.
 * Eventually the protocol will be finalized but the current protocol is a work
 * in progress.
 *
 * <p><strong><font color="#FF0000">Warning:</font></strong> This class
 * breaks the contract required by {@link java.util.List}. See {@link EventList}
 * for an example.
 *
 * <p><table border="1" width="100%" cellpadding="3" cellspacing="0">
 * <tr class="TableHeadingColor"><td colspan=2><font size="+2"><b>EventList Overview</b></font></td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Writable:</b></td><td>yes</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Concurrency:</b></td><td>Requires {@link ReadWriteLock} for every access, even for single-threaded use</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Performance:</b></td><td>N/A</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Memory:</b></td><td>O(N)</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Unit Tests:</b></td><td>N/A</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Issues:</b></td><td>N/A</td></tr>
 * </table>
 *
 * @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
 */
public final class NetworkList<E> extends TransformedList<E, E> {

    /** listeners to resource changes */
    private List<ResourceListener> resourceListeners = new ArrayList<ResourceListener>();
    
    /** listeners to status changes */
    private List<NetworkListStatusListener> statusListeners = new ArrayList<NetworkListStatusListener>();
    
    /** how bytes are encoded and decoded */
    private ByteCoder byteCoder;
    
    /** who manages this resource's connection */
    private ResourceStatus resourceStatus = null;
    
    /** whether this NetworkList is writable via its own API */
    private boolean writable = false;
    
    /** implementations of ResourceStatusListener and Resource */
    private PrivateInterfaces privateInterfaces = new PrivateInterfaces();
    
    /**
     * Create a {@link NetworkList} that brings the specified source online.
     */
    NetworkList(EventList<E> source, ByteCoder byteCoder) {
        super(source);
        this.byteCoder = byteCoder;
        source.addListEventListener(this);
    }
    
    /**
     * Sets the ResourceStatus to delegate connection information requests to.
     */
    void setResourceStatus(ResourceStatus resourceStatus) {
        if(this.resourceStatus != null) throw new IllegalStateException();
        this.resourceStatus = resourceStatus;
        resourceStatus.addResourceStatusListener(privateInterfaces);
    }
    
    /** 
     * Set the NetworkList as writable.
     */
    void setWritable(boolean writable) {
        this.writable = writable;
    }
    /** {@inheritDoc} */
    @Override
    public boolean isWritable() {
        return writable;
    }
    
    /**
     * Gets the {@link Resource} that is the peer of this NetworkList.
     */
    Resource getResource() {
        return privateInterfaces;
    }
    
    /** {@inheritDoc} */
    @Override
    public void listChanged(ListEvent<E> listChanges) {
        // notify resource listeners
        try {
            ListEvent<E> listChangesCopy = listChanges.copy();
            Bufferlo listChangesBytes = ListEventToBytes.toBytes(listChangesCopy, byteCoder);
            for(int r = 0; r < resourceListeners.size(); r++) {
                ResourceListener listener = resourceListeners.get(r);
                listener.resourceUpdated(privateInterfaces, listChangesBytes.duplicate());
            }
        } catch(IOException e) {
            throw new IllegalStateException(e.getMessage());
        }
        
        // forward the event
        updates.forwardEvent(listChanges);
    }
    
    /**
     * Returns true if this resource is on the network. For published lists, this
     * requires that the list is being served. For subscribed lists, this requires
     * that a connection to the server has been established.
     */
    public boolean isConnected() {
         return resourceStatus.isConnected();
    }
    
    /**
     * Attempts to bring this {@link NetworkList} online. When the connection attempt
     * is successful (or when it fails), all {@link ResourceStatusListener}s will be
     * notified.
     */
    public void connect() {
        resourceStatus.connect();
    }
    
    /**
     * Attempts to take this {@link NetworkList} offline. When the {@link NetworkList}
     * is fully disconnected, all {@link ResourceStatusListener}s will be notified.
     */
    public void disconnect() {
        resourceStatus.disconnect();
    }
    
    /**
     * Implementations of all private interfaces.
     */
    private class PrivateInterfaces implements Resource, ResourceStatusListener {
    
        /**
         * Called each time a resource becomes connected.
         */
        public void resourceConnected(ResourceStatus resource) {
            for(Iterator i = statusListeners.iterator(); i.hasNext(); ) {
                NetworkListStatusListener listener = (NetworkListStatusListener)i.next();
                listener.connected(NetworkList.this);
            }
        }
        
        /**
         * Called each time a resource's disconnected status changes. This method may
         * be called for each attempt it makes to reconnect to the network.
         */
        public void resourceDisconnected(ResourceStatus resource, Exception cause) {
            for(Iterator i = statusListeners.iterator(); i.hasNext(); ) {
                NetworkListStatusListener listener = (NetworkListStatusListener)i.next();
                listener.disconnected(NetworkList.this, cause);
            }
        }

        /** {@inheritDoc} */
        public Bufferlo toSnapshot() {
            getReadWriteLock().writeLock().lock();
            try {
                return ListEventToBytes.toBytes(NetworkList.this, byteCoder);
            } catch(IOException e) {
                throw new IllegalStateException(e.getMessage());
            } finally {
                getReadWriteLock().writeLock().unlock();
            }
        }
    
        /** {@inheritDoc} */
        public void fromSnapshot(Bufferlo snapshot) {
            applyCodedEvent(snapshot);
        }
        
        /** {@inheritDoc} */
        private void applyCodedEvent(Bufferlo data) {
            getReadWriteLock().writeLock().lock();
            try {
                updates.beginEvent(true);
                ListEventToBytes.toListEvent(data, source, byteCoder);
                updates.commitEvent();
            } catch(IOException e) {
                throw new IllegalStateException(e.getMessage());
            } finally {
                getReadWriteLock().writeLock().unlock();
            }
        }
        
        /** {@inheritDoc} */
        public void update(Bufferlo delta) {
            applyCodedEvent(delta);
        }
        
        /** {@inheritDoc} */
        public void addResourceListener(ResourceListener listener) {
            resourceListeners.add(listener);
        }
        
        /** {@inheritDoc} */
        public void removeResourceListener(ResourceListener listener) {
            for(int r = 0; r < resourceListeners.size(); r++) {
                if(resourceListeners.get(r) == listener) {
                    resourceListeners.remove(r);
                    return;
                }
            }
        }
        
        /** {@inheritDoc} */
        public ReadWriteLock getReadWriteLock() {
             return NetworkList.this.getReadWriteLock();
        }
        @Override
        public String toString() {
            return NetworkList.this.toString();
        }
    }
        
    /**
     * Registers the specified listener to receive events about the status of this
     * {@link NetworkList}.
     */
    public void addStatusListener(NetworkListStatusListener listener) {
        statusListeners.add(listener);
    }
    
    /**
     * Deregisters the specified listener from receiving events about the status of
     * this {@link NetworkList}.
     */
    public void removeStatusListener(NetworkListStatusListener listener) {
        statusListeners.remove(listener);
    }
    
    /** {@inheritDoc} */
    @Override
    public void dispose() {
        resourceStatus.removeResourceStatusListener(privateInterfaces);
        disconnect();
        super.dispose();
    }
}