1 /*
2 Copyright 2008 Ramon Servadei
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 package fulmine.context;
17
18 import fulmine.IDomain;
19 import fulmine.ILifeCycle;
20 import fulmine.IType;
21 import fulmine.distribution.IDistributionManager;
22 import fulmine.distribution.connection.IConnectionBroker;
23 import fulmine.distribution.connection.IConnectionDiscoverer;
24 import fulmine.event.IEvent;
25 import fulmine.event.IEventManager;
26 import fulmine.event.IEventSource;
27 import fulmine.event.listener.IEventListener;
28 import fulmine.model.IModelManager;
29 import fulmine.model.container.IContainer;
30 import fulmine.model.field.BooleanField;
31 import fulmine.model.field.DoubleField;
32 import fulmine.model.field.FloatField;
33 import fulmine.model.field.IField;
34 import fulmine.model.field.IntegerField;
35 import fulmine.model.field.LongField;
36 import fulmine.model.field.StringField;
37 import fulmine.rpc.IRpcManager;
38
39 /**
40 * This is the application context for fulmine, a distributed data model system
41 * (DDMS). Application code uses a context to interact with the data model,
42 * event and distribution framework.
43 *
44 * <h2>Data model framework</h2> The data model is composed of
45 * {@link IContainer} objects. These represent the data entities that the
46 * context manages. The context keeps track of all containers created in this
47 * context. Containers created in the context are keyed against their type and
48 * identity. Changes to the containers can be observed using the event and
49 * distribution framework (see next sections).
50 * <p>
51 * There are 2 types of container in the context; local and remote. A local
52 * container is an instance that is created by local user code; the container is
53 * native to this context. Local containers are created via
54 * {@link IModelManager#getLocalContainer(String, IType, IDomain)}. A remote
55 * container is a 'proxy' to a local container that is native to a remote
56 * context. Any attempt by user code to alter a remote container's state will
57 * generate an exception. A remote container is created by accessing the
58 * {@link IModelManager#getRemoteContainer(String, String, IType, IDomain)}
59 * method.
60 * <p>
61 * Containers have a unidirectional distributed state; only changes in a local
62 * container are distributed to remote container instances. Remote containers
63 * are, to all intents and purposes, read-only in the local context.
64 *
65 * <h2>Event framework</h2>
66 * The event framework is based on {@link IEventListener} instances registered
67 * against {@link IEventSource} instances. The listeners receive the events
68 * generated by the sources they are registered for. This is all in a single
69 * process.
70 * <p>
71 * A key design principle is that an event source has an associated 'event
72 * processor' thread that never changes. The processor is mapped to the event
73 * source's type (see {@link IEventSource#getType()}). An event processor thread
74 * may service multiple sources of different types. All listeners registered
75 * against a source will be activated by the same thread when events are being
76 * notified. This does not mean the listener is inherently thread safe; if it is
77 * registered against 2 sources of 2 different types, there might be 2 threads
78 * activating the listener. The only rule is that the same thread activates the
79 * listener when notifying it with an event from the source the thread is
80 * associated with.
81 * <p>
82 * The events framework is accessed via the following methods:
83 * <ul>
84 * <li>{@link #queueEvent(IEvent)} to notify a listener with an event from a
85 * <li>
86 * {@link IDistributionManager#subscribe(String, String, IType, IDomain, IEventListener)}
87 * to register a listener for events from a source in a context
88 * </ul>
89 * The event framework can be used as an event distribution mechanism as
90 * demonstrated by the following code snippet.
91 *
92 * <pre>
93 * // create the context
94 * IFulmineContext context = new FulmineContext("context name");
95 * context.start();
96 *
97 * IEventListener listener = new CustomListener();
98 * // subscribe for an event source
99 * context.subscribe("context name", "foobar", Type.get(99), Domain.get(12),
100 * listener);
101 *
102 * // get the event source so we can trigger a change (a container is an event source)
103 * IEventSource container =
104 * context.getLocalContainer("foobar", Type.get(99), Domain.get(12));
105 * // generate an event from the event source
106 * // note that CustomEvent extends AbstractEvent...
107 * IEvent event = new CustomEvent(container);
108 * context.queueEvent(event);
109 * // the listener will receive the event in a separate thread - one of the context's event processors
110 * </pre>
111 *
112 * The data model framework interacts directly with the event framework and
113 * allows user code to receive events from the data model. Containers
114 * effectively raise events that encapsulate the latest change. The event will
115 * be a <i>clone</i> of the container so listeners are not interacting with the
116 * actual container. The following code snippet shows how to use the event
117 * framework to listen for data model changes.
118 *
119 * <pre>
120 * // create the context
121 * IFulmineContext context = new FulmineContext("context name");
122 * context.start();
123 *
124 * IEventListener listener = new CustomListener();
125 * // subscribe for the container
126 * context.subscribe("context name", "foobar", Type.get(99), Domain.get(12),
127 * listener);
128 *
129 * // get an event source (a container is an event source)
130 * IEventSource container =
131 * context.getLocalContainer("foobar", Type.get(99), Domain.get(12));
132 * // alter the container
133 * IntegerField field = new IntegerField("an integer component");
134 * field.set(1);
135 * container.add(field);
136 * container.flushFrame();
137 * // the listener will receive a clone of the container that has the field added
138 * </pre>
139 *
140 * <h2>Distribution framework</h2>
141 * Out-of-process event distribution is only available for data model changes
142 * (this was designed primarily as a distributed data model system). It requires
143 * collaborating objects:
144 * <ul>
145 * <li>{@link IConnectionDiscoverer} - to find other remote contexts
146 * <li>{@link IConnectionBroker} - to create a connection to the remote context
147 * </ul>
148 * The context must have these injected via the setter methods prior to being
149 * started. Once started with the connection collaborators, the context is
150 * remotely available and can receive data model changes to remote containers.
151 * It can also distribute local container changes to remote contexts. The
152 * distribution is achieved by a local context subscribing for a remote
153 * container in a remote context, as demonstrated below
154 *
155 * <pre>
156 * IEventListener listener = new CustomListener();
157 * // subscribe for the remote container from the remote context
158 * // it is assumed that the remote context exists in this example
159 * context.subscribe("a remote context", "foobar", Type.get(99), Domain.get(12),
160 * listener);
161 * // the listener will now receive changes from the remote container
162 * </pre>
163 *
164 * <h3>Updating remote containers</h3>
165 *
166 * A context can update a remote container only by invoking the
167 * {@link #updateRemoteContainer(String, String, IType, IDomain, String, String)}
168 * method. However, by default, a context will veto any attempt by a remote
169 * context to update one of its local containers. To override this, application
170 * code needs to provide a {@link IRemoteUpdateHandler} via the
171 * {@link #setRemoteUpdateHandler(IRemoteUpdateHandler)} method. The permission
172 * profile of the updating context is passed to the remote context during the
173 * operation; if the permissions of the updating context are not compatible with
174 * the field being updated, the operation will fail. The
175 * {@link #updateRemoteContainer(String, String, IType, IDomain, String, String)}
176 * method is in fact an RPC. RPC and permissions are described in the following
177 * sections.
178 *
179 * <h2>RPC framework</h2>
180 *
181 * Remote procedures can be invoked on remote contexts. In the remote context,
182 * application code must first register a procedure with the context. After
183 * this, the procedure is published to any contexts that have subscribed for
184 * registered RPCs.
185 * <p>
186 * The arguments for any RPC are limited to the 'native' types;
187 * {@link IntegerField}, {@link StringField}, {@link LongField},
188 * {@link DoubleField}, {@link FloatField}, {@link BooleanField}. The return
189 * type for an RPC is also limited to the above types. The 'void' return type is
190 * not supported.
191 * <p>
192 * See {@link RpcManager} for a more detailed description of the RPC
193 * fundamentals. The following code snippet demonstrates invoking a RPC:
194 *
195 * <pre>
196 * // local context registers for RPCs from the remote context
197 * context.addRpcPublicationListener(remoteContextIdentity, listener);
198 *
199 * // ... assume an RPC has been picked up by the publication listener
200 * // the RPC signature published is 'boolean helloWorld(String name)'
201 * // note that the name of the StringField (or any args during the
202 * // invocation) are purely arbitrary
203 * context.invoke(remoteContextIdentity, "helloWorld", new StringField("name",
204 * "lasers"));
205 * </pre>
206 *
207 * <h2>Permissions</h2>
208 *
209 * All data fields of a container include a permission profile. This allows the
210 * context to control visibility of the data for other remote contexts. Further
211 * information on the permissions is described in {@link IPermissionProfile}.
212 * <p>
213 *
214 * @author Ramon Servadei
215 */
216 public interface IFulmineContext extends ILifeCycle, IModelManager,
217 IEventManager, IDistributionManager, IRpcManager
218 {
219
220 final static String NAME = "Fulmine";
221
222 /**
223 * Start the context. The context becomes active and usable after this
224 * method completes. If a {@link IConnectionBroker} and
225 * {@link IConnectionDiscoverer} have been provided via the relevant setter
226 * methods, this context becomes remotely available.
227 */
228 void start();
229
230 /**
231 * Set the {@link INetwork} for the context. This allows the context to
232 * communicate with other contexts that use the same network transport. This
233 * must be called before {@link #start()}.
234 *
235 * @param network
236 * the network
237 */
238 void setNetwork(INetwork network);
239
240 /**
241 * Set the component that will report on the state of this context
242 *
243 * @param watchdog
244 * a component that can report on the state of this context
245 */
246 void setContextWatchdog(IContextWatchdog watchdog);
247
248 /**
249 * Set the permission profile for the context. This will define all the
250 * {@link IField}s that this context will be allowed to view.
251 *
252 * @param profile
253 * the permission profile
254 */
255 void setPermissionProfile(IPermissionProfile profile);
256
257 /**
258 * Get the permission profile for the context. This defines all the
259 * {@link IField}s that this context is allowed to view. If a profile has
260 * not been set by application code, a default one should be provided.
261 *
262 * @return the permission profile for this context
263 */
264 IPermissionProfile getPermissionProfile();
265
266 /**
267 * Get the unique identity of this context. In a network of contexts, the
268 * identity must by unique.
269 *
270 * @return the identity for this context
271 */
272 String getIdentity();
273
274 /**
275 * Get a unique integer for this context. This must be completely unique
276 * across all distributed context instances in the network, regardless of
277 * the context identity. This integer can be used to uniquely identify this
278 * context instance within the network.
279 *
280 * @return a unique integer for the context
281 */
282 int getContextHashCode();
283
284 /**
285 * Set the object that will handle updates to local records from remote
286 * contexts via RPC calls. By default a vetoing updater is used that
287 * prevents remote contexts from updating local records via RPC calls.
288 *
289 * @see IDistributionManager#updateRemoteContainer(String, String, IType,
290 * IDomain, String, String)
291 *
292 * @param handler
293 * the handler to use
294 */
295 void setRemoteUpdateHandler(IRemoteUpdateHandler handler);
296 }