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