1 19 20 package org.apache.cayenne.remote; 21 22 import java.util.List ; 23 import java.util.ListIterator ; 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 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 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 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 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 objects = response.currentList(); 130 131 if (!objects.isEmpty()) { 132 133 DeepMergeOperation merger = new DeepMergeOperation(context); 134 135 ListIterator it = objects.listIterator(); 138 while (it.hasNext()) { 139 Persistent object = (Persistent) it.next(); 140 ObjectId id = object.getObjectId(); 141 142 if (id == null) { 144 throw new CayenneRuntimeException( 145 "Server returned an object without an id: " 146 + object); 147 } 148 149 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 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 postedBy = (originatingContext != null) 212 ? (Object ) 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 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 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 bridge.startup(eventManager, EventBridge.RECEIVE_LOCAL_EXTERNAL, null, this); 260 } 261 catch (Exception e) { 262 throw new CayenneRuntimeException("Error starting EventBridge " + bridge, e); 263 } 264 265 this.remoteChannelListener = bridge; 266 return true; 267 } 268 269 275 protected Object send(ClientMessage message, Class resultClass) { 276 Object result = connection.sendMessage(message); 277 278 if (result != null && !resultClass.isInstance(result)) { 279 String 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 |