PowerShellで作るdeliciousクライアント ラスト
前回の続きです。
タグの一覧を取得
タグの一覧を取得する機能を実装します。
まずは「DeliciousTag.vb」というファイル名でクラスファイルを追加します。
DeliciousTag.vb
Public Class DeliciousTag Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property Private _count As Integer Public Property Count() As Integer Get Return _count End Get Set(ByVal value As Integer) _count = value End Set End Property End Class
このクラスはタグの情報を格納します。
DeliciousクラスにGetTagsという名前でメソッドを定義します。
Delicious.vb
Function GetTags() As List(Of DeliciousTag) Dim address = "https://api.del.icio.us/v1/tags/get" Dim contents = GetContents(address) Dim xml = XDocument.Parse(contents) Dim results = From ele In xml.Descendants("tag") _ Select New DeliciousTag With { _ .Name = ele.Attribute("tag"), _ .Count = ele.Attribute("count") _ } Return results.ToList() End Function Private Function GetContents(ByVal address As String) Dim wc = New Net.WebClient() wc.Credentials = _credential wc.Encoding = Text.Encoding.UTF8 Return wc.DownloadString(address) End Function
処理的には特に説明する必要がありませんね。
ちなみにWebClientを使ってリクエストを送る処理をGetContentsというメソッドに分離しておきました。
後は、このメソッドをプロバイダークラスから呼び出すように変更します。
GetChildItemsParametersクラスにTagOnlyというプロパティを追加します。 このパラメータが指定された時だけタグ一覧を出力するという感じです。
DeliciousProvider.vb
Private Class GetChildItemsParameters Private _start As Integer = 0 <Parameter(Position:=0)> _ Public Property Start() As Integer Get Return _start End Get Set(ByVal value As Integer) _start = value End Set End Property Private _count As Integer = 20 <Parameter(Position:=1)> _ Public Property Count() As Integer Get Return _count End Get Set(ByVal value As Integer) _count = value End Set End Property Private _tag As String() <Parameter(Position:=2)> _ Public Property Tag() As String() Get Return _tag End Get Set(ByVal value As String()) _tag = value End Set End Property Private _tagOnly As SwitchParameter <Parameter()> _ Public Property TagOnly() As SwitchParameter Get Return _tagOnly End Get Set(ByVal value As SwitchParameter) _tagOnly = value End Set End Property End Class
GetChildItemsメソッドを以下のように変更します。
DeliciousProvider.vb
Protected Overrides Sub GetChildItems(ByVal path As String, ByVal recurse As Boolean) Dim dynaParams = CType(DynamicParameters, GetChildItemsParameters) If dynaParams.TagOnly.IsPresent Then For Each tag In Me.Delicious.GetTags() WriteItemObject(tag, path, False) Next Else For Each bookmark In Me.Delicious.GetBookmarks(dynaParams.Start, dynaParams.Count, dynaParams.Tag) WriteItemObject(bookmark, path, False) Next End If End Sub
TagOnlyプロパティが指定された時だけタグを取得して、出力していますね。
タグの情報を見易く表示する為に書式設定ファイルにDeliciousTagクラスの設定を追加しておきます。
psdelicious.format.ps1xml
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <ViewDefinitions> <View> <Name>Tags</Name> <ViewSelectedBy> <TypeName>PSDelicious.DeliciousTag</TypeName> </ViewSelectedBy> <TableControl> <TableHeaders> <TableColumnHeader> <Label>タグ名</Label> </TableColumnHeader> <TableColumnHeader> <Label>タグ数</Label> </TableColumnHeader> </TableHeaders> <TableRowEntries> <TableRowEntry> <TableColumnItems> <TableColumnItem> <PropertyName>Name</PropertyName> </TableColumnItem> <TableColumnItem> <PropertyName>Count</PropertyName> </TableColumnItem> </TableColumnItems> </TableRowEntry> </TableRowEntries> </TableControl> </View> <View> <Name>Tags</Name> <ViewSelectedBy> <TypeName>PSDelicious.DeliciousBookmark</TypeName> </ViewSelectedBy> <ListControl> <ListEntries> <ListEntry> <ListItems> <ListItem> <Label>タイトル</Label> <PropertyName>Title</PropertyName> </ListItem> <ListItem> <Label>URL</Label> <PropertyName>Url</PropertyName> </ListItem> <ListItem> <Label>コメント</Label> <PropertyName>Comment</PropertyName> </ListItem> <ListItem> <Label>タグ</Label> <PropertyName>Tags</PropertyName> </ListItem> <ListItem> <Label>更新日付</Label> <PropertyName>Date</PropertyName> </ListItem> </ListItems> </ListEntry> </ListEntries> </ListControl> </View> </ViewDefinitions> </Configuration>
これで、以下のようにするとタグの一覧を表示することができます。
PS > dir -TagOnly タグ名 タグ数 ------ ------ .NET 67 ADO.NET 3 AIR 2 AOP 1 API 2 ASP.NET 19 ActionScript 1 Ajax 2 Apache 3 AppEngine 4
残りの機能を一気に実装します。
タグの編集と削除
DeliciousクラスにDeleteTagとRenameTagというメソッドを追加します。
Delicious.vb
Sub DeleteTag(ByVal tag As String) Dim address = "https://api.del.icio.us/v1/tags/delete?tag=" + Web.HttpUtility.UrlEncode(tag) Dim contents = GetContents(address) Dim result = GetResult(contents) If result <> "done" Then Throw New ArgumentException(result) End Sub Sub RenameTag(ByVal oldValue As String, ByVal newValue As String) Dim address = String.Format( _ "https://api.del.icio.us/v1/tags/rename?old={0}&new={1}", _ Web.HttpUtility.UrlEncode(oldValue), Web.HttpUtility.UrlEncode(newValue) _ ) Dim contents = GetContents(address) Dim result = GetResult(contents) If result <> "done" Then Throw New ArgumentException(result) End Sub Private Shared Function GetResult(ByVal contents As String) As String Dim result = Text.RegularExpressions.Regex.Match( _ contents, "<result>(?<msg>.*?)</result>", Text.RegularExpressions.RegexOptions.Multiline _ ) Return IIf(result.Success, result.Groups("msg").Value, String.Empty) End Function
見たまんまですね。
RemoveItemParametersクラスにTagNameというプロパティを追加します。 このパラメータが指定されたらタグを削除するようにします。
DeliciousProvider.vb
Private Class RemoveItemParameters Private _bookmark As DeliciousBookmark <Parameter(Position:=0, ValueFromPipeline:=True)> _ Public Property Bookmark() As DeliciousBookmark Get Return _bookmark End Get Set(ByVal value As DeliciousBookmark) _bookmark = value End Set End Property Private _tagName As String <Parameter()> _ Public Property TagName() As String Get Return _tagName End Get Set(ByVal value As String) _tagName = value End Set End Property End Class
RemoveItemメソッドを以下のように変更します。
DeliciousProvider.vb
Protected Overrides Sub RemoveItem(ByVal path As String, ByVal recurse As Boolean) Dim dynaParams = CType(Me.DynamicParameters, RemoveItemParameters) If String.IsNullOrEmpty(dynaParams.TagName) Then Me.Delicious.DeleteBookmark(dynaParams.Bookmark.Url) Else Me.Delicious.DeleteTag(dynaParams.TagName) End If End Sub
これで以下のようにして、タグを削除する事ができます。
PS delicious:\> del -Tag .NET
SetItemParametersクラスにTagというプロパティを追加します。このパラメータが指定されたらタグ名を変更するようにします。
DeliciousProvider.vb
Private Class SetItemParameters Private _bookmark As DeliciousBookmark <Parameter(Position:=0, ValueFromPipeline:=True)> _ Public Property Bookmark() As DeliciousBookmark Get Return _bookmark End Get Set(ByVal value As DeliciousBookmark) _bookmark = value End Set End Property Private _share As SwitchParameter <Parameter(Position:=1)> _ Public Property Share() As SwitchParameter Get Return _share End Get Set(ByVal value As SwitchParameter) _share = value End Set End Property Private _tag As SwitchParameter <Parameter()> _ Public Property Tag() As SwitchParameter Get Return _tag End Get Set(ByVal value As SwitchParameter) _tag = value End Set End Property End Class
SetItemメソッドを以下のように変更します。
DeliciousProvider.vb
Protected Overrides Sub SetItem(ByVal path As String, ByVal value As Object) Dim dynaParams = CType(Me.DynamicParameters, SetItemParameters) If dynaParams.Tag.IsPresent Then Me.Delicious.RenameTag(path, value.ToString()) ElseIf Not value Is Nothing Then Me.Delicious.AddBookmark( _ dynaParams.Bookmark, replace:=True, share:=dynaParams.Share.IsPresent _ ) End If End Sub
これで以下のようにして、タグ名を変更することができます。
PS delicious:\> si -Tag .net .net-framework
これでだいたい当初予定していた機能は実装できたはずです。インターフェースはまだまだ煮詰める余地がありますが、普通に使えると思います。
最後の方は駆け足になってしまいましたが、PowerShellでのプロバイダ開発の実際のやり方がなんとなくつかめたのではないでしょうか。今回は基本的な機能しか実装していませんが、アイデア次第でいくらでも便利にする事ができます。
こういった拡張性の高さもPowerShellのプロバイダ開発の魅力だと思います。皆さんも試しに作ってみて下さい。楽しいですよ!!
PowerShellで作るdeliciousクライアント その4
前回の続きです。
ブックマークの削除
今回はブックマークの削除機能から実装します。
DeliciousクラスにDeleteBookmarkという名前で、引数に削除するブックマークのURLを指定するメソッドを定義します。
Delicious.vb
Sub DeleteBookmark(ByVal url As String) Dim address = "https://api.del.icio.us/v1/posts/delete?url=" + Web.HttpUtility.UrlEncode(url) Dim wc = New Net.WebClient() wc.Credentials = _credential wc.Encoding = Text.Encoding.UTF8 Dim contents = wc.DownloadString(address) Dim resultMsg = GetResultMessage(contents) If resultMsg <> "done" Then Throw New ArgumentException(resultMsg) End Sub Private Shared Function GetResultMessage(ByVal contents As String) As String Dim result = Text.RegularExpressions.Regex.Match( _ contents, "<result code=""(?<msg>.*?)"" />", Text.RegularExpressions.RegexOptions.Multiline _ ) Return IIf(result.Success, result.Groups("msg").Value, String.Empty) End Function
レスポンスのXMLから結果メッセージを取得する処理をGetResultMessageというメソッドに分離しておきました。
Remove-Itemコマンドレットのパラメータクラスをインナークラスで定義しておきます。
DeliciousProvider.vb
Private Class RemoveItemParameters Private _bookmark As DeliciousBookmark <Parameter(Mandatory:=True, Position:=0, ValueFromPipeline:=True)> _ Public Property Bookmark() As DeliciousBookmark Get Return _bookmark End Get Set(ByVal value As DeliciousBookmark) _bookmark = value End Set End Property End Class
後は、RemoveItem(Remove-Itemコマンドレットに対応)メソッドをオーバーライドして、その中でDeleteBookmarkメソッドを呼び出すだけです。
DeliciousProvider.vb
Protected Overrides Sub RemoveItem(ByVal path As String, ByVal recurse As Boolean) Dim dynaParams = CType(Me.DynamicParameters, RemoveItemParameters) Me.Delicious.DeleteBookmark(dynaParams.Bookmark.Url) End Sub Protected Overrides Function RemoveItemDynamicParameters(ByVal path As String, ByVal recurse As Boolean) As Object Return New RemoveItemParameters() End Function
これで以下のようにして、ブックマークを削除する事ができるようになります。
PS delicious:\> dir -Count 10 | del
DeliciousクラスのGetBookmarksメソッドを以下のように変更します。
Delicious.vb
Function GetBookmarks(ByVal start As Integer?, ByVal count As Integer?, ByVal tags As String()) As List(Of DeliciousBookmark) Dim address = "https://api.del.icio.us/v1/posts/all" Dim queries = New Dictionary(Of String, Object)() If start.HasValue Then queries.Add("start", start.Value) If count.HasValue Then queries.Add("results", count.Value) If Not tags Is Nothing AndAlso tags.Length > 0 Then queries.Add("tag", String.Join(" ", tags)) Dim wc = New Net.WebClient() wc.Credentials = _credential wc.Encoding = Text.Encoding.UTF8 Dim contents = wc.DownloadString(JoinQueries(address, queries)) Dim xml = XDocument.Parse(contents) Dim results = From ele In xml.Descendants("post") _ Select New DeliciousBookmark With { _ .Title = ele.Attribute("description"), _ .Url = ele.Attribute("href"), _ .Tags = CType(ele.Attribute("tag"), String).Split(" "), _ .Comment = ele.Attribute("extended"), _ .Date = ele.Attribute("time") _ } Return New List(Of DeliciousBookmark)(results) End Function
「tags」という引数を追加して、タグ名でブックマークを検索できるようにしました。
Get-ChildItemコマンドレットのパラメータクラスにもTagプロパティを追加しておきます。
DeliciousProvider.vb
Private Class GetChildItemsParameters Private _start As Integer = 0 <Parameter(Position:=0)> _ Public Property Start() As Integer Get Return _start End Get Set(ByVal value As Integer) _start = value End Set End Property Private _count As Integer = 20 <Parameter(Position:=1)> _ Public Property Count() As Integer Get Return _count End Get Set(ByVal value As Integer) _count = value End Set End Property Private _tag As String() <Parameter(Position:=2)> _ Public Property Tag() As String() Get Return _tag End Get Set(ByVal value As String()) _tag = value End Set End Property End Class
GetChildItemsメソッドを以下のように変更しておきます。
DeliciousProvider.vb
Protected Overrides Sub GetChildItems(ByVal path As String, ByVal recurse As Boolean) Dim dynaParams = CType(DynamicParameters, GetChildItemsParameters) For Each bookmark In Me.Delicious.GetBookmarks(dynaParams.Start, dynaParams.Count, dynaParams.Tag) WriteItemObject(bookmark, path, False) Next End Sub
PS delicious:\> dir -Tag .net -Count 10 | del
たいした事じゃないですけど、なんだか夢が広がりますね!!
次はタグ関係を一気にやります。
PowerShellで作るdeliciousクライアント その3
前回の続きです。
前回でブックマークの一覧を取得できるようにしましたが、あのままではURLがわかってもブラウザで開くという作業が面倒なので、その辺を簡略化できるように与えられたURLを既定のブラウザで開くコマンドレットを作っておきます。
「Commands」というフォルダを作成して、その中に「BrowseObjectCommand.vb」というファイル名でクラスファイルを追加します。
Commands\BrowseObjectCommand.vb
Namespace Commands <Cmdlet("Browse", "Object")> _ Public Class BrowseObjectCommand Inherits PSCmdlet Private _url As String <Parameter(Mandatory:=True, Position:=0, ValueFromPipelineByPropertyName:=True)> _ Public Property Url() As String Get Return _url End Get Set(ByVal value As String) _url = value End Set End Property Protected Overrides Sub ProcessRecord() Process.Start(Me.Url) End Sub End Class End Namespace
PSCmdletクラスを継承します。コマンドレットである事を示す為にCmdletAttribute属性でマークしておきます。引数にはコマンドレットの動詞と名詞を指定します(Browse-Objectという書式になる)。
コマンドレットが実行されるとProcessRecordメソッドが呼び出されるので、これをオーバーライドして、その中ではURLプロパティの値をシェルで呼び出すだけです。
URLプロパティはParameterAttribute属性でマークして、必須属性(Mandatory:=True)、位置が0(Position:=0)、値をパイプラインから取得し、そのオブジェクトのプロパティと結びつけます(ValueFromPipelineByPropertyName:=True)。
このコマンドレットを使うと以下のようにして、URLをブラウザで開くことができるようになります。
PS delicious:\> dir -Count 1 | Browse-Object
ブックマークの追加・編集
ブックマークの追加・編集機能を実装します。
DeliciousクラスにAddBookmarkという名前で引数にDeliciousBookmark型を受け取るメソッドを定義します。
Delicious.vb
Sub AddBookmark(ByVal bookmark As DeliciousBookmark, _ Optional ByVal share As Boolean = False, Optional ByVal replace As Boolean = False) Dim address = "https://api.del.icio.us/v1/posts/add" Dim queries = New Dictionary(Of String, Object)() If String.IsNullOrEmpty(bookmark.Url) Then Throw New ArgumentException("[Url]を指定して下さい。") If String.IsNullOrEmpty(bookmark.Title) Then Throw New ArgumentException("[Title]を指定して下さい。") queries.Add("url", bookmark.Url) queries.Add("description", bookmark.Title) ' optional If Not String.IsNullOrEmpty(bookmark.Comment) Then queries.Add("extended", bookmark.Comment) If Not bookmark.Tags Is Nothing AndAlso bookmark.Tags.Length > 0 Then queries.Add("tags", String.Join(" ", bookmark.Tags)) End If If Not bookmark.Date Is Nothing Then queries.Add("dt", bookmark.Date.Value.ToString("yyyy-MM-ddTHH:mm:ssZ")) End If queries.Add("shared", IIf(share, "yes", "no")) queries.Add("replace", IIf(replace, "yes", "no")) Dim wc = New Net.WebClient() wc.Credentials = _credential wc.Encoding = Text.Encoding.UTF8 Dim contents = wc.DownloadString(JoinQueries(address, queries)) Dim result = Text.RegularExpressions.Regex.Match( _ contents, "<result code=""(?<msg>.*?)"" />", Text.RegularExpressions.RegexOptions.Multiline _ ) If result.Success Then Dim msg = result.Groups("msg").Value ' replaceを指定してね! If msg <> "done" Then Throw New ArgumentException(msg) End If End Sub Private Shared Function JoinQueries(ByVal address As String, ByVal queries As IDictionary(Of String, Object)) If queries.Count = 0 Then Return address Return address + "?" + String.Join( _ "&", queries.Select( _ Function(q) String.Format("{0}={1}", q.Key, Web.HttpUtility.UrlEncode(q.Value)) _ ).ToArray() _ ) End Function
特に説明する必要は無いでしょう。単に「https://api.del.icio.us/v1/posts/add」というURLにリクエストを送っているだけです。
あと、JoinQueriesメソッドのq.Valueを取得する部分をHttpUtilityクラスを使ってURLエンコードしておきました(System.Webアセンブリへの参照が必要です)。
使うとしたら、以下のようになるでしょうか。
Program.vb
Sub Main() Dim obj = New Delicious(New Net.NetworkCredential("ユーザ名", "パスワード")) Dim bookmark = New DeliciousBookmark With { _ .Title = "google", _ .Url = "http://www.google.co.jp" _ } ' 共有して、上書きする。 obj.AddBookmark(bookmark, share:=True, replace:=True) End Sub
では、このメソッドをプロバイダークラスから呼び出すように変更しましょう。
このメソッドを呼び出すには山ほどパラメータが必要になるので 、(インナークラスで)パラメータクラスを作っておきます。
Delicious.vb
Private Class NewItemParameters Private _url As String <Parameter(Mandatory:=True, Position:=0)> _ Public Property Url() As String Get Return _url End Get Set(ByVal value As String) _url = value End Set End Property Private _tags As String() <Parameter(Position:=1)> _ Public Property Tags() As String() Get Return _tags End Get Set(ByVal value As String()) _tags = value End Set End Property Private _comment As String <Parameter(Position:=2)> _ Public Property Comment() As String Get Return _comment End Get Set(ByVal value As String) _comment = value End Set End Property Private _date As DateTime? <Parameter(Position:=3)> _ Public Property [Date]() As DateTime? Get Return _date End Get Set(ByVal value As DateTime?) _date = value End Set End Property Private _share As SwitchParameter <Parameter()> _ Public Property Share() As SwitchParameter Get Return _share End Get Set(ByVal value As SwitchParameter) _share = value End Set End Property End Class
URL、Tags、Comment、Date、Shareというプロパティを定義しています。それぞれParameterAttribute属性でマークして、必須属性にはMandatory:=TrueをPostion:=nで順番を指定しています。
後はNewItem(New-Itemコマンドレットに対応)メソッドをオーバーライドして、その中でAddBookmarkメソッドを呼び出すだけです。
NewItemDynamicParametersメソッドをオーバーライドして、NewItemParametersクラスのインスタンスを返しておく事をお忘れなく。
Delicious.vb
Protected Overrides Sub NewItem(ByVal path As String, ByVal itemTypeName As String, ByVal newItemValue As Object) Dim dynaParams = CType(Me.DynamicParameters, NewItemParameters) Dim bookmark = New DeliciousBookmark With { _ .Title = path, _ .Url = dynaParams.Url, _ .Tags = dynaParams.Tags, _ .Comment = dynaParams.Comment, _ .Date = dynaParams.Date _ } Me.Delicious.AddBookmark( _ bookmark, replace:=Me.Force, share:=dynaParams.Share.IsPresent _ ) WriteItemObject(bookmark, path, False) End Sub Protected Overrides Function NewItemDynamicParameters(ByVal path As String, ByVal itemTypeName As String, ByVal newItemValue As Object) As Object Return New NewItemParameters() End Function
追加に成功すれば(例外が投げられなければ)、DeliciousBookmarkオブジェクトをパイプラインに出力しておきます。
これで、以下のようにしてブックマークを追加する事ができるようになります。
PS delicious:\> ni google http://www.google.co.jp -Tags google -Comment テストです。 タイトル : google URL : http://www.google.co.jp コメント : テストです。 タグ : {google} 更新日付 : PS delicious:\> dir -Count 1 タイトル : google URL : http://www.google.co.jp/ コメント : テストです。 タグ : {google} 更新日付 : 2009/03/12 4:30:46
次はブックマークを編集できるようにしておきます。
まずはSet-Itemコマンドレットのパラメータクラスをインナークラスで定義しておきます。
Delicious.vb
Private Class SetItemParameters Private _bookmark As DeliciousBookmark <Parameter(Mandatory:=True, Position:=0, ValueFromPipeline:=True)> _ Public Property Bookmark() As DeliciousBookmark Get Return _bookmark End Get Set(ByVal value As DeliciousBookmark) _bookmark = value End Set End Property Private _share As SwitchParameter <Parameter(Position:=1)> _ Public Property Share() As SwitchParameter Get Return _share End Get Set(ByVal value As SwitchParameter) _share = value End Set End Property End Class
BookmarkとShareというプロパティを定義しています。BookmarkプロパティはValueFromPipeline:=Trueにする事でパイプラインから取得できるようにしておきます。
後はSetItemメソッド(Set-Itemコマンドレットに対応)をオーバーライドして、その中でAddBookmarkメソッドをreplace:=Trueで呼び出しています。
Delicious.vb
Protected Overrides Sub SetItem(ByVal path As String, ByVal value As Object) Dim dynaParams = CType(Me.DynamicParameters, SetItemParameters) Me.Delicious.AddBookmark( _ dynaParams.Bookmark, replace:=True, share:=dynaParams.Share.IsPresent _ ) End Sub Protected Overrides Function SetItemDynamicParameters(ByVal path As String, ByVal value As Object) As Object Return New SetItemParameters() End Function
あと、Deliciousプロパティに一部不具合があったので、以下のように修正しておいて下さい。
Delicious.vb
Private _delicious As Delicious Public ReadOnly Property Delicious() As Delicious Get If _delicious Is Nothing Then ' Me.PSDriveInfoプロパティでは駄目!! Dim driveInfo = Me.SessionState.Drive.Current _delicious = New Delicious(driveInfo.Credential.GetNetworkCredential()) End If Return _delicious End Get End Property
これで、以下のようにしてブックマークの情報を編集する事ができるようになります。
PS delicious:\> $bookmark = dir -Count 1 PS delicious:\> $bookmark.Title = "google.co.jp" PS delicious:\> $bookmark | si
更新系のコマンドレットには、他にもRename-ItemとMove-Itemがありますが、これらには対応しない事にします。
次はブックマークの削除機能を実装します。
次回に続く
PowerShellで作るdeliciousクライアント その2
前回の続きです。
ブックマーク一覧の取得 - dirコマンドへの対応
まずはブックマーク一覧の取得機能を実装します。操作的には「delicious」ドライブに移動して、dirコマンド(Get-ChildItemコマンドレット)を実行するとブックマークの一覧が表示されるといった具合です。
テストしやすくする為にdeliciousにアクセスする機能を別クラスとして定義する事にします。
「Delicious.vb」というファイル名でクラスファイルを追加します。
Delicious.vb
Public NotInheritable Class Delicious Private _credential As Net.ICredentials Sub New(ByVal credential As Net.ICredentials) _credential = credential End Sub End Class
deliciousにアクセスする為には認証が必要になるので、コンストラクタでICredentialsのインスタンスを受け取るようにします。
これは後述するPowerShellで使用される認証クラスであるPSCredentialがGetNetworkCredentialメソッドでNetworkCredentialクラスのインスタンスを返すのでこのようにしています。
次に、deliciousブックマークの情報を格納する為のクラスを「DeliciousBookmark.vb」というファイル名のクラスファイルとして追加します。
DeliciousBookmark.vb
<Serializable()> _ <DebuggerStepThrough()> _ Public Class DeliciousBookmark Private _title As String Public Property Title() As String Get Return _title End Get Set(ByVal value As String) _title = value End Set End Property Private _url As String Public Property Url() As String Get Return _url End Get Set(ByVal value As String) _url = value End Set End Property Private _tags As String() Public Property Tags() As String() Get Return _tags End Get Set(ByVal value As String()) _tags = value End Set End Property Private _comment As String Public Property Comment() As String Get Return _comment End Get Set(ByVal value As String) _comment = value End Set End Property Private _date As DateTime Public Property [Date]() As DateTime Get Return _date End Get Set(ByVal value As DateTime) _date = value End Set End Property Public Overrides Function ToString() As String Return String.Format("Title = {0}, Url = {1}, Tags = {2}", Title, Url, String.Join(",", Tags)) End Function End Class
では、ブックマークの一覧を取得する機能を実装します。
GetBookmarksというメソッドで、引数として、取得する開始位置と件数を受け取ります。 返り値としてList(Of DeliciousBookmark)型を返します。
Delicious.vb
Function GetBookmarks(ByVal start As Integer?, ByVal count As Integer?) As List(Of DeliciousBookmark) Dim address = "https://api.del.icio.us/v1/posts/all" Dim queries = New Dictionary(Of String, Object)() If start.HasValue Then queries.Add("start", start.Value) If count.HasValue Then queries.Add("results", count.Value) Dim wc = New Net.WebClient() wc.Credentials = _credential wc.Encoding = Text.Encoding.UTF8 Dim contents = wc.DownloadString(JoinQueries(address, queries)) Dim xml = XDocument.Parse(contents) Dim results = From ele In xml.Descendants("post") _ Select New DeliciousBookmark With { _ .Title = ele.Attribute("description"), _ .Url = ele.Attribute("href"), _ .Tags = CType(ele.Attribute("tag"), String).Split(" "), _ .Comment = ele.Attribute("extended"), _ .Date = ele.Attribute("time") _ } Return New List(Of DeliciousBookmark)(results) End Function Private Shared Function JoinQueries(ByVal address As String, ByVal queries As IDictionary(Of String, Object)) If queries.Count = 0 Then Return address Return address + "?" + String.Join( _ "&", queries.Select(Function(q) String.Format("{0}={1}", q.Key, q.Value)).ToArray() _ ) End Function
処理的には、与えられた引数でURLを組み立て、WebClientクラスを使ってリクエストを送っています。
レスポンスはXMLとして返ってくるので、それをLINQ to XMLを使ってパースを行い、DeliciousBookmark型に変換しています。
使い方としては以下のようになります。
Program.vb
Sub Main() Dim obj = New Delicious(New Net.NetworkCredentials("ユーザ名", "パスワード")) ' 10件だけ取ってくる Dim bookmarks = obj.GetBookmarks(0, 10) ' do something End Sub
次は、このメソッドをプロバイダー側から呼び出すようにします。
そのためにはまず、プロバイダーに認証情報が渡されたかどうかをチェックする必要があります。
認証情報はNewDriveメソッド(New-PSDriveコマンドレットに対応)の引数として渡されるPSDriveInfoクラスのCredentialプロパティからアクセスできます。
DeliciousProvider.vb
Protected Overrides Function NewDrive(ByVal drive As PSDriveInfo) As PSDriveInfo If drive.Credential Is PSCredential.Empty Then Throw New ArgumentException("認証情報[-Credential]を指定して下さい。") Return MyBase.NewDrive(drive) End Function
PSCredentialクラスのEmptyプロパティと比較する事で、認証情報が指定されているかどうかを調べられます。
認証情報を指定してドライブを作る時は以下のようにします。
PS > New-PSDrive delicious Delicious c:\temp -Credential ユーザ名
というコマンドを実行すると、以下のパスワードを入力するダイアログが表示されます。
ここでパスワードを入力して、「OK」ボタンをクリックすればプロバイダーに認証情報を渡す事ができます。
後は、GetChildItem(Get-ChildItemコマンドレットに対応)メソッドをオーバーライドして、その中でGetBookmarksメソッドを呼び出し、WriteItemObjectメソッドでオブジェクトをPowerShellのパイプラインに出力します。
DeliciousProvider.vb
Private _delicious As Delicious Public ReadOnly Property Delicious() As Delicious Get If _delicious Is Nothing Then _delicious = New Delicious(Me.PSDriveInfo.Credential.GetNetworkCredential()) End If Return _delicious End Get End Property Protected Overrides Sub GetChildItems(ByVal path As String, ByVal recurse As Boolean) For Each bookmark In Me.Delicious.GetBookmarks(0, 20) WriteItemObject(bookmark, path, False) Next End Sub
ちなみにDeliciousクラスのインスタンスをプロパティ経由でインスタンス化しているのは、プロバイダークラスはコマンドレットが呼び出される度にインスタンス化が行われる為、クラスのフィールドに格納してもそれが保持され続けるわけでは無いからです。
ここまでで一度ビルドして、PowerShellを起動して「delicious」ドライブにアクセスしてみましょう。
dirコマンドを実行すると以下のように表示されるはずです。
PS delicious:\> dir PSPath : Delicious\Delicious::c:\temp\ PSParentPath : PSChildName : temp PSDrive : delicious PSProvider : Delicious\Delicious PSIsContainer : False Title : Microsoft Semblio - Home Page Url : http://www.microsoft.com/learningspace/semblio/Default.aspx Tags : {Microsoft, Silverlight} Comment : 学習教材を作るサイト Date : 2009/03/11 6:10:36 ....
見づらいですね。Format-Tableなどの整形系のコマンドレットを使えば見易くなりますが、毎回指定するのは面倒なのでオブジェクトの書式設定ファイルを用意します。
psdelicious.format.ps1xml
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <ViewDefinitions> <View> <Name>Tags</Name> <ViewSelectedBy> <TypeName>PSDelicious.DeliciousBookmark</TypeName> </ViewSelectedBy> <ListControl> <ListEntries> <ListEntry> <ListItems> <ListItem> <Label>タイトル</Label> <PropertyName>Title</PropertyName> </ListItem> <ListItem> <Label>URL</Label> <PropertyName>Url</PropertyName> </ListItem> <ListItem> <Label>コメント</Label> <PropertyName>Comment</PropertyName> </ListItem> <ListItem> <Label>タグ</Label> <PropertyName>Tags</PropertyName> </ListItem> <ListItem> <Label>更新日付</Label> <PropertyName>Date</PropertyName> </ListItem> </ListItems> </ListEntry> </ListEntries> </ListControl> </View> </ViewDefinitions> </Configuration>
この書式設定ファイルを以下のようにして、読み込みます。
PS > Update-FormatData -prependPath psdelicious.format.ps1xml
この状態で、再度dirコマンドを実行すると、
PS delicious:\> dir タイトル : Microsoft Semblio - Home Page URL : http://www.microsoft.com/learningspace/semblio/Default.aspx コメント : 学習教材を作るサイト タグ : {Microsoft, Silverlight} 更新日付 : 2009/03/11 6:10:36 ...
とりあえず、これで20件だけですがブックマークの一覧を表示する事ができました。
せっかくなので、オプションを指定して取得する開始位置と件数を指定できるようにしておきましょう。
DeliciousProviderクラスのインナークラスとして、以下のクラスを定義します。
DeliciousProvider.vb
Private Class GetChildItemsParameters Private _start As Integer <Parameter()> _ Public Property Start() As Integer Get Return _start End Get Set(ByVal value As Integer) _start = value End Set End Property Private _count As Integer = 20 <Parameter()> _ Public Property Count() As Integer Get Return _count End Get Set(ByVal value As Integer) _count = value End Set End Property End Class
StartとCountというプロパティを持つだけのクラスです。プロパティはそれぞれParameterAttribute属性でマークしておきます。
GetChildItemsDynamicParametersメソッドをオーバーライドして、このクラスのインスタンスを返しておきます。
DeliciousProvider.vb
Protected Overrides Function GetChildItemsDynamicParameters(ByVal path As String, ByVal recurse As Boolean) As Object Return New GetChildItemsParameters() End Function
DeliciousProvider.vb
Protected Overrides Sub GetChildItems(ByVal path As String, ByVal recurse As Boolean) Dim dynaParams = CType(Me.DynamicParameters, GetChildItemsParameters) For Each bookmark In Me.Delicious.GetBookmarks(dynaParams.Start, dynaParams.Count) WriteItemObject(bookmark, path, False) Next End Sub
これで、以下のように開始位置と取得件数を指定して、dirコマンドを実行することができるようになります。
PS delicious:\> dir -Start 10 -Count 100
次回に続く・・・
PowerShellで作るdeliciousクライアント その1
以前、PowerShellのカスタムプロバイダーという機構を紹介する記事を書きました。
この時は具体的なプロバイダーではなく、プロバイダー作成の概要を説明するという形でしたが、今回いい題材を思いついたのでその事を記事にしたいと思います。
今回作るプロバイダーは、SBS(ソーシャルブックマークサービス)のdeliciousのクライアントです。
このプロバイダーでは以下の操作ができます。
- ブックマークの一覧を取得
- ブックマークの追加・編集
- ブックマークの削除
- タグの一覧を取得
- タグの編集
- タグの削除
全部実装できるかどうかはわかりませんが、とりあえずやっていきましょう。開発言語にはVB.NET 2008(VB9)を使用します。開発環境については以前の記事を参考にして下さい。
注: 開発には、deliciousのアカウントが必要になります。
プロジェクトの作成
Visual Studio 2008を起動して、言語は「Visual Basic」で「クラス ライブラリ」プロジェクトを作成します。プロジェクト名は「PSDelicious」にします。
プロジェクトのプロパティを開いて、参照設定に以下のアセンブリを追加します。
- System.Configuration.dll
- System.Management.Automation.dll
また、以下の名前空間をインポート設定に追加しておきます。
- Microsoft.PowerShell
- Microsoft.PowerShell.Commands
- System.Management.Automation
- System.Management.Automation.Provider
プロジェクトの設定は以上です。
とりあえず動かす
では、とりあえずプロバイダークラスを作って、PowerShellに組み込んで動くようにしておきましょう。
「DeliciousProvider.vb」というファイル名でクラスファイルを追加します。このクラスはContainerCmdletProviderNavigationCmdletProviderクラスから派生させます*1。
DeliciousProvider.vb
<CmdletProvider("Delicious", ProviderCapabilities.ShouldProcess Or ProviderCapabilities.Credentials)> _ Public Class DeliciousProvider Inherits ContainerCmdletProvider Protected Overrides Function IsValidPath(ByVal path As String) As Boolean Return True End Function Protected Overrides Function ItemExists(ByVal path As String) As Boolean Return True End Function Protected Overrides Function IsItemContainer(ByVal path As String) As Boolean Return True End Function End Class
このクラスがプロバイダークラスである事を示す為に、CmdletProviderAttribute属性でマークしておきます。引数としてプロバイダー名とプロバイダーの種類(このプロバイダーでは認証機構を使用するのでCredentialsを指定)を指定しておきます。
IsValidPathメソッドをオーバーライドする必要があるので、とりあえずTrueを返しておきます。
あと、とりあえずこのドライブに移動(Set-Location)できるようにする為に、ItemExistsメソッドとIsItemContainerメソッドをオーバーライドしておきます(いずれもTrueを返すだけ)。
次にこのプロバイダークラスをPowerShellに登録するためのSnapInクラスを作ります。
「DeliciousSnapIn.vb」というファイル名でクラスファイルを追加します。このクラスはPSSnapInクラスから派生させます。
DeliciousSnapIn.vb
Imports System.ComponentModel <RunInstaller(True)> _ Public Class DeliciousSnapIn Inherits PSSnapIn Public Overrides ReadOnly Property Name() As String Get Return "Delicious" End Get End Property Public Overrides ReadOnly Property Description() As String Get Return "deliciousプロバイダーなどを追加するスナップインです。" End Get End Property Public Overrides ReadOnly Property Vendor() As String Get Return "coma2n" End Get End Property End Class
このクラスがインストーラクラスである事を示す為に、RunInstallerAttribute属性でマークしておきます。
以下の読み取り専用のプロパティをオーバーライドしておきます(必須)。
- Name
- Description
- Vendor
ここまでで一度ビルドしておきましょう。
ビルドに成功したらPowerShellを起動して、このプロジェクトの「bin\Debug」フォルダに移動して下さい。そこで以下のコマンドを実行します。
PS Debug> installutil.exe PSDelicious.dll
コマンドが正常に完了すれば、スナップインのインストールが成功です。
では、インストールしたスナップインを読み込んでみましょう。以下のコマンドを実行して下さい。
PS Debug> Add-PSSnapIn Delicious PS Debug> New-PSDrive delicious Delicious c:\temp Name Provider Root CurrentLocation ---- -------- ---- --------------- delicious Delicious c:\temp
Add-PSSnapInコマンドレットで「Delicious」スナップインを読み込み、New-PSDriveコマンドレットで「Delicious」プロバイダーで「delicious」ドライブを作成しています。
「delicious」ドライブに移動できるか試してみ下さい。
PS > cd delicious:\ PS delicious:\>
移動はできますが、その他のコマンドレット(例えばdir)を実行するとエラーになります。
それでは、機能を実装していきましょう。
長くなったので次回に続きます・・・
目次
*1:SessionStateProviderBaseでもいいのですが、このクラスから派生するとPowerShellへのインストール時に何故かエラーが出てしまいます
PowerShellでWebにアクセスしてみよう!
今回はPowerShellを使ってWeb(インターネット)にアクセスする方法を紹介します。
PowerShellは.NET Framework(.NET)をベースにして作られているので、.NETに用意されている豊富なライブラリをそのまま使用することができます。
Webへのアクセスについても.NETのライブラリを使うことになります。基本的にはC#やVB.NETなどの.NET対応言語と同じやり方になります。
WebへのアクセスにはSystem.Net.WebClientクラスかSystem.Net.HttpWebRequestクラスのどちらかを使って行います。
前者は簡単に使えますが細かい制御が出来ません、後者は少々手数が増えますが細かい制御が出来ます。今回はSystem.Net.HttpWebRequestクラスを使用します。
PowerShellからGoogle検索
Webと言えば「Google」と言えるほどの代名詞的な存在のGoogle検索をPowerShellから呼び出してみましょう。
今回作るのは引数として渡した文字列でGoogle検索を行い、その検索結果をコンソールに出力するPowerShellスクリプトです。
PowerShellを起動して、カレントディレクトリに「Find-Google.ps1」というファイルを作り、適当なテキストエディタ(秀丸エディタなど)で開いて下さい。
PS > ni -File Find-Google.ps1 PS > hidemaru Find-Google.ps1
まずは引数の定義を行います。文字列型で「word」という引数を定義しています。
param([string]$word)
次にこの引数に日本語が渡された場合に備えて(URLの一部として渡すため)、ASCIIコードに変換しておきます。
ASCIIコードに変換するには、System.Web.HttpUtilityクラスのUrlEncodeメソッドを使います。
このクラスはSystem.Webアセンブリに定義されているので、System.ReflectionクラスのLoadWithPartialNameメソッドを使ってアセンブリを読み込んでおきます。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word)
次に実際にGoogleにアクセスするためのコードを記述します。
やることはGoogleに検索のクエリを投げて(HTTPリクエスト) 、その結果を文字列として取得する(HTTPレスポンス)だけです。
Googleに投げる検索クエリのフォーマットは以下のようになります。
- http://www.google.co.jp/search?hl=ja&q=検索文字列
検索文字列の部分を引数で渡された文字列に置換します。
では、やってみましょう。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word) $webReq = [Net.HttpWebRequest]::Create("http://www.google.co.jp/search?hl=ja&q=$word") $webReq.Method = "GET" $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)" $webRes = $webReq.GetResponse() $sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $webRes.ContentEncoding) $content = $sr.ReadToEnd() $sr.Close() $webRes.Close()
System.Net.HttpWebRequestクラスのCreateメソッドを使って、引数で渡したURLにHTTPリクエストを送る、このクラスのインスタンスを生成します。
Methodプロパティに「GET」を設定することで、GETメソッドでHTTPリクエストを行います。
UserAgentプロパティには適当なユーザーエージェントを設定しておきます。ユーザーエージェントについては以下のURLを参考にして下さい。
ここまででHTTPリクエストの準備が整いました。後はGetResponseメソッドで実際にリクエストを行い、HTTPレスポンスを取得します。
GetResponseメソッドの返り値として、System.Net.HttpWebResponseクラスのインスタンスが返ってきますので、GetResponseStreamメソッドでHTTPレスポンスの出力ストリームを取得します。
後はこの出力ストリームをSystem.IO.StreamReaderクラスを使って読み出すだけです。出力ストリームとHTTPレスポンスを閉じるの(Closeメソッドの呼び出し)を忘れないで下さい。
ここで「content」という変数の中には、Google検索を使って検索した結果のHTMLが文字列として格納されています。
どのようなHTMLかは以下のURLにアクセスして、ソースを見てみて下さい。
このHTMLの中から検索結果の以下の情報を抽出して、PowerShellのコンソールに出力するわけです。
- タイトル
- URL
しかし、見てわかるとおりこのHTMLはかなり汚いです。タグが閉じられていなかったり、属性の値が引用符で囲まれていなかったりしています。
ある程度ならば少し変更してXHTMLにしてから、System.Xml.XmlDocumentクラスやSystem.Xml.Linq.XDocumentクラスなどを使って簡単に目的の情報を取得するのですが、このHTMLをちゃんとしたXHTMLに変換するのはかなり大変そうです。
なので、正規表現を使ってテキストレベルで情報を抽出します。ちなみに、こういった手法を使ってWebサイトの一部の情報だけを抜き出すことを「スクレイピング」と呼びます。
まずは正規表現で探しやすいように改行を削除しておきます。あと、邪魔なのでscript要素とstyle要素も削除しておきます(これは個人的な好みです)。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word) $webReq = [Net.HttpWebRequest]::Create("http://www.google.co.jp/search?hl=ja&q=$word") $webReq.Method = "GET" $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)" $webRes = $webReq.GetResponse() $sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $webRes.ContentEncoding) $content = $sr.ReadToEnd() $sr.Close() $webRes.Close() # 改行を削除 $content = $content -replace "`n", "" # スクリプトとスタイルを削除 $content = $content -replace "<(script|style)>.*?", ""
検索結果は一件ごとに「<li class=g>」というタグの中に入っていますが、なんとこのタグは閉じられていません。仕方が無いので、「</div>」か「</table>」というタグで閉じると仮定して正規表現で取りに行きます。
条件的には「<li class=g>(?<line>.*?)</(div|table)>」という感じになります。
これをSystem.Text.RegularExpressions.Regexクラスを使って行います(PowerShellには標準で-matchという正規表現用の演算子が用意されていますが、これは一件しか取得できないので使いません)。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word) $webReq = [Net.HttpWebRequest]::Create("http://www.google.co.jp/search?hl=ja&q=$word") $webReq.Method = "GET" $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)" $webRes = $webReq.GetResponse() $sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $webRes.ContentEncoding) $content = $sr.ReadToEnd() $sr.Close() $webRes.Close() # 改行を削除 $content = $content -replace "`n", "" # スクリプトとスタイルを削除 $content = $content -replace "<(script|style)>.*?</(script|style)>", "" $ptrn = New-Object Text.RegularExpressions.Regex("<li class=g>(?<line>.*?)</(div|table)>") $m = $ptrn.Matches($content) $m | % { if($_.Value -match '<h3 class=r><a href=\"(?<url>.*?)\" .*?>(?<title>.*?)</a>') { $obj = New-Object PSObject # タイトル $obj | Add-Member NoteProperty Title ($matches.title -replace "</*\w*>", "") # URL $obj | Add-Member NoteProperty Url $matches.url $obj } }
後は正規表現の条件にマッチした結果をForEach-Objectコマンドレット(%エイリアス)を使って、要素の数だけぐるぐる回します。
その中では「<h3 class=r><a href=\"(?<url>.*?)\" .*?>(?<title>.*?)</a>」という条件でタイトルとURLを取得して、それらをPSObjectクラスのNotePropertyとして追加して、コンソールに出力します。
このスクリプトを実行してみると、以下のように表示されます。
PS > Find-Google powershell Title Url ----- --- Windows PowerShell でのスクリプティング http://www.microsoft.com/japan/technet/scriptcenter/hubs... Windows PowerShell - Wikipedia http://ja.wikipedia.org/wiki/Windows_PowerShell オブジェクト指向なコマンド環境「Powershell」を試してみた... http://d.hatena.ne.jp/nitoyon/20080806/powershell_tutorial 次世代Windowsシェル「Windows PowerShell」を試す(前編) ... http://www.atmarkit.co.jp/fdotnet/special/powershell01/p... Windows PowerShell徹底解説:ITpro http://itpro.nikkeibp.co.jp/article/COLUMN/20061106/252598/ Windows PowerShell 入門(1)−基本操作編:CodeZine http://codezine.jp/article/detail/2067?p=1 PowerShell FAQ http://newpops.wankuma.com/ PowerShell Scripting http://www.roy.hi-ho.ne.jp/mutaguchi/powershell/
これは完全に動作するわけではないですが、Webにアクセスする方法の初歩とのサンプルとしてはいいのではないでしょうか。
まだまだ足らない機能がありますが、この先は皆さんの手で作ってみて下さい。
Spring.NETのロギング設定
Spring.NETのロギング設定についてメモ
Spring.NETではロギングに「log4net」やEnterprise Libraryの「Logging Application Block」なんかを使えるんだけど、それらを直接使うんじゃなくて「Common.Logging」というロギングライブラリを挟んで、実際のロガーを隠蔽している。
で、その設定がちょっとややこしいので書いておく。
まずはロギングを行うクラスを定義する。ロガーにはCommon.Logging.ILogというインターフェースからアクセスできる。
Hoge.vb
Imports Common.Logging Public Class Hoge Private _logger As ILog Public Property Logger() As ILog Get Return _logger End Get Set(ByVal value As ILog) _logger = value End Set End Property Sub Run() Logger.Info("Start Run method.") ' Do something Logger.Info("End Run method.") End Sub End Class
アプリケーション構成ファイルにSpring.NETの設定とCommon.Loggingの設定、ついでにオブジェクト定義*1も追加しておく。
App.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> </sectionGroup> <sectionGroup name="common"> <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" /> </sectionGroup> </configSections> <spring> <context> <resource uri="config://spring/objects" /> </context> <objects xmlns="http://www.springframework.net"> <object name="logger" type="Spring.Objects.Factory.Config.LogFactoryObject, Spring.Core"> <property name="LogName" value="sampleLogger" /> </object> <object name="hoge" type="SpringLoggingSample.Hoge, SpringLoggingSample" singleton="true"> <property name="Logger" ref="logger" /> </object> </objects> </spring> <common> <logging> <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net"> <arg key="configType" value="FILE" /> <arg key="configFile" value="log4net.config" /> </factoryAdapter> </logging> </common> </configuration>
今回は「log4net」を使うので、「factoryAdapter」にCommon.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Netを指定している。
この部分をCommon.Logging.EntLib.EntLibLoggerFactoryAdapter, Common.Logging.EntLibに変えるとロガーを「Logging Application Block」に切り替える事ができる。
「log4net」の設定は別ファイルに書いておくので、configFileプロパティには「log4net.config」というファイル名を指定しておく。
<common> <logging> <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net"> <arg key="configType" value="FILE" /> <arg key="configFile" value="log4net.config" /> </factoryAdapter> </logging> </common>
以下が実際のロガーの設定。ファイルとコンソールに出力する。
log4net.config
<?xml version="1.0" encoding="utf-8" ?> <log4net> <!-- ファイルへのログ出力 --> <appender name="rollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <file value="Log\" /> <appendToFile value="true" /> <rollingStyle value="Date" /> <datePattern value="yyyyMMdd.LOG" /> <staticLogFileName value="false" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date %-5level - %message%newline" /> </layout> </appender> <!-- コンソールへのログ出力 --> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date %-5level - %message%newline" /> </layout> </appender> <logger name="sampleLogger"> <level value="DEBUG" /> <appender-ref ref="rollingLogFileAppender" /> <appender-ref ref="ConsoleAppender" /> </logger> </log4net>
で、最後にエントリーポイント。アプリケーションコンテキストを取ってきて、「hoge」オブジェクトを取りだし、Runメソッドを呼び出すだけ。
Program.vb
Imports Spring.Context Module Program Sub Main() Dim context As IApplicationContext = ConfigurationManager.GetSection("spring/context") Dim obj As Hoge = context.GetObject("hoge") obj.Run() Console.ReadKey() End Sub End Module
実行すると以下のように表示される。
*1:普段は別ファイルに分離するけど、面倒くさいのでくっつけておいた