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 }