001 /*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016 package org.opengion.fukurou.process;
017
018 import org.opengion.fukurou.util.Argument;
019
020 import org.opengion.fukurou.util.StringUtil;
021 import org.opengion.fukurou.util.HybsEntry ;
022 import org.opengion.fukurou.util.LogWriter;
023
024 import java.util.Hashtable;
025 import java.util.List;
026 import java.util.ArrayList;
027 import java.util.Map ;
028 import java.util.LinkedHashMap ;
029
030 import javax.naming.Context;
031 import javax.naming.NamingEnumeration;
032 import javax.naming.NamingException;
033 import javax.naming.directory.DirContext;
034 import javax.naming.directory.InitialDirContext;
035 import javax.naming.directory.SearchControls;
036 import javax.naming.directory.SearchResult;
037 import javax.naming.directory.Attribute;
038 import javax.naming.directory.Attributes;
039
040 /**
041 * Process_LDAPReaderは、LDAPから読み取った?容を?LineModel に設定後?
042 * 下流に渡す?FirstProcess インターフェースの実?ラスです?
043 *
044 * LDAPから読み取った?容より、LineModelを作?し?下?プロセスチェインは?
045 * チェインして?ため、データは上流から下流へと渡されます?)に渡します?
046 *
047 * 引数??中にスペ?スを含??合?、ダブルコー??ション("") で括って下さ??
048 * 引数??の ?』?前後には、スペ?スは挟めません。??key=value の様に
049 * 繋げてください?
050 *
051 * @og.formSample
052 * Process_LDAPReader -attrs=uid,cn,officeName,ou,mail,belongOUID -orderBy=uid -filter=(&(objectClass=person)(|(belongOUID=61200)(belongOUID=61100)))
053 *
054 * [ -initctx=コン?ストファクトリ ] ??期コン?ストファクトリ (初期値:com.sun.jndi.ldap.LdapCtxFactory)
055 * [ -providerURL=サービスプロバイ? ] ?サービスプロバイ? (初期値:ldap://ldap.opengion.org:389)
056 * [ -entrydn=取得?の名前 ] ?属?の取得?のオブジェクト?名前 (初期値:cn=inquiry-sys,o=opengion,c=JP)
057 * [ -password=取得?のパスワー? ] ?属?の取得?のパスワー? (初期値:******)
058 * [ -searchbase=コン?スト?ース? ] ?検索するコン?スト?ベ?ス?(初期値:soouid=employeeuser,o=opengion,c=JP)
059 * [ -searchScope=検索? ] ?検索?。?OBJECT』?ONELEVEL』?SUBTREE』?どれか(初期値:SUBTREE)
060 * [ -timeLimit=検索制限時? ] ?結果が返されるまでのミリ秒数? の場合無制?初期値:0)
061 * [ -attrs=属?の識別? ] ?エントリと?に返される属?の識別子?null の場合すべての属?
062 * [ -columns=属?のカラ? ] ?属?の識別子に対する別名?識別子と同じ場合??』?みで区??
063 * [ -maxRowCount=?検索数 ] ?最大検索数(初期値:0[無制限])
064 * [ -match_XXXX=正規表現 ] ?指定?カラ?正規表現で??時?み処? -match_LANG=ABC=[a-zA-Z]* など?
065 * [ -filter=検索条件 ] ?検索する LDAP に?する条件
066 * [ -referral=REFERAL ] ?ignore/follow/throw
067 * [ -display=[false/true] ] ?結果を標準?力に表示する(true)かしな?false)?初期値:false[表示しない])
068 * [ -debug=[false/true] ] ?デバッグ??を標準?力に表示する(true)かしな?false)?初期値:false[表示しない])
069 *
070 * @version 4.0
071 * @author Kazuhiko Hasegawa
072 * @since JDK5.0,
073 */
074 public class Process_LDAPReader extends AbstractProcess implements FirstProcess {
075 private static final String INITCTX = "com.sun.jndi.ldap.LdapCtxFactory";
076 private static final String PROVIDER = "ldap://ldap.opengion.org:389";
077 private static final String PASSWORD = "password";
078 private static final String SEARCH_BASE = "soouid=employeeuser,o=opengion,c=JP";
079 private static final String ENTRYDN = "cn=inquiry-sys,o=opengion,c=JP"; // 4.2.2.0 (2008/05/10)
080 private static final String REFERRAL = ""; // 5.6.7.0 (2013/07/27)
081
082 // 検索?。OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか 1 つ
083 private static final String[] SCOPE_LIST = new String[] { "OBJECT","ONELEVEL","SUBTREE" };
084 private static final String SEARCH_SCOPE = "SUBTREE";
085
086 private static final long COUNT_LIMIT = 0; // 返すエントリの?数? の場合?フィルタを?すエントリをすべて返す
087
088 private String filter = null; // "employeeNumber=87019";
089 private int timeLimit = 0; // 結果が返されるまでのミリ秒数? の場合?無制?
090 private String[] attrs = null; // エントリと?に返される属?の識別子?null の場合?すべての属?を返す。空の場合?属?を返さな?
091 private String[] columns = null; // 属?の識別子に対する、別名?識別子と同じ場合?、?,』?みで区??
092 private static final boolean RETURN_OBJ_FLAG = false; // true の場合?エントリの名前にバインドされたオブジェクトを返す。false 場合?オブジェクトを返さな?
093 private static final boolean DEREF_LINK_FLAG = false; // true の場合?検索中にリンクを間接参?する
094
095 private int executeCount = 0; // 検索/実行件数
096 private int maxRowCount = 0; // ?検索数(0は無制?
097
098 // 3.8.0.9 (2005/10/17) 正規表現マッ?
099 private String[] matchKey = null; // 正規表現
100 private boolean display = false; // 表示しな?
101 private boolean debug = false; // 5.7.3.0 (2014/02/07) ????
102
103 private static final Map<String,String> mustProparty ; // ?プロパティ???チェ?用 Map
104 private static final Map<String,String> usableProparty ; // ?プロパティ?整合?チェ? Map
105
106 private NamingEnumeration<SearchResult> nameEnum = null; // 4.3.3.6 (2008/11/15) Generics警告対?
107 private LineModel newData = null;
108 private int count = 0;
109
110 static {
111 mustProparty = new LinkedHashMap<String,String>();
112 mustProparty.put( "filter", "検索条件(??) ? (&(objectClass=person)(|(belongOUID=61200)(belongOUID=61100)))" );
113
114 usableProparty = new LinkedHashMap<String,String>();
115 usableProparty.put( "initctx", "初期コン?ストファクトリ? +
116 CR + " (初期値:com.sun.jndi.ldap.LdapCtxFactory)" );
117 usableProparty.put( "providerURL", "サービスプロバイ? (初期値:ldap://ldap.opengion.org:389)" );
118 usableProparty.put( "entrydn", "属?の取得?のオブジェクト?名前? +
119 CR + " (初期値:cn=inquiry-sys,o=opengion,c=JP)" );
120 usableProparty.put( "password", "属?の取得?のパスワー?初期値:******)" );
121 usableProparty.put( "searchbase", "検索するコン?スト?ベ?ス名?" +
122 CR + " (初期値:soouid=employeeuser,o=opengion,c=JP)" );
123 usableProparty.put( "searchScope", "検索?。?OBJECT』?ONELEVEL』?SUBTREE』?どれか? +
124 CR + " (初期値:SUBTREE)" );
125 usableProparty.put( "timeLimit", "結果が返されるまでのミリ秒数? の場合無制?初期値:0)" );
126 usableProparty.put( "attrs", "エントリと?に返される属?の識別子?null の場合すべての属?" );
127 usableProparty.put( "columns", "属?の識別子に対する別名?識別子と同じ場合??』?みで区?? );
128 usableProparty.put( "maxRowCount", "?検索数(0は無制? (初期値:0)" );
129 usableProparty.put( "match_", "??カラ?正規表現で??時?み処? +
130 CR + " ( -match_LANG=ABC=[a-zA-Z]* など?" );
131 usableProparty.put( "display", "結果を標準?力に表示する(true)かしな?false)? +
132 CR + "(初期値:false:表示しな?" );
133 usableProparty.put( "debug", "????を標準?力に表示する(true)かしな?false)? +
134 CR + "(初期値:false:表示しな?" ); // 5.7.3.0 (2014/02/07) ????
135 }
136
137 /**
138 * ?ォルトコンストラクター?
139 * こ?クラスは、動??されます??ォルトコンストラクターで?
140 * super クラスに対して、?な初期化を行っておきます?
141 *
142 */
143 public Process_LDAPReader() {
144 super( "org.opengion.fukurou.process.Process_LDAPReader",mustProparty,usableProparty );
145 }
146
147 /**
148 * プロセスの初期化を行います?初めに??、呼び出されます?
149 * 初期処?ファイルオープン??オープン?に使用します?
150 *
151 * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対?
152 * @og.rev 5.3.4.0 (2011/04/01) StringUtil.nval ではなく?getProparty の 初期値機?を使?
153 * @og.rev 5.6.7.0 (2013/07/27) REFERRAL対?
154 *
155 * @param paramProcess ??タベ?スの接続???などを持って?オブジェク?
156 */
157 public void init( final ParamProcess paramProcess ) {
158 Argument arg = getArgument();
159
160 String initctx = arg.getProparty("initctx " ,INITCTX );
161 String providerURL = arg.getProparty("providerURL" ,PROVIDER );
162 String entrydn = arg.getProparty("entrydn" ,ENTRYDN ); // 4.2.2.0 (2008/05/10)
163 String password = arg.getProparty("password" ,PASSWORD );
164 String searchbase = arg.getProparty("searchbase" ,SEARCH_BASE );
165
166 String searchScope = arg.getProparty("searchScope" ,SEARCH_SCOPE , SCOPE_LIST );
167 // timeLimit = StringUtil.nval( arg.getProparty("timeLimit") ,timeLimit );
168 // maxRowCount = StringUtil.nval( arg.getProparty("maxRowCount") ,maxRowCount );
169 timeLimit = arg.getProparty("timeLimit",timeLimit ); // 5.3.4.0 (2011/04/01)
170 maxRowCount = arg.getProparty("maxRowCount",maxRowCount ); // 5.3.4.0 (2011/04/01)
171 display = arg.getProparty("display",display);
172 debug = arg.getProparty("debug",debug); // 5.7.3.0 (2014/02/07) ????
173 // if( debug ) { println( arg.toString() ); } // 5.7.3.0 (2014/02/07) ????
174
175 String referral = arg.getProparty("referral",REFERRAL); // 5.6.7.0 (2013/07/27)
176
177 // 属?配?を取得?なければゼロ配?
178 attrs = StringUtil.csv2Array( arg.getProparty("attrs") );
179 if( attrs.length == 0 ) { attrs = null; }
180
181 // 別名定義配?を取得?なければ属?配?をセ?
182 columns = StringUtil.csv2Array( arg.getProparty("columns") );
183 if( columns.length == 0 ) { columns = attrs; }
184
185 // 属?配?が存在し?属?定義数と別名?列数が異なれ?エラー
186 // 以降?、attrs == null か?属?定義数と別名?列数が同じ?ず?
187 if( attrs != null && attrs.length != columns.length ) {
188 String errMsg = "attrs と columns で??引数の数が異なります?" +
189 " attrs=[" + arg.getProparty("attrs") + "] , columns=[" +
190 arg.getProparty("columns") + "]" ;
191 throw new RuntimeException( errMsg );
192 }
193
194 // 3.8.0.9 (2005/10/17) 正規表現マッ?
195 HybsEntry[] entry = arg.getEntrys( "match_" );
196 int len = entry.length;
197 matchKey = new String[columns.length]; // 正規表現
198 for( int clm=0; clm<columns.length; clm++ ) {
199 matchKey[clm] = null; // 判定チェ?有無の初期?
200 for( int i=0; i<len; i++ ) {
201 if( columns[clm].equalsIgnoreCase( entry[i].getKey() ) ) {
202 matchKey[clm] = entry[i].getValue();
203 }
204 }
205 }
206
207 filter = arg.getProparty( "filter" ,filter );
208
209 Hashtable<String,String> env = new Hashtable<String,String>();
210 env.put(Context.INITIAL_CONTEXT_FACTORY, initctx);
211 env.put(Context.PROVIDER_URL, providerURL);
212 // 3.7.1.1 (2005/05/31)
213 // if( password != null && password.length() > 0 ) {
214 env.put(Context.SECURITY_CREDENTIALS, password);
215 // }
216
217 // 4.2.2.0 (2008/05/10) entrydn 属?の追?
218 // if( entrydn != null && entrydn.length() > 0 ) {
219 env.put(Context.SECURITY_PRINCIPAL , entrydn);
220 // }
221
222 env.put( Context.REFERRAL, referral ); // 5.6.7.0 (2013/07/27)
223
224 try {
225 DirContext ctx = new InitialDirContext(env);
226 SearchControls constraints = new SearchControls(
227 changeScopeString( searchScope ),
228 COUNT_LIMIT ,
229 timeLimit ,
230 attrs ,
231 RETURN_OBJ_FLAG ,
232 DEREF_LINK_FLAG
233 );
234
235 nameEnum = ctx.search(searchbase, filter, constraints);
236
237 } catch ( NamingException ex ) {
238 String errMsg = "NamingException !"
239 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正
240 throw new RuntimeException( errMsg,ex );
241 }
242 }
243
244 /**
245 * プロセスの終?行います??に??、呼び出されます?
246 * 終???ファイルクローズ??クローズ?に使用します?
247 *
248 * @param isOK ト?タルで、OK?たかど?[true:成功/false:失敗]
249 */
250 public void end( final boolean isOK ) {
251 try {
252 if( nameEnum != null ) { nameEnum.close() ; nameEnum = null; }
253 }
254 catch ( NamingException ex ) {
255 String errMsg = "?スコネクトすることが?来ません?;
256 throw new RuntimeException( errMsg,ex );
257 }
258 }
259
260 /**
261 * こ???タの処?おいて、次の処?出来るかど?を問?わせます?
262 * こ?呼び出し1回毎に、次の??タを取得する準備を行います?
263 *
264 * @return 処?きる:true / 処?きな?false
265 */
266 public boolean next() {
267 try {
268 return nameEnum != null && nameEnum.hasMore() ;
269 }
270 catch ( NamingException ex ) {
271 String errMsg = "ネクストすることが?来ません?;
272 throw new RuntimeException( errMsg,ex );
273 }
274 }
275
276 /**
277 * ??に?行データである LineModel を作?しま?
278 * FirstProcess は、次?処?チェインして???の行データ?
279 * 作?して、後続? ChainProcess クラスに処?ータを渡します?
280 *
281 * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対?
282 *
283 * @param rowNo 処?の行番号
284 *
285 * @return 処?換後?LineModel
286 */
287 public LineModel makeLineModel( final int rowNo ) {
288 count++ ;
289 try {
290 if( maxRowCount > 0 && maxRowCount <= executeCount ) { return null ; }
291 SearchResult sRslt = nameEnum.next(); // 4.3.3.6 (2008/11/15) Generics警告対?
292 Attributes att = sRslt.getAttributes();
293
294 if( newData == null ) {
295 newData = createLineModel( att );
296 if( display ) { println( newData.nameLine() ); }
297 }
298
299 for( int i=0; i<attrs.length; i++ ) {
300 Attribute attr = att.get(attrs[i]);
301 if( attr != null ) {
302 NamingEnumeration<?> vals = attr.getAll(); // 4.3.3.6 (2008/11/15) Generics警告対?
303 StringBuilder buf = new StringBuilder();
304 // if( vals.hasMore() ) { buf.append( vals.next() ) ;}
305 if( vals.hasMore() ) { getDataChange( vals.next(),buf ) ;} // 4.2.2.0 (2008/05/10)
306 while ( vals.hasMore() ) {
307 buf.append( "," ) ;
308 // buf.append( vals.next() ) ;
309 getDataChange( vals.next(),buf ) ; // 4.2.2.0 (2008/05/10)
310 }
311 // 3.8.0.9 (2005/10/17) 正規表現マッチしなければ、スルーする?
312 String value = buf.toString();
313 String key = matchKey[i];
314 if( key != null && value != null && !value.matches( key ) ) {
315 return null;
316 }
317 newData.setValue( i, value );
318 executeCount++ ;
319 }
320 }
321
322 newData.setRowNo( rowNo );
323 if( display ) { println( newData.dataLine() ); }
324 }
325 catch ( NamingException ex ) {
326 String errMsg = "??タを??きませんでした?" + rowNo + "]件目";
327 if( newData != null ) { errMsg += newData.toString() ; }
328 throw new RuntimeException( errMsg,ex );
329 }
330 return newData;
331 }
332
333 /**
334 * LDAPから取得したデータの変換を行います?
335 *
336 * 主に、バイト??byte[]) オブジェクト?場合???に戻します?
337 *
338 * @og.rev 4.2.2.0 (2008/05/10) 新規追?
339 *
340 * @param obj 主にバイト?列オブジェク?
341 * @param buf ??StringBuilder
342 *
343 * @return ??タを追??StringBuilder
344 */
345 private StringBuilder getDataChange( final Object obj, final StringBuilder buf ) {
346 if( obj == null ) { return buf; }
347 else if( obj instanceof byte[] ) {
348 // buf.append( new String( (byte[])obj,"ISO-8859-1" ) );
349 byte[] bb = (byte[])obj ;
350 char[] chs = new char[bb.length];
351 for( int i=0; i<bb.length; i++ ) {
352 chs[i] = (char)bb[i];
353 }
354 buf.append( chs );
355 }
356 else {
357 buf.append( obj ) ;
358 }
359
360 return buf ;
361 }
362
363 /**
364 * ?で使用する LineModel を作?します?
365 * こ?クラスは、?ロセスチェインの基点となります?で、新?LineModel を返します?
366 * Exception 以外では、? LineModel オブジェクトを返します?
367 *
368 * @param att Attributesオブジェク?
369 *
370 * @return ??タベ?スから取り出して変換した LineModel
371 * @throws RuntimeException カラ?を取得できなかった?合?
372 */
373 private LineModel createLineModel( final Attributes att ) {
374 LineModel model = new LineModel();
375
376 try {
377 // init() でチェ?済み。attrs == null か?属?定義数と別名?列数が同じ?ず?
378 // attrs ?null の場合?、?キー??を取得します?
379 if( attrs == null ) {
380 NamingEnumeration<String> nmEnum = att.getIDs(); // 4.3.3.6 (2008/11/15) Generics警告対?
381 List<String> lst = new ArrayList<String>();
382 try {
383 while( nmEnum.hasMore() ) {
384 lst.add( nmEnum.next() ); // 4.3.3.6 (2008/11/15) Generics警告対?
385 }
386 }
387 finally {
388 nmEnum.close();
389 }
390 attrs = lst.toArray( new String[lst.size()] );
391 columns = attrs;
392 }
393
394 int size = columns.length;
395 model.init( size );
396 for(int clm = 0; clm < size; clm++) {
397 model.setName( clm,StringUtil.nval( columns[clm],attrs[clm] ) );
398 }
399 }
400 catch ( NamingException ex ) {
401 String errMsg = "ResultSetMetaData から、カラ?を取得できませんでした?;
402 throw new RuntimeException( errMsg,ex );
403 }
404 return model;
405 }
406
407 /**
408 * スコープを表す文字??SearchControls の定数に変換します?
409 * 入力文字?は、OBJECT、ONELEVEL、SUBTREEです?変換する定数は?
410 * SearchControls クラスの static 定数です?
411 *
412 * @param scope スコープを表す文字?(OBJECT、ONELEVEL、SUBTREE)
413 *
414 * @return SearchControlsの定数(OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE)
415 * @see javax.naming.directory.SearchControls#OBJECT_SCOPE
416 * @see javax.naming.directory.SearchControls#ONELEVEL_SCOPE
417 * @see javax.naming.directory.SearchControls#SUBTREE_SCOPE
418 */
419 private int changeScopeString( final String scope ) {
420 int rtnScope ;
421 if( "OBJECT".equals( scope ) ) { rtnScope = SearchControls.OBJECT_SCOPE ; }
422 else if( "ONELEVEL".equals( scope ) ) { rtnScope = SearchControls.ONELEVEL_SCOPE ; }
423 else if( "SUBTREE".equals( scope ) ) { rtnScope = SearchControls.SUBTREE_SCOPE ; }
424 else {
425 String errMsg = "Search Scope in 『OBJECT』?ONELEVEL』?SUBTREE』Selected"
426 + "[" + scope + "]" ;
427 throw new RuntimeException( errMsg );
428 }
429 return rtnScope ;
430 }
431
432 /**
433 * プロセスの処?果のレポ?ト表現を返します?
434 * 処??ログラ?、?力件数、?力件数などの??です?
435 * こ???をそのまま、標準?力に出すことで、結果レポ?トと出来るよ?
436 * 形式で出してください?
437 *
438 * @return 処?果のレポ??
439 */
440 public String report() {
441 String report = "[" + getClass().getName() + "]" + CR
442 + TAB + "Search Filter : " + filter + CR
443 + TAB + "Input Count : " + count ;
444
445 return report ;
446 }
447
448 /**
449 * こ?クラスの使用方法を返します?
450 *
451 * @return こ?クラスの使用方?
452 */
453 public String usage() {
454 StringBuilder buf = new StringBuilder();
455
456 buf.append( "Process_LDAPReaderは、LDAPから読み取った?容を?LineModel に設定後?" ).append( CR );
457 buf.append( "下流に渡す?FirstProcess インターフェースの実?ラスです?" ).append( CR );
458 buf.append( CR );
459 buf.append( "LDAPから読み取った?容より、LineModelを作?し?下?プロセスチェインは? ).append( CR );
460 buf.append( "チェインして?ため、データは上流から下流へと渡されます?)に渡します?" ).append( CR );
461 buf.append( CR );
462 buf.append( "引数??中に空白を含??合?、ダブルコー??ション(\"\") で括って下さ??" ).append( CR );
463 buf.append( "引数??の ?』?前後には、空白は挟めません。??key=value の様に" ).append( CR );
464 buf.append( "繋げてください? ).append( CR );
465 buf.append( CR ).append( CR );
466
467 buf.append( getArgument().usage() ).append( CR );
468
469 return buf.toString();
470 }
471
472 /**
473 * こ?クラスは、main メソ?から実行できません?
474 *
475 * @param args コマンド引数配?
476 */
477 public static void main( final String[] args ) {
478 LogWriter.log( new Process_LDAPReader().usage() );
479 }
480 }