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 */ 016package org.opengion.hayabusa.resource; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.fukurou.util.StringUtil; 021 022import java.util.Hashtable; 023import java.util.List; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Comparator ; 027import java.io.Serializable; 028 029import javax.naming.Context; 030import javax.naming.NamingEnumeration; 031import javax.naming.NamingException; 032import javax.naming.directory.DirContext; 033import javax.naming.directory.InitialDirContext; 034import javax.naming.directory.SearchControls; 035import javax.naming.directory.SearchResult; 036import javax.naming.directory.Attribute; 037import javax.naming.directory.Attributes; 038 039/** 040 * LDAPの内容を検索するための、ldapQueryタグです。 041 * 042 * 検索した結果は、配列で取得します。 043 * 044 * 下記の項目については、src/resource/システムパラメータ に、予め 045 * 設定しておくことで、タグごとに指定する必要がなくなります。 046 * ・LDAP_INITIAL_CONTEXT_FACTORY 047 * ・LDAP_PROVIDER_URL 048 * ・LDAP_ENTRYDN 049 * ・LDAP_PASSWORD 050 * ・LDAP_SEARCH_BASE 051 * ・LDAP_SEARCH_SCOPE 052 * ・LDAP_SEARCH_REFERRAL 053 * 054 * @og.rev 3.7.1.0 (2005/04/15) LDAPにアクセスできる、LDAPSearch.java を新規に作成。 055 * @og.group その他入力 056 * 057 * @version 4.0 058 * @author Kazuhiko Hasegawa 059 * @since JDK5.0, 060 */ 061public class LDAPSearch { 062 063 private String initctx = HybsSystem.sys( "LDAP_INITIAL_CONTEXT_FACTORY" ); 064 private String providerURL = HybsSystem.sys( "LDAP_PROVIDER_URL" ); 065 private String entrydn = HybsSystem.sys( "LDAP_ENTRYDN" ); 066 private String password = HybsSystem.sys( "LDAP_PASSWORD" ); // 4.2.2.0 (2008/05/10) 067 private String searchbase = HybsSystem.sys( "LDAP_SEARCH_BASE" ); 068 private String referral = HybsSystem.sys( "LDAP_SEARCH_REFERRAL" ); // 5.6.7.0 (201/07/27) 069 070 // 検索範囲。OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか 1 つ 071 private String searchScope = HybsSystem.sys( "LDAP_SEARCH_SCOPE" ); 072 private static final long COUNTLIMIT = 0; // 返すエントリの最大数。0 の場合、フィルタを満たすエントリをすべて返す 073 private int timeLimit = 0; // 結果が返されるまでのミリ秒数。0 の場合、無制限 074 private String[] attrs = null; // エントリと一緒に返される属性の識別子。null の場合、すべての属性を返す。空の場合、属性を返さない 075 private boolean returningObjFlag = false; // true の場合、エントリの名前にバインドされたオブジェクトを返す。false 場合、オブジェクトを返さない 076 private boolean derefLinkFlag = false; // true の場合、検索中にリンクを間接参照する 077 078 private int executeCount = 0; // 検索/実行件数 079 private int maxRowCount = 0; // 最大検索数(0は無制限) 080 private SearchControls constraints = null; 081 private DirContext ctx = null; 082 private String[] orderBy = null; // ソート項目(csv) 083 private boolean[] desc = null; // 降順フラグ 084 085 /** 086 * LDAPパラメータを利用して、LDAP検索用オブジェクトを構築します。 087 * 088 * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対応 089 * @og.rev 5.6.7.0 (2013/07/27) LDAPのREFERRAL対応 090 * 091 * 通常、パラメータをセット後、search( String filter ) の実行前に、呼びます。 092 */ 093 public void init() { 094 Hashtable<String,String> env = new Hashtable<String,String>(); 095 env.put(Context.INITIAL_CONTEXT_FACTORY, initctx); 096 env.put(Context.PROVIDER_URL, providerURL); 097 if( ! StringUtil.isNull( referral ) ) { // 5.6.7.0 (2013/07/27) 098 env.put( Context.REFERRAL, referral ); 099 } 100 // 3.7.1.1 (2005/05/31) 101 if( ! StringUtil.isNull( password ) ) { 102 env.put( Context.SECURITY_CREDENTIALS, password.trim() ); 103 } 104 // 4.2.2.0 (2008/05/10) entrydn 属性の追加 105 if( ! StringUtil.isNull( entrydn ) ) { 106 env.put( Context.SECURITY_PRINCIPAL , entrydn ); 107 } 108 109 try { 110 ctx = new InitialDirContext(env); 111 constraints = new SearchControls( 112 changeScopeString( searchScope ), 113 COUNTLIMIT , 114 timeLimit , 115 attrs , 116 returningObjFlag , 117 derefLinkFlag 118 ); 119 } catch ( NamingException ex ) { 120 String errMsg = "LDAP検索用オブジェクトの初期化に失敗しました。" ; 121 throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 122 } 123 } 124 125 /** 126 * LDPA から、値を取り出し、List オブジェクトを作成します。 127 * 引数の headerAdd をtrueにする事により、1件目に、キー情報の配列を返します。 128 * 129 * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対応 130 * 131 * @param filter フィルター文字列 132 * 133 * @return 検索結果の Listオブジェクト 134 */ 135 public List<String[]> search( final String filter ) { 136 137 List<String[]> list = new ArrayList<String[]>(); 138 try { 139 NamingEnumeration<SearchResult> results = ctx.search(searchbase, filter, constraints); // 4.3.3.6 (2008/11/15) Generics警告対応 140 141 while (results != null && results.hasMore()) { 142 if( maxRowCount > 0 && maxRowCount <= executeCount ) { break ; } 143 SearchResult si = results.next(); // 4.3.3.6 (2008/11/15) Generics警告対応 144 Attributes at = si.getAttributes(); 145 // attrs が null の場合は、キー情報を取得します。 146 if( attrs == null ) { 147 NamingEnumeration<String> ne = at.getIDs(); // 4.3.3.6 (2008/11/15) Generics警告対応 148 List<String> lst = new ArrayList<String>(); 149 while( ne.hasMore() ) { 150 lst.add( ne.next() ); // 4.3.3.6 (2008/11/15) Generics警告対応 151 } 152 ne.close(); 153 attrs = lst.toArray( new String[lst.size()] ); 154 } 155 156 String[] values = new String[attrs.length]; 157 boolean flag = false; // 属性チェックフラグ 158 for( int i=0; i<attrs.length; i++ ) { 159 if( maxRowCount > 0 && maxRowCount <= executeCount ) { break ; } 160 Attribute attr = at.get(attrs[i]); 161 if( attr != null ) { 162 NamingEnumeration<?> vals = attr.getAll(); // 4.3.3.6 (2008/11/15) Generics警告対応 163 StringBuilder buf = new StringBuilder(); 164 if( vals.hasMore() ) { getDataChange( vals.next(),buf ) ;} // 4.2.2.0 (2008/05/10) 165 while ( vals.hasMore() ) { 166 buf.append( "," ) ; 167 getDataChange( vals.next(),buf ) ; // 4.2.2.0 (2008/05/10) 168 } 169 values[i] = buf.toString(); 170 flag = true; 171 } 172 } 173 if( flag ) { 174 list.add( values ); 175 executeCount++ ; 176 } 177 } 178 if( results != null ) { results.close(); } 179 } catch ( NamingException ex ) { 180 String errMsg = "List オブジェクトの検索に失敗しました。" 181 + HybsSystem.CR 182 + "searchbase や、entrydn の記述をご確認ください。" 183 + HybsSystem.CR 184 + "searchbase:" + searchbase 185 + " , entrydn:" + entrydn ; 186 throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 187 } 188 return sort( list,attrs ) ; 189 } 190 191 /** 192 * LDAPから取得したデータの変換を行います。 193 * 194 * 主に、バイト配列(byte[]) オブジェクトの場合、文字列に戻します。 195 * 196 * @og.rev 4.2.2.0 (2008/05/10) 新規追加 197 * 198 * @param obj 主にバイト配列データ 199 * @param buf 元のStringBuilder 200 * 201 * @return データを追加したStringBuilder 202 */ 203 private StringBuilder getDataChange( final Object obj, final StringBuilder buf ) { 204 if( obj == null ) { return buf; } 205 else if( obj instanceof byte[] ) { 206 byte[] bb = (byte[])obj ; 207 char[] chs = new char[bb.length]; 208 for( int i=0; i<bb.length; i++ ) { 209 chs[i] = (char)bb[i]; 210 } 211 buf.append( chs ); 212 } 213 else { 214 buf.append( obj ) ; 215 } 216 217 return buf ; 218 } 219 220 /** 221 * 検索範囲(OBJECT/ONELEVEL/SUBTREE)を設定します(初期値:LDAP_SEARCH_SCOPE)。 222 * 223 * 検索範囲を OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか 1 つです。 224 * 指定文字列は、それぞれ『OBJECT』『ONELEVEL』『SUBTREE』です。 225 * 226 * @param scope SearchControlsの検索範囲 227 */ 228 public void setSearchScope( final String scope ) { 229 searchScope = StringUtil.nval( scope, searchScope ); 230 if( ! "OBJECT".equals( searchScope ) && 231 ! "ONELEVEL".equals( searchScope ) && 232 ! "SUBTREE".equals( searchScope ) ) { 233 String errMsg = "検索範囲は、『OBJECT』『ONELEVEL』『SUBTREE』の中から選択して下さい。" 234 + "[" + searchScope + "]" ; 235 throw new HybsSystemException( errMsg ); 236 } 237 } 238 239 /** 240 * 引数の searchScope 文字列(『OBJECT』『ONELEVEL』『SUBTREE』のどれか)を、 241 * SearchControls クラス定数である、OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか 242 * 1 つに設定します。 243 * 244 * @param scope searchScope文字列 245 * 246 * @return SearchControls定数 247 */ 248 private int changeScopeString( final String scope ) { 249 final int rtnScope; 250 if( "OBJECT".equals( scope ) ) { rtnScope = SearchControls.OBJECT_SCOPE ; } 251 else if( "ONELEVEL".equals( scope ) ) { rtnScope = SearchControls.ONELEVEL_SCOPE ; } 252 else if( "SUBTREE".equals( scope ) ) { rtnScope = SearchControls.SUBTREE_SCOPE ; } 253 else { 254 String errMsg = "Search Scope in 『OBJECT』『ONELEVEL』『SUBTREE』Selected" 255 + "[" + searchScope + "]" ; 256 throw new HybsSystemException( errMsg ); 257 } 258 return rtnScope ; 259 } 260 261 /** 262 * これらの SearchControls の時間制限をミリ秒単位で設定します(初期値:0[無制限])。 263 * 264 * 値が 0 の場合、無制限に待つことを意味します。 265 * 266 * @param limit ミリ秒単位の時間制限(初期値:無制限) 267 */ 268 public void setTimeLimit( final int limit ) { 269 timeLimit = limit; 270 } 271 272 /** 273 * 検索中のリンクへの間接参照を有効または無効[true/false]にします(初期値:false)。 274 * 275 * 検索中のリンクへの間接参照を有効または無効にします。 276 * 277 * @param deref リンクを逆参照する場合は true、そうでない場合は false(初期値:false) 278 */ 279 public void setDerefLinkFlag( final boolean deref ) { 280 derefLinkFlag = deref; 281 } 282 283 /** 284 * 結果の一部としてオブジェクトを返すことを有効または無効[true/false]にします(初期値:false)。 285 * 286 * 無効にした場合、オブジェクトの名前およびクラスだけが返されます。 287 * 有効にした場合、オブジェクトが返されます。 288 * 289 * @param pbjflag オブジェクトが返される場合は true、そうでない場合は false(初期値:false) 290 */ 291 public void setReturningObjFlag( final boolean pbjflag ) { 292 returningObjFlag = pbjflag; 293 } 294 295 /** 296 * レジストリの最大検索件数をセットします(初期値:0[無制限])。 297 * 298 * DBTableModelのデータとして登録する最大件数をこの値に設定します。 299 * サーバーのメモリ資源と応答時間の確保の為です。 300 * 0 は、無制限です。(初期値は、無制限です。) 301 * 302 * @param count レジストリの最大検索件数 303 */ 304 public void setMaxRowCount( final int count ) { 305 maxRowCount = count; 306 } 307 308 /** 309 * 検索の一部として返される属性を文字列配列でセットします。 310 * 311 * null は属性が何も返されないことを示します。 312 * このメソッドからは、空の配列をセットすることは出来ません。 313 * 314 * @param atr 返される属性を識別する属性 ID の配列 315 */ 316 public void setAttributes( final String[] atr ) { 317 if( atr != null ) { 318 attrs = new String[atr.length]; 319 System.arraycopy( atr,0,attrs,0,atr.length ); 320 } 321 } 322 323 /** 324 * 検索の一部として返される属性を文字列配列で取得します。 325 * 326 * setAttributes で、設定した文字列配列が返されます。 327 * 属性配列に、 null をセットした場合、全属性が返されます。 328 * 329 * @return 返される属性を識別する属性 ID の配列 330 */ 331 public String[] getAttributes() { 332 return (attrs == null) ? new String[0] : attrs.clone() ; 333 } 334 335 /** 336 * 初期コンテキストファクトリを指定します(初期値:システムパラメータ の INITIAL_CONTEXT_FACTORY)。 337 * 338 * 初期値は、システムパラメータ の INITIAL_CONTEXT_FACTORY 属性です。 339 * 例)com.sun.jndi.ldap.LdapCtxFactory 340 * 341 * @param ctx INITIAL_CONTEXT_FACTORY属性 342 */ 343 public void setInitctx( final String ctx ) { 344 initctx = StringUtil.nval( ctx, initctx ); 345 } 346 347 /** 348 * サービスプロバイダの構成情報を指定します(初期値:システムパラメータ の LDAP_PROVIDER_URL)。 349 * 350 * プロトコルとサーバーとポートを指定します。 351 * 例)『ldap://ldap.opengion.org:389』 352 * 353 * @param url PROVIDER_URL属性 354 */ 355 public void setProviderURL( final String url ) { 356 providerURL = StringUtil.nval( url, providerURL ); 357 } 358 359 /** 360 * 検索するコンテキストまたはオブジェクトの名前を設定します(初期値:システムパラメータ の LDAP_SEARCH_BASE)。 361 * 362 * 例)『soOUID=employeeuser,o=opengion,c=JP』 363 * 364 * @param base SEARCHBASE属性 365 */ 366 public void setSearchbase( final String base ) { 367 searchbase = StringUtil.nval( base, searchbase ); 368 } 369 370 /** 371 * 属性の取得元のオブジェクトの名前を設定します(初期値:システムパラメータ の LDAP_ENTRYDN)。 372 * 373 * 例)『cn=inquiry-sys,o=opengion,c=JP』 374 * 375 * @param dn 取得元のオブジェクトの名前 376 */ 377 public void setEntrydn( final String dn ) { 378 entrydn = StringUtil.nval( dn, entrydn ); 379 } 380 381 /** 382 * 属性の取得元のオブジェクトのパスワードを設定します(初期値:システムパラメータ の LDAP_PASSWORD)。 383 * 384 * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対応 385 * 386 * @param pwd 取得元のオブジェクトのパスワード 387 */ 388 public void setPassword( final String pwd ) { 389 password = StringUtil.nval( pwd, password ); 390 } 391 392 /** 393 * 検索した結果を表示する表示順をファイル属性名で指定します。 394 * 395 * attributes 属性で指定するキー、または、LDAPから返されたキーについて 396 * その属性でソートします。逆順を行う場合は、DESC を指定のカラム名の後ろに 397 * 付けて下さい。 398 * 399 * @param ordr ソートキーを指定。 400 */ 401 public void setOrderBy( final String ordr ) { 402 orderBy = StringUtil.csv2Array( ordr ); 403 404 desc = new boolean[orderBy.length]; 405 for( int i=0; i<orderBy.length; i++ ) { 406 String key = orderBy[i].trim(); 407 int ad = key.indexOf( " DESC" ) ; 408 if( ad > 0 ) { 409 desc[i] = true; 410 key = key.substring( 0,ad ); 411 } 412 else { 413 desc[i] = false; 414 } 415 orderBy[i] = key ; 416 } 417 } 418 419 /** 420 * リストオブジェクトをヘッダーキーに対応させてソートします。 421 * 422 * @og.rev 4.2.2.0 (2008/05/10) ソート条件を増やします。 423 * 424 * @param in ソートするリストオブジェクト 425 * @param headers ソートするキーになる文字列配列 426 * 427 * @return ソート結果のリストオブジェクト 428 */ 429 private List<String[]> sort( final List<String[]> in,final String[] headers ) { 430 // 4.2.2.0 (2008/05/10) ソート条件を増やします。 431 if( orderBy == null || orderBy.length == 0 || 432 headers == null || headers.length == 0 || 433 in.isEmpty() ) { return in; } 434 435 int[] no = new int[orderBy.length]; 436 for( int i=0; i<orderBy.length; i++ ) { 437 String key = orderBy[i] ; 438 no[i] = -1; // 未存在時のマーカー 439 for( int j=0; j<headers.length; j++ ) { 440 if( key.equalsIgnoreCase( headers[j] ) ) { 441 no[i] = j ; break; 442 } 443 } 444 if( no[i] < 0 ) { 445 String errMsg = "指定の Order BY キーは、ヘッダー列に存在しません。" 446 + "order Key=[" + key + "] , attri=[" 447 + StringUtil.array2csv( headers ) + "]" + HybsSystem.CR ; 448 throw new HybsSystemException( errMsg ); 449 } 450 } 451 452 String[][] data = in.toArray( new String[in.size()][(in.get(0)).length] ); 453 Arrays.sort( data, new IdComparator( no,desc ) ); 454 List<String[]> rtn = new ArrayList<String[]>(); 455 for( int i=0; i<data.length; i++ ) { 456 rtn.add( data[i] ); 457 } 458 return rtn ; 459 } 460 461 /** 462 * LDAPの検索結果を並び替える為の Comparator実装内部クラスです。 463 * 464 * @og.group その他入力 465 * 466 * @version 4.0 467 * @author Kazuhiko Hasegawa 468 * @since JDK5.0, 469 */ 470 private static class IdComparator implements Comparator<String[]>,Serializable { 471 private static final long serialVersionUID = 400020050131L ; // 4.0.0.0 (2005/01/31) 472 473 private final int[] no ; 474 private final boolean[] desc ; 475 private final int cnt ; 476 477 /** 478 * コンストラクター 479 * 480 * @param no int[] ソートするリストオブジェクト 481 * @param desc boolean[] ソートするキーになる文字列配列 482 */ 483 public IdComparator( final int[] no , final boolean[] desc ) { 484 this.no = no; 485 this.desc = desc; 486 cnt = no.length; 487 } 488 489 /** 490 * Comparator インターフェースのcompareメソッド 491 * 492 * 順序付けのために 2 つの引数を比較します。 493 * 最初の引数が 2 番目の引数より小さい場合は負の整数、 494 * 両方が等しい場合は 0、最初の引数が 2 番目の引数より 495 * 大きい場合は正の整数を返します。 496 * 497 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。トリッキーな値の置き換えをやめます。 498 * 499 * @param s1 比較対象の最初のオブジェクト 500 * @param s2 比較対象の 2 番目のオブジェクト 501 * @return 最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数 502 */ 503 public int compare( final String[] s1,final String[] s2 ) { 504 if( s1 == null ) { return -1; } 505 506 for( int i=0; i<cnt; i++ ) { 507 if( s1[no[i]] == null ) { return -1; } 508 if( s2[no[i]] == null ) { return 1; } // 5.5.2.6 (2012/05/25) 比較を途中で止めないために、nullチェックしておく。 509 // 5.5.2.6 (2012/05/25) findbugs対応 510 int rtn = desc[i] ? s2[no[i]].compareTo( s1[no[i]] ) : s1[no[i]].compareTo( s2[no[i]] ) ; 511 if( rtn != 0 ) { return rtn ;} 512 } 513 return 0; 514 } 515 516 // public boolean equals(Object obj) { 517 // return ( this == obj ); 518 // } 519 } 520 521 /** 522 * このオブジェクトの文字列表現を返します。 523 * 基本的にデバッグ目的に使用します。 524 * 525 * @return このクラスの文字列表現 526 */ 527 @Override 528 public String toString() { 529 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 530 buf.append( " initctx [" ).append( initctx ).append( "]" ).append( HybsSystem.CR ); 531 buf.append( " providerURL [" ).append( providerURL ).append( "]" ).append( HybsSystem.CR ); 532 buf.append( " entrydn [" ).append( entrydn ).append( "]" ).append( HybsSystem.CR ); 533 buf.append( " searchbase [" ).append( searchbase ).append( "]" ).append( HybsSystem.CR ); 534 buf.append( " searchScope [" ).append( searchScope ).append( "]" ).append( HybsSystem.CR ); 535 buf.append( " executeCount [" ).append( executeCount ).append( "]" ).append( HybsSystem.CR ); 536 buf.append( " attributes [" ).append( StringUtil.array2line( attrs,"," ) ); 537 buf.append( "]" ).append( HybsSystem.CR ); 538 539 return buf.toString(); 540 } 541}