[Tween-svn] [1129] TwitterクラスのUserstream部をクラス化。

Zurück zum Archiv-Index

svnno****@sourc***** svnno****@sourc*****
2010年 11月 29日 (月) 19:17:11 JST


Revision: 1129
          http://sourceforge.jp/projects/tween/svn/view?view=rev&revision=1129
Author:   kiri_feather
Date:     2010-11-29 19:17:11 +0900 (Mon, 29 Nov 2010)

Log Message:
-----------
TwitterクラスのUserstream部をクラス化。
HttpTwitterをIClonableにして、Streamを複数張れるように(接続・切断の簡素化)

Modified Paths:
--------------
    branches/UserStream/Tween/Connection/HttpTwitter.vb
    branches/UserStream/Tween/Twitter.vb


-------------- next part --------------
Modified: branches/UserStream/Tween/Connection/HttpTwitter.vb
===================================================================
--- branches/UserStream/Tween/Connection/HttpTwitter.vb	2010-11-29 04:04:56 UTC (rev 1128)
+++ branches/UserStream/Tween/Connection/HttpTwitter.vb	2010-11-29 10:17:11 UTC (rev 1129)
@@ -1,8 +1,10 @@
 Imports System.Net
 Imports System.IO
 Imports System.Web
+Imports System.Threading
 
 Public Class HttpTwitter
