/// <summary> /// Handles a quark crossing request. Checks to see if the request is possible, and returns true or false based on /// the validity of the crossing to the actor who requested. This is performed in 3 steps: /// Step 1: Tell root to be ready to receive crossing requests for the object /// Root will also do sanity checks (e.g. object was deleted before crossing), so checking actorStatus code /// is important. /// Step 2: Root provides URL for uploading updated properties and for downloading said object. /// Step 3: Tell actor about the URL where it may contact the root directly. /// TODO: This can be optimized if we are allowed to remember or calculate the URL. We could respond immediately with the URL /// and the actor would keep trying it until root accepts it. For now, we avoid concurrency. /// </summary> /// <param name="context"></param> /// <param name="actorRequest"></param> private void HandleCrossing(HttpListenerContext context, HttpListenerRequest actorRequest) { StreamReader actorReader = new StreamReader(actorRequest.InputStream); XmlSerializer deserializer = new XmlSerializer(typeof(CrossingRequest)); CrossingRequest cross = (CrossingRequest)deserializer.Deserialize(actorReader); QuarkPublisher curQp = null, prevQp = null; string url = ""; HttpStatusCode status; m_crossLock.EnterReadLock(); try { m_log.InfoFormat("{0}: Handling Crossing. Time: {1}", LogHeader, DateTime.Now.Ticks); if (m_quarkSubscriptions.TryGetValue(cross.curQuark, out curQp) && m_quarkSubscriptions.TryGetValue(cross.prevQuark, out prevQp)) { if (curQp.RootActorID != prevQp.RootActorID) { // TODO: Inter-root communication } else { // Actor's response variables HttpListenerResponse actorResponse = context.Response; Stream actorOutput = actorResponse.OutputStream; // Root's request variables RootInfo root = (RootInfo)m_actor[curQp.RootActorID]; HttpWebRequest rootRequest = (HttpWebRequest)WebRequest.Create("http://" + root.quarkAddress + "/cross/"); rootRequest.Credentials = CredentialCache.DefaultCredentials; rootRequest.Method = "POST"; rootRequest.ContentType = "text/json"; Stream rootOutput = rootRequest.GetRequestStream(); status = ValidateCrossing(cross); if (status != HttpStatusCode.Created) { actorResponse.StatusCode = (int)status; actorOutput.Close(); actorResponse.Close(); return; } // From here on, I might have to write, make sure we only do one of these at a time. // Can't go in UpgradeableLock with a ReadLock, so let go first. m_crossLock.ExitReadLock(); m_crossLock.EnterUpgradeableReadLock(); try { // First we double check nothing changed while we were waiting for the lock, and we are still valid to cross. status = ValidateCrossing(cross); if (status != HttpStatusCode.Created) { actorResponse.StatusCode = (int)status; actorOutput.Close(); actorResponse.Close(); return; } // Step 1: Tell root to be ready to receive crossing requests for the object // Root will also do sanity checks (e.g. object was deleted before crossing), so checking actorStatus code // is important. OSDMap DataMap = new OSDMap(); DataMap["uuid"] = OSD.FromUUID(cross.uuid); DataMap["pq"] = OSD.FromString(cross.prevQuark); DataMap["cq"] = OSD.FromString(cross.curQuark); DataMap["ts"] = OSD.FromLong(cross.timestamp); string encodedMap = OSDParser.SerializeJsonString(DataMap, true); byte[] rootData = System.Text.Encoding.ASCII.GetBytes(encodedMap); int rootDataLength = rootData.Length; rootOutput.Write(rootData, 0, rootDataLength); rootOutput.Close(); // Step 2: Root provides URL for uploading updated properties and for downloading said object. HttpWebResponse response = (HttpWebResponse)rootRequest.GetResponse(); if (HttpStatusCode.OK == response.StatusCode) { m_crossLock.EnterWriteLock(); try { m_crossings[cross.uuid] = new CurrentCrossings(); m_crossings[cross.uuid].cross = cross; m_crossings[cross.uuid].actors.UnionWith(m_quarkSubscriptions[cross.prevQuark].GetAllQuarkSubscribers()); m_crossings[cross.uuid].actors.UnionWith(m_quarkSubscriptions[cross.curQuark].GetAllQuarkSubscribers()); // Remove the starting actor from the list of actors to ACK. m_crossings[cross.uuid].actors.Remove(cross.actorID); m_crossings[cross.uuid].rootHandler = root; } finally { m_crossLock.ExitWriteLock(); } Stream respData = response.GetResponseStream(); StreamReader rootReader = new StreamReader(respData); url = rootReader.ReadToEnd(); m_log.WarnFormat("{0}: Got URL for object request from server: {1}", LogHeader, url); if (url.Length > 0) { // Step 3: Tell actor about the URL where it may contact the root directly. // TODO: This can be optimized if we are allowed to remember or calculate the URL. We could respond immediately with the URL // and the actor would keep trying it until root accepts it. For now, we avoid concurrency. actorResponse.StatusCode = (int)HttpStatusCode.Created; byte[] actorUrlData = System.Text.Encoding.ASCII.GetBytes(url); int actorUrlDataLength = actorUrlData.Length; actorOutput.Write(actorUrlData, 0, actorUrlDataLength); actorOutput.Close(); actorResponse.Close(); } else { m_log.ErrorFormat("{0}: Received empty URL from Root", LogHeader); } } else { m_log.ErrorFormat("{0}: Failed to request crossing from root. Error Code: {1}", LogHeader, response.StatusCode); } } finally { m_crossLock.ExitUpgradeableReadLock(); } } } } catch (Exception e) { m_log.ErrorFormat("{0}: Failed to request crossing from root and forward to actor. Exception: {1}\n{2}", LogHeader, e, e.StackTrace); } finally { if (m_crossLock.IsReadLockHeld) { m_crossLock.ExitReadLock(); } } }
private void RegisterActor(XMLQuarkSubscription sub, bool isRoot) { HashSet <string> activeQuarks = SyncQuark.DecodeSyncQuarks(sub.activeQuarks); HashSet <string> passiveQuarks = SyncQuark.DecodeSyncQuarks(sub.passiveQuarks); QuarkPublisher qp; // Save SyncID to XMLQuarkSubscription, if we need the info later if (m_actor.ContainsKey(sub.actorID)) { UnRegisterActor(sub.actorID); } if (isRoot) { RootInfo root = new RootInfo(); root.quarkAddress = sub.rootAddress; root.syncAddress = sub.syncListenerAddress; root.Roots.Add(root); m_actor[sub.actorID] = root; m_rootActors.Add((RootInfo)m_actor[sub.actorID]); } else { if (sub.syncListenerAddress.Length > 0) { RelayActor relActor = new RelayActor(); relActor.syncAddress = sub.syncListenerAddress; m_actor[sub.actorID] = relActor; } else { Actor actor = new Actor(); m_actor[sub.actorID] = actor; } // Note: Adding all the roots as this actor's root for now. foreach (RootInfo root in m_rootActors) { m_actor[sub.actorID].Roots.Add(root); } } m_actor[sub.actorID].ActiveQuarks = activeQuarks; m_actor[sub.actorID].PassiveQuarks = passiveQuarks; m_actor[sub.actorID].XmlSubscription = sub; foreach (string quark in activeQuarks) { if (!QuarkSubscriptions.TryGetValue(quark, out qp)) { qp = new QuarkPublisher(new SyncQuark(quark)); QuarkSubscriptions.Add(quark, qp); } qp.AddActiveSubscriber(sub.actorID); if (isRoot) { qp.SetRootActor(sub.actorID); } } foreach (string quark in passiveQuarks) { if (!QuarkSubscriptions.TryGetValue(quark, out qp)) { qp = new QuarkPublisher(new SyncQuark(quark)); QuarkSubscriptions.Add(quark, qp); } qp.AddPassiveSubscriber(sub.actorID); } }
/// <summary> /// Used for actors ACKing received crossed messages. Once all expected actors have ACKEd the crossing message, /// the Sync Service informs the root actor to push a crossing finished. /// </summary> /// <param name="context"></param> /// <param name="request"></param> private void HandleAckCrossing(HttpListenerContext context, HttpListenerRequest request) { m_log.InfoFormat("{0}: HandleAckCrossing", LogHeader); StreamReader actorReader = new StreamReader(request.InputStream); XmlSerializer deserializer = new XmlSerializer(typeof(CrossingFinished)); CrossingFinished cf = (CrossingFinished)deserializer.Deserialize(actorReader); string ackActorID = cf.ackActorID; CrossingRequest cross = cf.cross; RootInfo root = null; bool allAcksReceived = false; try { HttpStatusCode actorStatus; m_crossings[cross.uuid].actors.Remove(ackActorID); m_log.InfoFormat("{0}: Ack received from {1}, {2} acks remaining.", LogHeader, ackActorID, m_crossings[cross.uuid].actors.Count); if (m_crossings[cross.uuid].actors.Count == 0) { root = m_crossings[cross.uuid].rootHandler; actorStatus = CancelCrossing(cross); allAcksReceived = true; } else { actorStatus = HttpStatusCode.OK; } HttpListenerResponse actorResponse = context.Response; actorResponse.StatusCode = (int)actorStatus; actorResponse.Close(); if (allAcksReceived) { if (actorStatus == HttpStatusCode.OK) { m_log.InfoFormat("{0}: Informing root that crossing is finished", LogHeader); // Now tell root to tell every actor that the crossing is finnished! // Root's request variables HttpWebRequest rootRequest = (HttpWebRequest)WebRequest.Create("http://" + root.quarkAddress + "/finished/"); rootRequest.Credentials = CredentialCache.DefaultCredentials; rootRequest.Method = "POST"; rootRequest.ContentType = "text/json"; Stream rootOutput = rootRequest.GetRequestStream(); OSDMap DataMap = new OSDMap(); DataMap["uuid"] = OSD.FromUUID(cross.uuid); DataMap["ts"] = OSD.FromLong(cross.timestamp); string encodedMap = OSDParser.SerializeJsonString(DataMap, true); byte[] rootData = System.Text.Encoding.ASCII.GetBytes(encodedMap); int rootDataLength = rootData.Length; rootOutput.Write(rootData, 0, rootDataLength); rootOutput.Close(); // Check if everything went OK try { HttpWebResponse response = (HttpWebResponse)rootRequest.GetResponse(); if (response.StatusCode == HttpStatusCode.OK) { m_log.InfoFormat("{0}: Successfully finished crossing.", LogHeader); return; } } catch (WebException we) { var resp = we.Response as HttpWebResponse; m_log.ErrorFormat("{0}: Sync Crossing finished fail with actorStatus code: {1}", LogHeader, resp.StatusCode); } } else { throw new Exception("Could not find the object in the crossing dictionary"); } } } catch (Exception e) { m_log.ErrorFormat("{0}: Unkown exception while handling ACK from actor. Exception {1}", LogHeader, e); } }