KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > remote > ClientChannel


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.remote;
21
22 import java.util.List JavaDoc;
23 import java.util.ListIterator JavaDoc;
24
25 import org.apache.commons.lang.builder.ToStringBuilder;
26 import org.apache.cayenne.CayenneRuntimeException;
27 import org.apache.cayenne.DataChannel;
28 import org.apache.cayenne.ObjectContext;
29 import org.apache.cayenne.ObjectId;
30 import org.apache.cayenne.Persistent;
31 import org.apache.cayenne.QueryResponse;
32 import org.apache.cayenne.event.EventBridge;
33 import org.apache.cayenne.event.EventManager;
34 import org.apache.cayenne.event.EventSubject;
35 import org.apache.cayenne.graph.CompoundDiff;
36 import org.apache.cayenne.graph.GraphDiff;
37 import org.apache.cayenne.graph.GraphEvent;
38 import org.apache.cayenne.map.EntityResolver;
39 import org.apache.cayenne.query.Query;
40 import org.apache.cayenne.query.QueryMetadata;
41 import org.apache.cayenne.reflect.ClassDescriptor;
42 import org.apache.cayenne.util.DeepMergeOperation;
43
44 /**
45  * A {@link org.apache.cayenne.DataChannel} implementation that accesses a remote server
46  * via a ClientConnection.
47  *
48  * @since 1.2
49  * @author Andrus Adamchik
50  */

