新規起業者向けの税務講習会をやりますと前にハガキが来ていたので、今日、松戸法人会に行ってきました。
講習会の構成は法人税、消費税の概要と源泉徴収の3点。税務署の人が講師で、参加者は15人くらいでしょうか、9時半から2時間程度で終わりました。
税務署の人に直接聞いて確認したところ、前に保留していた役員報酬に関する届け出は、定額であれば特に税務署に届け出の必要は無く、普通に帳簿を付けて申告するだけ、あとは源泉徴収で行えばいいとの事。なので、法人税は基本的に決算期まで特に何もいらないし、消費税も2年後からだし売上的にも当面考えなくてよさそう。。まずは、源泉徴収ですが、「源泉所得税の納期の特例の承認に関する申請書」を出しているので、半期に一度、今はE-Taxで申告や引落の手続きができるみたいなので、起業に関してはだいぶ整理が付いた気がします。
源泉徴収って、サラリーマン時代は会社がやってくれていたので、ほとんど内容を知りませんでしたが、計算の仕方とかみてると国の方もいろいろと所得再分配とか考えてこうしているのでしょうが、もうちょっとシンプルにならないものでしょうかね?
月別アーカイブ: 2015年11月
P-COMマクロでホスト作業を自動化
ホスト(=メインフレーム)ってまだ結構使っているところが多いように思います。
筆者が知る限り(3270系しか知りませんが)、エミュレータにPersonal Communications(P-COM)かFalconのどちらかを使っていて、どちらもマクロ機能があり、主に画面操作を自動化することが出来ます。
で、ちょっと前に、とあるホストの運用作業を自動化したいという話があったので、P-COMのホスト・アクセス・クラス・ライブラリーを使ったVBスクリプトを作ってみました。P-COMのホスト・アクセス・クラス・ライブラリーについては下記が参考になります。
http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_5.9.0/com.ibm.pcomm.doc/books/html/host_access08.htm?lang=ja
基本的にP-COM起動やホスト接続、終了以外は、画面の座標軸(縦と横)に対して文字を入力したり、指定場所に表示されているのが特定の文字かをチェックするという繰り返しになると思いますので、VBSではそれらをサブルーチンにしてます。
また、スクリプトの引数として、ログインIDとパスワード及び、事前に用意が必要な接続プロファイル(拡張子がwsのやつ)を指定する形になってます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
'2秒*30でタイムアウト判定 Const DEF_MAXCOUNT = 30 Dim oConnMGR, oECLPS, oECLOIA Dim oParam, HUserID, HPassWd, ProFile Dim i, idx Set oParam = WScript.Arguments For idx = 0 To oParam.Count - 1 Select Case idx Case 0 HUserID = UCase( oParam(idx) ) Case 1 HPassWd = UCase( oParam(idx) ) Case 2 ProFile = oParam(idx) End Select Next '''''引数エラー処理割愛 'PCOMオブジェクトの作成 Set oConnMGR = CreateObject("PCOMM.autECLConnMgr") Set oECLPS = CreateObject("PCOMM.autECLPS") Set oECLOIA = CreateObject("PCOMM.autECLOIA") '接続開始 Host_Start If Check_Host("DISP3270") = false Then WScript.Sleep(10000) If Check_Host("DISP3270") = false Then Host_End WScript.Quit(-11) End If End If '(13/01) ログイン [LOGON ユーザーID] + CTL Host_Input 13, 1, "LOGON " & HUserID , "[enter]", 1 '(8/20) パスワード [ ] + CTL 2回 Host_Input 8, 20, HPassWd , "[enter]", 2 Dim iPutLine, InputCmd '画面遷移チェック (4,59) ユーザーIDが表示されるのをチェック If Host_OutputChk(4, 59, Len(HUserID), HUserID) = False Then 'エラー Host_Input 0, 0, "", "[pf3]", 2 End If ・・・以下省略 '終了 'F3 2回で後処理へ戻る Host_Input 0, 0, "", "[pf3]", 2 '(5/27) 印刷せずにデータセットを削除「2」を選択 [2] + CTL Host_Input 4, 27, "2", "[enter]", 1 '(2/01) ログオフ [LOGOFF] + CTL Host_Input 2, 1, "LOGOFF", "[enter]", 1 '切断 Host_End 'ここでスクリプト終了 WScript.Quit(0) '以下サブルーチン '1 ホスト接続開始 Sub Host_Start oConnMGR.autECLConnList.Refresh '事前に取得したコネクションの数だけループして接続を停止する For i = 1 To oConnMGR.autECLConnList.Count oConnMGR.autECLConnList(i).StopCommunication() WScript.Sleep 5000 oConnMGR.StopConnection oConnMGR.autECLConnList(i).Name, "saveprofile=no" WScript.Sleep 5000 Next Dim strCmd strCmd = "profile=" & ProFile & " connname=A" oConnMGR.StartConnection strCmd End Sub '2 ホスト接続終了 Sub Host_End oConnMGR.autECLConnList.Refresh If oConnMGR.autECLConnList.Count > 0 Then oConnMGR.StopConnection "A", "saveprofile=no" WScript.Sleep 3000 End If End Sub '3 ホストセッションの取得(接続タイプ) Function Check_Host(ConnType) For i = 0 To DEF_MAXCOUNT oConnMGR.autECLConnList.Refresh If oConnMGR.autECLConnList.Count > 0 Then Exit For End If WScript.Sleep 2000 Next For i = 1 To oConnMGR.autECLConnList.Count '取得したコネクションの数だけループ If oConnMGR.autECLConnList(i).ConnType = ConnType Then '該当セッションのハンドルからセッションオブジェクトをセット oECLPS.SetConnectionByHandle (oConnMGR.autECLConnList(i).Handle) oECLOIA.SetConnectionByHandle (oConnMGR.autECLConnList(i).Handle) Exit For End If Next Check_Host = True If i > oConnMGR.autECLConnList.Count Then Check_Host = False Else For i = 0 To DEF_MAXCOUNT WScript.Sleep 2000 '処理完了待ち If oECLOIA.InputInhibited = 0 Then If Host_OutputChk(12, 16, 5, "*****") = True Then Exit For End If End If Next '起動確認タイムアウト If i >= DEF_MAXCOUNT Then Host_End WScript.Quit(-2) End If End If End Function '4 ホスト画面への入力(Y座標、X座標、文字列、キー入力、キー入力回数) Sub Host_Input(argY, argX, argStr, argKeyStr, argKeyCnt) Dim iKey If argY <> 0 And argX <> 0 Then oECLPS.SetCursorPos argY, argX oECLPS.SendKeys argStr End If 'エンターキー If argKeyStr <> "" Then For iKey = 1 To argKeyCnt oECLPS.SendKeys argKeyStr For i = 0 To DEF_MAXCOUNT * 10 WScript.Sleep 200 '処理完了待ち If oECLOIA.InputInhibited = 0 Then Exit For End If Next If i >= DEF_MAXCOUNT * 10 Then Host_End WScript.Quit(-2) End If Next End If End Sub '5 画面表示内容のチェック(Y座標、X座標、文字列の長さ、文字列 一致したらtrueを返す Function Host_OutputChk(argY, argX, argLen, argStr) Dim strBuff strBuff = oECLPS.GetTextRect(argY, argX, argY, argX + argLen-1) If Trim(strBuff) = Trim(argStr) Then Host_OutputChk = True Else Host_OutputChk = False End If End Function |
やっかいなのは、あくまで画面操作なので接続とか応答に時間が掛かったり、いろいろ問題が出てきます。
ちょっと雑にSleepをガンガンかましていますが、お察しください。。
JSF2カスタムコンバータでデコードしつつdataTableの背景を変える
今作っているWEBアプリで、画面に一覧を表示して、一覧表示内の項目値によって背景色を変えたいという要件がありました。
で、JSF2のカスタムコンバータを使って、コード値を文言にデコードしつつ、裏でCSSクラスを変えることで背景色が動的に変わる形にしてみました。コンバータは一つで、どのコード体系でデコードするかは、attributeでコンバータに情報を渡す構成になってます、とりあえずは。。
CSS抜粋
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
.listTable th { font-weight: normal; color: #ffffff; background-color: #339933; text-align: center; padding: 1px; border-right-style: solid; border-bottom-style: solid; border-right-color: #CCCCCC; border-bottom-color: #CCCCCC; font-size: 10pt; white-space: nowrap; } .listTable td { padding: 1px; background-color: #ffffff; border-right-style: solid; border-bottom-style: solid; border-right-color: #CCCCCC; border-bottom-color: #CCCCCC; font-size: 11pt; } /* 警告表示 */ .cError { background-color: #ff0000; color: #ffffff; display:block; } /* 一部異常表示 */ .cCaution { background-color: #ffff00; display:block; } /* 対象外表示 */ .cNoTaget { background-color: #a4a4a4; display:block; } /* 通常表示 */ .cDefault { background-color: #ffffff; display:block; } |
xhtmlのdataTable部分抜粋
※wordsはwords.propertiesのファイル側に定義している項目タイトルの文言です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<h:dataTable id="Info" var="list" value="#{listView.itemlist}" styleClass="listTable"> <h:column> <f:facet name="header" >#{words.Label_coName}</f:facet> <h:outputText value="#{list.coName}"/> </h:column> <h:column> <f:facet name="header" >#{words.Label_coCode}</f:facet> <h:outputText value="#{list.coCode}"/> </h:column> <h:column> <f:facet name="header" >#{words.Label_status1}</f:facet> <h:outputText value="#{list.status1}"> <f:converter converterId="statusConverter"/> <f:attribute name="ConvertType" value="1"/> </h:outputText> </h:column> <h:column> <f:facet name="header" >#{words.Label_status2}</f:facet> <h:outputText value="#{list.status2}"> <f:converter converterId="statusConverter"/> <f:attribute name="ConvertType" value="2"/> </h:outputText> </h:column> 以下省略 </h:dataTable> |
カスタムコンバータ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
@FacesConverter(value="statusConverter") public class StatusConverter implements Converter { private static final String DEFVALUE = "X"; private static final String DEFAULT = "cDefault"; private static final String NOTARGET = "cNoTaget"; private static final String CAUTION = "cCaution"; private static final String ERROR = "cError"; // ConvertType 1 OKNG表示 private static final Map<String, String> OkNgState; static { HashMap<String, String> map = new HashMap<String, String>(); map.put(DEFVALUE, "-"); map.put("1", "OK"); map.put("2", "NG"); OkNgState = Collections.unmodifiableMap(map); } // ConvertType 2 現在状況表示 private static final Map<String, String> NowState; static { HashMap<String, String> map = new HashMap<String, String>(); map.put(DEFVALUE, "-"); map.put("0", "処理前"); map.put("1", "仕掛中"); map.put("2", "処理済"); NowState = Collections.unmodifiableMap(map); } // ConvertType 3 結果表示 private static final Map<String, String> ResStatus; static { HashMap<String, String> map = new HashMap<String, String>(); map.put(DEFVALUE, "-"); map.put("0", "正常"); map.put("1", "異常"); map.put("2", "警告"); ResStatus = Collections.unmodifiableMap(map); } //警告表示対象 private static final Map<String, String> Caution; static { HashMap<String, String> map = new HashMap<String, String>(); map.put("異常", ERROR); map.put("警告", CAUTION); map.put("-", NOTARGET); Caution = Collections.unmodifiableMap(map); } @Override public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) { String DispValue=""; String InnerValue = (String) arg2; String StyleClass = DEFAULT; Map<String,String> ConvertMap = null; Map<String,Object> attrMap = arg1.getAttributes(); //f:attribute より変換タイプを取得 String type = (String)attrMap.get("ConvertType"); if (! StringUtils.isNumeric(type) ){ return ""; } switch (Integer.valueOf(type)){ case 1: ConvertMap = OkNgState; break; case 2: ConvertMap = NowState; break; case 3: ConvertMap = ResStatus; break; default: DispValue = InnerValue; ConvertMap = null; break; } //デコード if(ConvertMap != null){ DispValue = ConvertMap.get(InnerValue); //初期値 if (DispValue == null ){ DispValue = ConvertMap.get(DEFVALUE); } } //スタイルクラス指定 if(Caution.get(DispValue) != null){ StyleClass = Caution.get(DispValue); } } //色変 if (arg1.getClass() == HtmlOutputText.class){ HtmlOutputText ui = (HtmlOutputText) arg1; ui.setStyleClass(StyleClass); } return DispValue; } //こっちは関係無し @Override public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) { return null; } } |
ManagedBean抜粋
※一覧表示対象のitemInfoの実装は割愛します。プロパティとゲッタ・セッタがあるだけです。。
1 2 3 4 5 6 7 8 9 10 |
@ManagedBean(name="listView") @ViewScoped public class ListView { private List<itemInfo> itemlist; public List<itemInfo> getitemlist() { //ここでitemlistのデータをセットしてますが省略します return itemlist; } 以下、省略 |
こんな感じで表示できそうなので、別途整理して実装します。
上記例では色が変わるのは行でなくセル単位になります。なお、dataTableの場合、その中のoutputTextはdivに置き換えらえるようなので、コンバータではtdのスタイルを変えているのではなくdivが対象になってます。それをCSSでカバーしているのですが、CSSは得意でないので。。
フランク・ゲーリー展を見て感じたこと
以前から、建築業界とシステム業界というのは共通点が結構あるな~と考えていました。作る対象物は違えど設計、構築といった仕事の進め方もそうだし、大手ベンダー=ゼネコン、多重請負とかの業界構造もです。で、今ミッドタウンの21-21DESIGNSIGHTでやっているフランク・ゲーリー展に行ってみようと思った訳です。
正直、その斬新なデザインは賛否両論あるかと思いますが、筆者が驚いたのは、昔は模型を作っては壊してを繰り返して設計していたのが、今ではデザインコンセプトを模型で確認した後はコンピューターで3Dモデリングし、決められた予算範囲内でデザインを妥協することなく最適な資材構成となるようシミュレーションを繰り返して決定しているという事です。それにより、建築資材の数や搬入時期までを決める事で施工時のコストまでも最適化し、複数の施工業者が見積もっても誤差は0.5%とか。スゲエ!と思ってしまいました。ようするに、それを実現できるソフトウェアを開発した人であるということなんです。建築業界はまったく解らないですが、こういうやり方は一般的なのでしょうかね?国立競技場とかこの人に任せればよかったのにと思いますけど。。
で、建築とシステム開発の違いについて、フランク・ゲーリー展を見た後に筆者が感じた所ですが、建築では、住宅からビルやホールとか構築対象物の規模が解りやすいという事と、建築での施工業者なる位置付けの仕事がシステム開発に存在しているか?という事です。
システムの規模って、誰が見ても大規模なものもあれば、それ程でもないものってかなりあるように思いますが、その規模の判断が人それぞれな気がします。大抵大手ベンダーに依頼すれば大規模と見なされていませんかね?クリティカルなシステムだって、例えば5種類のデータしか扱わないのであればそれは対した複雑さは無いはずなんです。また、扱うデータ量が膨大だとしても実装されたシステムを多数のサーバに配置するだけなので、インフラ含めアプリもちゃんと設計されていれば開発自体のコストは何も変わりません。
あと、施工業者という点では、所謂上流下流といった分け方をしている場合、プログラマーは施工業者みたいなものだという考え方を持っている人もいると思いますが、それを当てはめた方がよいケースは極少数でしかないと筆者は考えます。どちらかというとプログラマーは施工業者というより建築資材供給者の位置づけに近いと思いますが、システムでは1つの設計=1つのプログラムみたいなものです。同じものを量産するという概念がありません。すごく似ている機能が多数存在する事もありますが、それを一つまたは最小限にできないなら設計に問題があるはず。であれば、何を作るべきかを把握しているであろう設計者自身がプログラムを書く方が品質が高く、また分業する事によるコミュニケーションコストも最小化できるはずです。
システム開発に施工業者が存在していないと考えの元では、建築家フランク・ゲーリーにとってのデザイン活動範囲は、システム開発全ての活動範囲と一致しており、構築対象物は違えど、システム開発におけるプログラミングはデザインという活動範囲に含まれているという事になります。施工業者的であるのは多数のサーバで構成された場合におけるインフラ側の仕事だけではないでしょうか?
筆者の立場では、今は一人か極少数で開発をしているので、これは注文住宅を自身で設計・施工している工務店の大工みたいなものですかね。こういう戸建レベルの規模の開発案件って結構存在しているはずなんですが、欲しているものの実際の規模と受注側が考えているその規模のズレを注文側が感じるとる事を出来るかという所になります。でも、大規模と言われた物を小規模であると主張するのは結構タイヘンなんですよね。発言に対し全てのリスクを引き受ける気合い!がなければ、なかなか言える事では無いかもしれません。別の方法として、一つのシステムを戸建てレベルの集合体と分割することも出来ますけど、構築後の保守フェーズまで考慮するとこちらもタイヘンかなあ。。
ganymed ssh2を使ってダウンロード画面を作る
今作っているWebアプリケーションでサーバのログファイルをダウンロードしたいという要件があり、そこでganymedを使ってみることにしました。アプリケーションをデプロイしているサーバとは別に複数存在するサーバ上にあるログファイルを画面から選択してクリックでダウンロードという流れです。
なお、サーバとログファイルが格納されているディレクトリ情報は別途DB等から取る形にして、下記はSFTPでファイルを取得する箇所の抜粋です。
POM.xml ganymed指定
1 2 3 4 5 |
<dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-ssh2</artifactId> <version>262</version> </dependency> |
画面に表示するログファイル情報
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class LogFile { /** ファイル名 */ private String fileName; /** 更新日時 */ private String upDateTime; /** ディレクトリ */ private String directory; /** サイズ */ private long fileSize; /** ホスト名 */ private String hostName; 以下、ゲッタとセッタは割愛 |
SFTP処理
※サーバは一般的なID・パスワード認証です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.SFTPv3Client; import ch.ethz.ssh2.SFTPv3DirectoryEntry; import ch.ethz.ssh2.SFTPv3FileHandle; /** * SFTPファイルアクセスクラス by Ganymed SSH-2 * */ @Stateless public class SFTPFileAccess { private final int PORT = 22; private SFTPv3Client sftp = null; private Connection conn = null; /** * リモートホストへログインします * @param HostName リモートホスト名 * @param User ログインユーザー名 * @param Password ログインパスワード * @throws IOException * * */ public boolean loginHost(String HostName, String User, String Password) throws IOException{ conn = new Connection(HostName, PORT); conn.connect(); if (!conn.authenticateWithPassword(User, Password)) { //エラー return false; } return true; } /** * リモートホスト接続をクローズします * @throws IOException * * */ public void logOutHost() throws IOException{ if(conn != null){ conn.close(); } return; } /** * リモートホスト上の指定ディレクトリのファイル一覧を取得する * @param String RemoteDir ディレクトリ名 * @param List<LogFile> セットするファイル一覧 * @throws IOException * */ public void getSFTPList(String RemoteDir, List<LogFile> FileList) throws IOException { if(conn == null){ return; } sftp = new SFTPv3Client(conn); List<SFTPv3DirectoryEntry> DirList = sftp.ls(RemoteDir); if ( FileList == null ){ FileList = new ArrayList<LogFile>(); } for (SFTPv3DirectoryEntry Entry : DirList ){ //ディレクトリ以外 if (!Entry.longEntry.startsWith("d") ){ LogFile file = new LogFile(); file.setDirectory(RemoteDir); file.setFileName(Entry.filename); file.setHostName(conn.getHostname()); file.setFileSize(Entry.attributes.size); //ファイル更新時間 Date tm = new Date(Entry.attributes.mtime * 1000L); file.setUpDateTime(DateUtils.format(tm, DateUtils.FORMAT_YYYY_MM_DD_HH_MM_SS)); FileList.add(file); } } return; } /** * SFTPファイル受信 * @param remoteFile 受信元ファイル * @param stream 出力ストリーム * * */ public void getFile( String remoteFile, OutputStream stream) throws IOException{ if(conn == null){ return; } if(sftp == null){ sftp = new SFTPv3Client(conn); } long remoteFileSize = sftp.stat(remoteFile).size; // open remote file with read only SFTPv3FileHandle sftpFileHandle = sftp.openFileRO(remoteFile); BufferedOutputStream bos = new BufferedOutputStream(stream); byte[] buffer = new byte[1024]; long offset = 0; int readLength = 0; while( (readLength = sftp.read(sftpFileHandle, offset, buffer, 0, buffer.length)) != -1){ bos.write(buffer, 0, readLength); offset += readLength; } // flush & Close bos.flush(); bos.close(); sftp.closeFile(sftpFileHandle); // compare if (offset == remoteFileSize) { } else { System.out.println("failed: file size is different from remote. remote:" + remoteFileSize+", local:"+offset); } return; } } |
画面処理
※HttpServletResponseのOutputStreamに直ファイルを突っ込むので、一時ファイルを置いたりしません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/** * ログダウンロード画面 * */ @ManagedBean(name="LogDownload") @SessionScoped public class LogDownload implements Serializable { private static final long serialVersionUID = 1L; protected static Logger logger = LogManager.getLogger(); @EJB private SFTPFileAccess ftp; private String selectedServer; private List<LogFile> fileList; public LogDownload() { fileList = new ArrayList<LogFile>(); } public List<LogFile> getFileList() { return fileList; } public void setFileList(List<LogFile> fileList) { this.fileList = fileList; } public String getSelectedServer() { return this.selectedServer; } public void setSelectedServer(String selectedServer) { this.selectedServer = selectedServer; } /** * ディレクトリ選択時にファイル一覧を取得 * */ public void getList(){ try { Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); String fileDir = params.get("fileDir"); ftp.loginHost(selectedServer, dao.getServerLoginUser(selectedServer), dao.getServerLoginPasswd(selectedServer)); fileList.clear(); ftp.getSFTPList(fileDir,fileList); } catch (IOException e) { e.printStackTrace(); } } /** * ファイルダウンロード * */ public void getFile(){ Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); String fileName = params.get("fileName"); String fileDir = params.get("fileDir"); try { HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance() .getExternalContext().getResponse(); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); OutputStream a = response.getOutputStream(); ftp.getFile(fileDir +"/" + fileName , a); FacesContext.getCurrentInstance().responseComplete(); } catch (IOException e) { e.printStackTrace(); } } } |
xhtml
※ディレクトリリストの箇所は、上記ソース上割愛しています。
ディレクトリリストの左に置いたボタンクリックで、ファイル一覧を取得し、ファイル一覧のファイル名クリックでダウンロードします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<h:form id="listForm"> <h:dataTable var="dirlist" value="#{LogDownload.directoryList}" rowIndexVar="rowIndex" class="table"> <h:column> <f:facet name="header" >#{words.Label_LogList}</f:facet> <h:commandLink value="#{dirlist.INFO_VALUE}" actionListener="#{LogDownload.downLoad}"> <f:attribute name="directory" value="#{dirlist.INFO_VALUE}"/> </h:commandLink> </h:column> <h:column> <h:commandButton id="listget" action="#{LogDownload.getList}" value="#{words.Label_FileListGet}"> <f:param name="fileDir" value="#{dirlist.INFO_VALUE}" /> <f:ajax render=":listForm:loglist"/> </h:commandButton> </h:column> </h:dataTable> <h:dataTable id="loglist" var="list" value="#{LogDownload.fileList}" styleClass="listTable"> <h:column><f:facet name="header" >ファイル名</f:facet> <h:commandLink id="filelistName" value="#{list.fileName}" action="#{LogDownload.getFile}"> <f:param name="fileName" value="#{list.fileName}" /> <f:param name="fileDir" value="#{list.directory}" /> </h:commandLink> </h:column> <h:column><f:facet name="header">ファイルサイズ</f:facet> <h:outputText value="#{list.fileSize}"> <f:convertNumber groupingUsed="true" /> </h:outputText> </h:column> <h:column><f:facet name="header" >ファイル更新日時</f:facet> <h:outputText value="#{list.upDateTime}"/> </h:column> </h:dataTable> </h:form> |
エラーハンドリングやサーバの切断、ファイルサイズの数値右寄せ等、細かい箇所がまだ出来ていませんが、とりあえず動きそうです。
最初、ログファイル情報のプロパティ名が先頭大文字とかだと、JSFでエラーになりちょっとハマりました。。
レルムの代わりにJASPICで
今、企業内で少人数が使用するWEBアプリケーションをWebLogicで動かす前提で作ってますが、ユーザー認証についてどうしようかと要件を確認したところ、少人数使用のアプリなのでLDAP無し、WebLogicに登録・管理したくないのでWeblogicのレルムも使わないでとのこと。となると従来のDBにユーザーテーブル作ってユーザー管理画面とかも作る感じになります。
ユーザーには数パターンあるうちの1つの権限を付与すれば使える画面と使えない画面が決まるので、基本的にURLで制御できる範囲です。で、いろいろ調べましたが、下記を参考にして、JASPIC(Java Authentication Service Provider Interface for Containers)というJavaEE7仕様に含まれているものを使ってみる事にしました。
http://arjan-tijms.omnifaces.org/2012/11/implementing-container-authentication.html
ここのSETP5にあるServerAuthModule実装のvalidateRequestに認証処理を書き込む形のようです。
下記のサンプルソースも参考になりました。
https://github.com/erik-wramner/YubikeyAuth/tree/master/yubi-jaspic-example
但し、WebLogicで動かす場合は、web.xmlにだけ設定すればよいだけでなく、weblogic.xmlにも同じような設定が必要みたいです。
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<security-role> <description>管理者</description> <role-name>Managers</role-name> </security-role> <security-role> <description>利用者</description> <role-name>Users</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>manager page</web-resource-name> <url-pattern>/manage/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>Managers</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>user page</web-resource-name> <url-pattern>/user/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>Users</role-name> </auth-constraint> </security-constraint> |
weblogic.xml
1 2 3 4 5 6 7 8 9 |
<wls:security-role-assignment> <wls:role-name>Managers</wls:role-name> <wls:principal-name>Managers</wls:principal-name> </wls:security-role-assignment> <wls:security-role-assignment> <wls:role-name>Users</wls:role-name> <wls:principal-name>Users</wls:principal-name> </wls:security-role-assignment> |
デプロイするとそのインスタンス全体に影響し、開発環境では管理サーバで動かしているので管理コンソールに入れなくなります。。