+    Implements ICloneable
 
     'OAuth関連
     '''<summary>
@@ -34,10 +36,6 @@
     End Enum
     Private connectionType As AuthMethod = AuthMethod.Basic
 
-    Public Sub New()
-        TwitterApiInfo.Initialize()
-    End Sub
-
     Public Sub Initialize(ByVal accessToken As String, _
                                     ByVal accessTokenSecret As String, _
                                     ByVal username As String)
@@ -52,7 +50,6 @@
             tk = accessToken
             tks = accessTokenSecret
             un = username
-            TwitterApiInfo.Initialize()
         End If
         con.Initialize(ConsumerKey, ConsumerSecret, accessToken, accessTokenSecret, username, "screen_name")
         httpCon = con
@@ -69,7 +66,6 @@
             ' 以前の認証状態よりひとつでも変化があったらhttpヘッダより読み取ったカウントは初期化
             un = username
             pw = password
-            TwitterApiInfo.Initialize()
         End If
         con.Initialize(username, password)
         httpCon = con
@@ -702,4 +698,14 @@
     Public Sub RequestAbort()
         httpCon.RequestAbort()
     End Sub
+
+    Public Function Clone() As Object Implements System.ICloneable.Clone
+        Dim myCopy As New HttpTwitter
+        If Me.connectionType = AuthMethod.Basic Then
+            myCopy.Initialize(Me.AuthenticatedUsername, Me.Password)
+        Else
+            myCopy.Initialize(Me.AccessToken, Me.AccessTokenSecret, Me.AuthenticatedUsername)
+        End If
+        Return myCopy
+    End Function
 End Class

Modified: branches/UserStream/Tween/Twitter.vb
===================================================================
--- branches/UserStream/Tween/Twitter.vb	2010-11-29 04:04:56 UTC (rev 1128)
+++ branches/UserStream/Tween/Twitter.vb	2010-11-29 10:17:11 UTC (rev 1129)
@@ -77,10 +77,6 @@
 
     Private twCon As New HttpTwitter
 
-    Private _streamThread As Thread
-    Private _streamActive As Boolean
-    Private _streamBypass As Boolean
-
     Public Function Authenticate(ByVal username As String, ByVal password As String) As String
 
         Dim res As HttpStatusCode
@@ -97,6 +93,7 @@
             Case HttpStatusCode.OK
                 Twitter.AccountState = ACCOUNT_STATE.Valid
                 _uid = username.ToLower
+                Me.ReconnectUserStream()
                 Return ""
             Case HttpStatusCode.Unauthorized
                 Twitter.AccountState = ACCOUNT_STATE.Invalid
@@ -119,6 +116,7 @@
 
     Public Sub ClearAuthInfo()
         Twitter.AccountState = ACCOUNT_STATE.Invalid
+        TwitterApiInfo.Initialize()
         twCon.ClearAuthInfo()
         _UserIdNo = ""
     End Sub
@@ -2797,26 +2795,131 @@
     Private Sub Twitter_ApiInformationChanged(ByVal sender As Object, ByVal e As ApiInformationChangedEventArgs) Handles Me.ApiInformationChanged
     End Sub
 
+
+
+    Public Event NewPostFromStream()
+    Public Event UserStreamStarted()
+    Public Event UserStreamStopped()
+    Public Event UserStreamPaused()
+    Public Event UserStreamGetFriendsList()
+    Private WithEvents userStream As TwitterUserstream
+
+    Private _streamBypass As Boolean
+    Private EventNameTable() As String = {
+        "favorite",
+        "unfavorite",
+        "follow",
+        "list_member_added",
+        "list_member_removed"
+    }
+
+    Private Sub userStream_StatusArrived(ByVal line As String) Handles userStream.StatusArrived
+        If _streamBypass OrElse String.IsNullOrEmpty(line) Then Exit Sub
+
+        Dim idx As Integer = line.IndexOf("{""")
+        Dim idx2 As Integer = line.IndexOf(""":")
+        If idx = 0 AndAlso idx2 > 0 Then
+            Dim eventname As String = line.Substring(idx + 2, idx2 - 2)
+            If eventname.Equals("friends") Then
+                Debug.Print("friends")
+                Exit Sub
+            ElseIf eventname.Equals("delete") Then
+                Debug.Print("delete")
+                Exit Sub
+            ElseIf eventname.Equals("target") Then
+                Dim data As DataModel.eventdata
+                Debug.Print("Event")
+                Using stream As New MemoryStream()
+                    Dim serializer As New DataContractJsonSerializer(GetType(DataModel.eventdata))
+                    Dim sb As New StringBuilder
+                    sb.Length = 0
+                    sb.Append(line)
+                    Dim buf As Byte() = Encoding.Unicode.GetBytes(sb.ToString)
+                    stream.Write(buf, offset:=0, count:=buf.Length)
+                    stream.Seek(offset:=0, loc:=SeekOrigin.Begin)
+                    data = DirectCast(serializer.ReadObject(stream), DataModel.eventdata)
+                End Using
+                Select Case Array.IndexOf(EventNameTable, data.event)
+                    Case 0  ' favorite
+                        Debug.Print("Event:favorite")
+                    Case 1  ' unfavorite
+                        Debug.Print("Event:unfavorite")
+                    Case 2  ' follow
+                        Debug.Print("Event:follow")
+                    Case 3  ' list_member_added
+                        Debug.Print("Event:list_member_added")
+                    Case 4  ' list_member_removed
+                        Debug.Print("Event:list_member_removed")
+                    Case Else ' その他イベント
+                        TraceOut("Unknown Event:" + data.event + Environment.NewLine + line)
+                End Select
+                Exit Sub
+            End If
+
+        End If
+
+        Dim res As New StringBuilder
+        res.Length = 0
+        res.Append("[")
+        res.Append(line)
+        res.Append("]")
+
+        Dim isDM As Boolean = False
+        If line.StartsWith("{""direct_message"":") Then
+            isDM = True
+        End If
+
+        Using stream As New MemoryStream()
+            Dim buf As Byte() = Encoding.Unicode.GetBytes(res.ToString)
+            stream.Write(buf, offset:=0, count:=buf.Length)
+            stream.Seek(offset:=0, loc:=SeekOrigin.Begin)
+            If isDM Then
+                CreateDirectMessagesFromJson(stream, WORKERTYPE.UserStream, False)
+            Else
+                CreatePostsFromJson(stream, WORKERTYPE.Timeline, Nothing, False, Nothing, Nothing)
+            End If
+        End Using
+        RaiseEvent NewPostFromStream()
+    End Sub
+
+    Private Sub userStream_Started() Handles userStream.Started
+        RaiseEvent UserStreamStarted()
+    End Sub
+
+    Private Sub userStream_Stopped() Handles userStream.Stopped
+        RaiseEvent UserStreamStopped()
+    End Sub
+
     Public ReadOnly Property UserStreamEnabled As Boolean
         Get
-            Return _streamActive
+            Return If(userStream Is Nothing, False, userStream.Enabled)
         End Get
     End Property
 
     Public Sub StartUserStream()
-        StopUserStream()
-        If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then Exit Sub
-        _streamThread = New Thread(AddressOf UserStreamLoop)
-        _streamThread.Name = "UserStreamReceiver"
-        _streamThread.IsBackground = True
-        _streamActive = True
-        _streamThread.Start()
+        If userStream IsNot Nothing Then
+            StopUserStream()
+        Else
+            Me._streamBypass = False
+            userStream = New TwitterUserstream(twCon)
+            userStream.Start()
+        End If
     End Sub
 
     Public Sub StopUserStream()
-        _streamActive = False
+        Me._streamBypass = True
+        If userStream IsNot Nothing Then userStream.Dispose()
+        userStream = Nothing
+        RaiseEvent UserStreamStopped()
     End Sub
 
+    Private Sub ReconnectUserStream()
+        If userStream IsNot Nothing Then
+            Me.StopUserStream()
+            Me.StartUserStream()
+        End If
+    End Sub
+
     Public Sub PauseUserStream()
         If _streamBypass Then
             _streamBypass = False
@@ -2827,138 +2930,115 @@
         End If
     End Sub
 
-    Private EventNameTable() As String = { _
-        "favorite", _
-        "unfavorite", _
-        "follow", _
-        "list_member_added", _
-        "list_member_removed"
-    }
+    Private Class TwitterUserstream
+        Implements IDisposable
 
-    Private Function ReadLine(ByVal sr As StreamReader) As String
-        Dim ret As New StringBuilder(8192)
-        Dim tmp As Integer
+        Public Event StatusArrived(ByVal status As String)
+        Public Event Stopped()
+        Public Event Started()
+        Private twCon As HttpTwitter
 
-        Do While _streamActive
-            tmp = sr.Read()
-            If tmp <> -1 Then
-                If tmp = &HA Then
-                    Return ret.ToString
-                Else
-                    ret.Append(Convert.ToChar(tmp))
-                End If
-            End If
-            Thread.Sleep(10)
-        Loop
-        Return ""
-    End Function
+        Private _streamThread As Thread
+        Private _streamActive As Boolean
 
-    Private Sub UserStreamLoop()
-        Dim st As Stream = Nothing
-        Dim sr As StreamReader = Nothing
-        Dim isRetry As Boolean = False
-        Dim res As New StringBuilder(8192)
-        Do
-            Try
-                isRetry = False
-                twCon.UserStream(st, False, "")
-                sr = New StreamReader(st)
-                RaiseEvent UserStreamStarted()
-                Do While _streamActive
-                    Dim line As String = ReadLine(sr)
-                    If _streamBypass OrElse String.IsNullOrEmpty(line) Then Continue Do
+        Public Sub New(ByVal twitterConnection As HttpTwitter)
+            twCon = DirectCast(twitterConnection.Clone(), HttpTwitter)
+        End Sub
 
-                    Dim idx As Integer = line.IndexOf("{""")
-                    Dim idx2 As Integer = line.IndexOf(""":")
-                    If idx = 0 AndAlso idx2 > 0 Then
-                        Dim eventname As String = line.Substring(idx + 2, idx2 - 2)
-                        If eventname.Equals("friends") Then
-                            Debug.Print("friends")
-                            Continue Do
-                        ElseIf eventname.Equals("delete") Then
-                            Debug.Print("delete")
-                            Continue Do
-                        ElseIf eventname.Equals("target") Then
-                            Dim data As DataModel.eventdata
-                            Debug.Print("Event")
-                            Using stream As New MemoryStream()
-                                Dim serializer As New DataContractJsonSerializer(GetType(DataModel.eventdata))
-                                res.Length = 0
-                                res.Append(line)
-                                Dim buf As Byte() = Encoding.Unicode.GetBytes(res.ToString)
-                                stream.Write(buf, offset:=0, count:=buf.Length)
-                                stream.Seek(offset:=0, loc:=SeekOrigin.Begin)
-                                data = DirectCast(serializer.ReadObject(stream), DataModel.eventdata)
-                            End Using
-                            Select Case Array.IndexOf(EventNameTable, data.event)
-                                Case 0  ' favorite
-                                    Debug.Print("Event:favorite")
-                                Case 1  ' unfavorite
-                                    Debug.Print("Event:unfavorite")
-                                Case 2  ' follow
-                                    Debug.Print("Event:follow")
-                                Case 3  ' list_member_added
-                                    Debug.Print("Event:list_member_added")
-                                Case 4  ' list_member_removed
-                                    Debug.Print("Event:list_member_removed")
-                                Case Else ' その他イベント
-                                    TraceOut("Unknown Event:" + data.event + Environment.NewLine + line)
-                            End Select
-                            Continue Do
-                        End If
+        Public Sub Start()
+            _streamActive = True
+            If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then Exit Sub
+            _streamThread = New Thread(AddressOf UserStreamLoop)
+            _streamThread.Name = "UserStreamReceiver"
+            _streamThread.IsBackground = True
+            _streamThread.Start()
+        End Sub
 
+        Public ReadOnly Property Enabled() As Boolean
+            Get
+                Return _streamActive
+            End Get
+        End Property
+
+        Private Sub UserStreamLoop()
+            Dim st As Stream = Nothing
+            Dim sr As StreamReader = Nothing
+            Do
+                Try
+                    RaiseEvent Started()
+
+                    twCon.UserStream(st, False, "")
+                    sr = New StreamReader(st)
+
+                    Do While _streamActive
+                        RaiseEvent StatusArrived(sr.ReadLine())
+                    Loop
+
+                    RaiseEvent Stopped()
+                    Exit Do
+                Catch ex As WebException
+                    If Not Me._streamActive Then
+                        Exit Do
+                    ElseIf ex.Status = WebExceptionStatus.Timeout Then
+                        RaiseEvent Stopped()
+                        Thread.Sleep(10 * 1000)
+                    Else
+                        ExceptionOut(ex)
                     End If
+                Catch ex As ThreadAbortException
+                    Exit Do
+                Catch ex As IOException
+                    If Not Me._streamActive Then
+                        Exit Do
+                    Else
+                        ExceptionOut(ex)
+                    End If
+                Catch ex As Exception
+                    ExceptionOut(ex)
+                Finally
+                    If sr IsNot Nothing Then
+                        twCon.RequestAbort()
+                        sr.BaseStream.Close()
+                    End If
+                End Try
+            Loop While True
+        End Sub
 
-                    res.Length = 0
-                    res.Append("[")
-                    res.Append(line)
-                    res.Append("]")
+#Region "IDisposable Support"
+        Private disposedValue As Boolean ' 重複する呼び出しを検出するには
 
-                    Dim isDM As Boolean = False
-                    If line.StartsWith("{""direct_message"":") Then
-                        isDM = True
+        ' IDisposable
+        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
+            If Not Me.disposedValue Then
+                If disposing Then
+                    ' TODO: マネージ状態を破棄します (マネージ オブジェクト)。
+                    _streamActive = False
+                    If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then
+                        _streamThread.Abort()
+                        _streamThread.Join(1000)
                     End If
-
-                    Using stream As New MemoryStream()
-                        Dim buf As Byte() = Encoding.Unicode.GetBytes(res.ToString)
-                        stream.Write(buf, offset:=0, count:=buf.Length)
-                        stream.Seek(offset:=0, loc:=SeekOrigin.Begin)
-                        If isDM Then
-                            CreateDirectMessagesFromJson(stream, WORKERTYPE.UserStream, False)
-                        Else
-                            CreatePostsFromJson(stream, WORKERTYPE.Timeline, Nothing, False, Nothing, Nothing)
-                        End If
-                    End Using
-                    RaiseEvent NewPostFromStream()
-                Loop
-            Catch ex As WebException
-                If ex.Status = WebExceptionStatus.Timeout Then
-                    isRetry = True
-                Else
-                    ExceptionOut(ex)
                 End If
-            Catch ex As Exception
-                ExceptionOut(ex)
-            Finally
-                _streamActive = False
-                If sr IsNot Nothing Then
-                    twCon.RequestAbort()
-                    sr.BaseStream.Close()
-                End If
-                RaiseEvent UserStreamStopped()
-                If isRetry Then
-                    Thread.Sleep(10 * 1000)
-                    _streamActive = True
-                End If
-            End Try
-        Loop While isRetry
-    End Sub
 
-    Public Event NewPostFromStream()
-    Public Event UserStreamStarted()
-    Public Event UserStreamStopped()
-    Public Event UserStreamPaused()
+                ' TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
+                ' TODO: 大きなフィールドを null に設定します。
+            End If
+            Me.disposedValue = True
+        End Sub
 
-    Public Event UserStreamGetFriendsList()
+        ' TODO: 上の Dispose(ByVal disposing As Boolean) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
+        'Protected Overrides Sub Finalize()
+        '    ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
+        '    Dispose(False)
+        '    MyBase.Finalize()
+        'End Sub
 
+        ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
+        Public Sub Dispose() Implements IDisposable.Dispose
+            ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
+            Dispose(True)
+            GC.SuppressFinalize(Me)
+        End Sub
+#End Region
+
+    End Class
 End Class



Tween-svn メーリングリストの案内
Zurück zum Archiv-Index