51 public class ClientChannel implements DataChannel {
52
53     protected ClientConnection connection;
54     protected EventManager eventManager;
55     protected EntityResolver entityResolver;
56     protected boolean channelEventsEnabled;
57
58     EventBridge remoteChannelListener;
59
60     /**
61      * Creates a new channel accessing remote server via provided connection. Channel
62      * created using this constructor will post no events of its own and provide its users
63      * with a multithreaded EventManager.
64      */

65     public ClientChannel(ClientConnection connection) {
66         this(connection, false);
67     }
68
69     public ClientChannel(ClientConnection connection, boolean channelEventsEnabled) {
70         this(connection, channelEventsEnabled, new EventManager(2));
71     }
72
73     public ClientChannel(ClientConnection connection, boolean channelEventsEnabled,
74             EventManager eventManager) throws CayenneRuntimeException {
75         this(connection, channelEventsEnabled, eventManager, false);
76     }
77
78     /**
79      * @param remoteEventsOptional if true, failure to start an EventBridge will not
80      * result in an exception.
81      * @since 3.0
82      */

83     public ClientChannel(ClientConnection connection, boolean channelEventsEnabled,
84             EventManager eventManager, boolean remoteEventsOptional)
85             throws CayenneRuntimeException {
86
87         this.connection = connection;
88         this.eventManager = eventManager;
89         this.channelEventsEnabled = eventManager != null && channelEventsEnabled;
90
91         if (!remoteEventsOptional) {
92             setupRemoteChannelListener();
93         }
94         else {
95             try {
96                 setupRemoteChannelListener();
97             }
98             catch (CayenneRuntimeException e) {
99
100             }
101         }
102     }
103
104     public EventManager getEventManager() {
105         return eventManager;
106     }
107
108     public QueryResponse onQuery(ObjectContext context, Query query) {
109
110         QueryResponse response = (QueryResponse) send(
111                 new QueryMessage(query),
112                 QueryResponse.class);
113
114         // if needed, register objects in provided context, rewriting the response
115
// (assuming all lists are mutable)
116

117         if (context != null) {
118
119             EntityResolver resolver = context.getEntityResolver();
120             QueryMetadata info = query.getMetaData(resolver);
121
122             if (!info.isFetchingDataRows()) {
123
124                 response.reset();
125
126                 while (response.next()) {
127                     if (response.isList()) {
128
129                         List JavaDoc objects = response.currentList();
130
131                         if (!objects.isEmpty()) {
132
133                             DeepMergeOperation merger = new DeepMergeOperation(context);
134
135                             // subclass descriptors will be resolved on the fly... here
136
// find objects base descriptor.
137
ListIterator JavaDoc it = objects.listIterator();
138                             while (it.hasNext()) {
139                                 Persistent object = (Persistent) it.next();
140                                 ObjectId id = object.getObjectId();
141
142                                 // sanity check
143
if (id == null) {
144                                     throw new CayenneRuntimeException(
145                                             "Server returned an object without an id: "
146                                                     + object);
147                                 }
148
149                                 // have to resolve descriptor here for every object, as
150
// often a query will not have any info indicating the
151
// entity type
152
ClassDescriptor descriptor = resolver
153                                         .getClassDescriptor(id.getEntityName());
154
155                                 it.set(merger.merge(object, descriptor));
156                             }
157                         }
158                     }
159                 }
160             }
161         }
162
163         return response;
164     }
165
166     public GraphDiff onSync(
167             ObjectContext originatingContext,
168             GraphDiff changes,
169             int syncType) {
170
171         GraphDiff replyDiff = (GraphDiff) send(new SyncMessage(
172                 originatingContext,
173                 syncType,
174                 changes), GraphDiff.class);
175
176         if (channelEventsEnabled) {
177             EventSubject subject;
178
179             switch (syncType) {
180                 case DataChannel.ROLLBACK_CASCADE_SYNC:
181                     subject = DataChannel.GRAPH_ROLLEDBACK_SUBJECT;
182                     break;
183                 case DataChannel.FLUSH_NOCASCADE_SYNC:
184                     subject = DataChannel.GRAPH_CHANGED_SUBJECT;
185                     break;
186                 case DataChannel.FLUSH_CASCADE_SYNC:
187                     subject = DataChannel.GRAPH_FLUSHED_SUBJECT;
188                     break;
189                 default:
190                     subject = null;
191             }
192
193             if (subject != null) {
194
195                 // combine message sender changes and message receiver changes into a
196
// single event
197
boolean sentNoop = changes == null || changes.isNoop();
198                 boolean receivedNoop = replyDiff == null || replyDiff.isNoop();
199
200                 if (!sentNoop || !receivedNoop) {
201                     CompoundDiff notification = new CompoundDiff();
202
203                     if (!sentNoop) {
204                         notification.add(changes);
205                     }
206
207                     if (!receivedNoop) {
208                         notification.add(replyDiff);
209                     }
210
211                     Object JavaDoc postedBy = (originatingContext != null)
212                             ? (Object JavaDoc) originatingContext
213                             : this;
214                     GraphEvent e = new GraphEvent(this, postedBy, notification);
215                     eventManager.postEvent(e, subject);
216                 }
217             }
218         }
219
220         return replyDiff;
221     }
222
223     /**
224      * Returns EntityResolver obtained from the server. On first access, this method sends
225      * a message to the server to retrieve the EntityResolver. On subsequent calls locally
226      * cached resolver is used.
227      */

228     public EntityResolver getEntityResolver() {
229         if (entityResolver == null) {
230             synchronized (this) {
231                 if (entityResolver == null) {
232                     entityResolver = (EntityResolver) send(
233                             new BootstrapMessage(),
234                             EntityResolver.class);
235                 }
236             }
237         }
238
239         return entityResolver;
240     }
241
242     /**
243      * Starts up an EventBridge to listen for remote updates. Returns true if the listener
244      * was setup, false if not. False can be returned if the underlying connection doesn't
245      * support events of if there is no EventManager available.
246      */

247     protected boolean setupRemoteChannelListener() throws CayenneRuntimeException {
248         if (eventManager == null) {
249             return false;
250         }
251
252         EventBridge bridge = connection.getServerEventBridge();
253         if (bridge == null) {
254             return false;
255         }
256
257         try {
258             // make sure events are sent on behalf of this channel...and received from all
259
bridge.startup(eventManager, EventBridge.RECEIVE_LOCAL_EXTERNAL, null, this);
260         }
261         catch (Exception JavaDoc e) {
262             throw new CayenneRuntimeException("Error starting EventBridge " + bridge, e);
263         }
264
265         this.remoteChannelListener = bridge;
266         return true;
267     }
268
269     /**
270      * Sends a message via connector, getting a result as an instance of a specific class.
271      *
272      * @throws org.apache.cayenne.client.CayenneClientException if an underlying connector
273      * exception occured, or a result is not of expected type.
274      */

275     protected Object JavaDoc send(ClientMessage message, Class JavaDoc resultClass) {
276         Object JavaDoc result = connection.sendMessage(message);
277
278         if (result != null && !resultClass.isInstance(result)) {
279             String JavaDoc resultString = new ToStringBuilder(result).toString();
280             throw new CayenneRuntimeException("Expected result type: "
281                     + resultClass.getName()
282                     + ", actual: "
283                     + resultString);
284         }
285
286         return result;
287     }
288 }
289
Popular Tags