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 package org.imixs.workflow.plugins.jee.extended;
29
30 import java.io.File;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Calendar;
34 import java.util.Collection;
35 import java.util.Date;
36 import java.util.List;
37 import java.util.Properties;
38 import java.util.StringTokenizer;
39 import java.util.logging.Logger;
40
41 import org.apache.lucene.analysis.Analyzer;
42 import org.apache.lucene.analysis.standard.StandardAnalyzer;
43 import org.apache.lucene.document.Document;
44 import org.apache.lucene.document.Field;
45 import org.apache.lucene.index.IndexWriter;
46 import org.apache.lucene.index.IndexWriterConfig;
47 import org.apache.lucene.index.Term;
48 import org.apache.lucene.queryParser.QueryParser;
49 import org.apache.lucene.search.IndexSearcher;
50 import org.apache.lucene.search.ScoreDoc;
51 import org.apache.lucene.search.TopDocs;
52 import org.apache.lucene.store.Directory;
53 import org.apache.lucene.store.FSDirectory;
54 import org.apache.lucene.util.Version;
55 import org.imixs.workflow.ItemCollection;
56 import org.imixs.workflow.Plugin;
57 import org.imixs.workflow.WorkflowContext;
58 import org.imixs.workflow.exceptions.PluginException;
59 import org.imixs.workflow.jee.ejb.EntityService;
60 import org.imixs.workflow.jee.ejb.WorkflowService;
61 import org.imixs.workflow.plugins.AbstractPlugin;
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 public class LucenePlugin extends AbstractPlugin {
87
88 IndexWriter writer = null;
89 static List<String> searchFieldList = null;
90 static List<String> indexFieldListAnalyse = null;
91 static List<String> indexFieldListNoAnalyse = null;
92 private static Logger logger = Logger.getLogger("org.imixs.workflow");
93
94 @Override
95 public void init(WorkflowContext actx) throws PluginException {
96 super.init(actx);
97 }
98
99
100
101
102
103
104
105
106
107
108
109 public int run(ItemCollection documentContext, ItemCollection activity)
110 throws PluginException {
111
112
113 int nextProcessID = activity.getItemValueInteger("numnextprocessid");
114 int currentProcessID = documentContext
115 .getItemValueInteger("$processid");
116
117 try {
118 documentContext.replaceItemValue("$processid", nextProcessID);
119
120
121 addWorkitem(documentContext);
122
123 documentContext.replaceItemValue("$processid", currentProcessID);
124
125 } catch (Exception e) {
126 throw new PluginException(e.getMessage());
127 }
128 return Plugin.PLUGIN_OK;
129 }
130
131 public void close(int status) throws PluginException {
132
133 }
134
135
136
137
138
139
140
141
142
143 public static boolean addWorkitem(ItemCollection documentContext)
144 throws Exception {
145 List<ItemCollection> workitems = new ArrayList<ItemCollection>();
146
147 workitems.add(documentContext);
148
149 addWorklist(workitems);
150
151 return true;
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public static boolean addWorklist(Collection<ItemCollection> worklist)
168 throws Exception {
169
170 Properties prop = loadProperties();
171 if (prop.isEmpty())
172 return false;
173
174 IndexWriter awriter = createIndexWriter(prop);
175
176
177 try {
178
179 for (ItemCollection workitem : worklist) {
180
181 Term term = new Term("$uniqueid",
182 workitem.getItemValueString("$uniqueid"));
183
184 if (matchConditions(prop, workitem))
185 awriter.updateDocument(term, createDocument(prop, workitem));
186 else
187 awriter.deleteDocuments(term);
188
189 }
190 } catch (Exception luceneEx) {
191
192 logger.warning(" Lucene Exception : " + luceneEx.getMessage());
193 throw luceneEx;
194 } finally {
195 logger.fine(" close writer");
196 awriter.optimize();
197 awriter.close();
198 }
199
200 logger.fine(" update worklist successfull");
201 return true;
202 }
203
204
205
206
207
208
209
210
211
212 public static boolean matchConditions(Properties prop,
213 ItemCollection aworktiem) {
214
215 String typePattern = prop.getProperty("MatchingType");
216 String processIDPattern = prop.getProperty("MatchingProcessID");
217
218 String type = aworktiem.getItemValueString("Type");
219 String sPid = aworktiem.getItemValueInteger("$Processid") + "";
220
221
222 if (typePattern != null && !"".equals(typePattern)
223 && !type.matches(typePattern))
224 return false;
225
226
227 if (processIDPattern != null && !"".equals(processIDPattern)
228 && !sPid.matches(processIDPattern))
229 return false;
230
231 return true;
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 public static List<ItemCollection> search(String sSearchTerm,
246 WorkflowService workflowService) throws Exception {
247
248 ArrayList<ItemCollection> workitems = new ArrayList<ItemCollection>();
249
250
251 if (sSearchTerm == null || "".equals(sSearchTerm))
252 return workitems;
253
254 long ltime = System.currentTimeMillis();
255 Properties prop = loadProperties();
256 if (prop.isEmpty())
257 return workitems;
258
259 String sIndexDir = prop.get("IndexDir") + "";
260 Directory directory;
261 try {
262 directory = FSDirectory.open(new File(sIndexDir));
263
264 IndexSearcher searcher = new IndexSearcher(directory, true);
265
266
267 Analyzer analyzer = new org.apache.lucene.analysis.KeywordAnalyzer();
268 QueryParser parser = new QueryParser(Version.LUCENE_31, "content",
269 analyzer);
270
271
272 if (!workflowService
273 .isUserInRole(EntityService.ACCESSLEVEL_MANAGERACCESS)) {
274
275 List<String> userNameList = workflowService.getUserNameList();
276
277 String sAccessTerm = "($readaccess:ANONYMOUS";
278 for (String aRole : userNameList) {
279 if (!"".equals(aRole))
280 sAccessTerm += " $readaccess:\"" + aRole + "\"";
281 }
282 sAccessTerm += ") AND ";
283 sSearchTerm = sAccessTerm + sSearchTerm;
284 }
285 logger.info(" lucene search:" + sSearchTerm);
286
287 if (!"".equals(sSearchTerm)) {
288 parser.setAllowLeadingWildcard(true);
289
290 TopDocs topDocs = searcher.search(parser.parse(sSearchTerm),
291 1000);
292
293 logger.fine(" total hits=" + topDocs.totalHits);
294
295
296 ScoreDoc[] scoreDosArray = topDocs.scoreDocs;
297 for (ScoreDoc scoredoc : scoreDosArray) {
298
299 Document doc = searcher.doc(scoredoc.doc);
300
301 String sID = doc.get("$uniqueid");
302 logger.fine(" lucene $uniqueid=" + sID);
303 ItemCollection itemCol = workflowService.getEntityService()
304 .load(sID);
305 if (itemCol != null) {
306 workitems.add(itemCol);
307 }
308 }
309
310 }
311
312 searcher.close();
313 directory.close();
314
315 logger.info(" lucene serach: "
316 + (System.currentTimeMillis() - ltime) + " ms");
317 } catch (Exception e) {
318 logger.warning(" lucene error!");
319 e.printStackTrace();
320 }
321
322 return workitems;
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336 public static Document createDocument(Properties prop,
337 ItemCollection aworkitem) {
338 String sValue = null;
339 Document doc = new Document();
340
341
342
343
344 String sContent = "";
345 for (String aFieldname : searchFieldList) {
346 sValue = "";
347
348 List vValues = aworkitem.getItemValue(aFieldname);
349 if (vValues.size() == 0)
350 continue;
351
352 for (Object o : vValues) {
353 if (o == null)
354
355 continue;
356
357 if (o instanceof Calendar || o instanceof Date) {
358 SimpleDateFormat dateformat = new SimpleDateFormat(
359 "yyyyMMddHHmm");
360
361 String sDateValue;
362 if (o instanceof Calendar)
363 sDateValue = dateformat
364 .format(((Calendar) o).getTime());
365 else
366 sDateValue = dateformat.format((Date) o);
367 sValue += sDateValue + ",";
368
369 } else
370
371 sValue += o.toString() + ",";
372 }
373 if (sValue != null) {
374 logger.fine(" add SearchField: " + aFieldname + " = " + sValue);
375 sContent += sValue + ",";
376 }
377 }
378 logger.fine(" content = " + sContent);
379 doc.add(new Field("content", sContent, Field.Store.NO,
380 Field.Index.ANALYZED));
381
382
383 for (String aFieldname : indexFieldListAnalyse) {
384 addFieldValue(doc, aworkitem, aFieldname, true);
385 }
386
387 for (String aFieldname : indexFieldListNoAnalyse) {
388 addFieldValue(doc, aworkitem, aFieldname, false);
389 }
390
391
392 doc.add(new Field("$uniqueid", aworkitem
393 .getItemValueString("$uniqueid"), Field.Store.YES,
394 Field.Index.NOT_ANALYZED));
395
396
397 List<String> vReadAccess = aworkitem.getItemValue("$readAccess");
398 if (vReadAccess.size() == 0
399 || (vReadAccess.size() == 1 && "".equals(vReadAccess.get(0)
400 .toString()))) {
401 sValue = "ANONYMOUS";
402 doc.add(new Field("$readaccess", sValue, Field.Store.NO,
403 Field.Index.NOT_ANALYZED_NO_NORMS));
404 } else {
405 sValue = "";
406
407 for (String sReader : vReadAccess)
408 doc.add(new Field("$readaccess", sReader, Field.Store.NO,
409 Field.Index.NOT_ANALYZED_NO_NORMS));
410
411 }
412 return doc;
413 }
414
415
416
417
418
419
420
421
422
423
424
425
426
427 private static void addFieldValue(Document doc, ItemCollection aworkitem,
428 String aFieldname, boolean analyzeValue) {
429 String sValue = null;
430 List vValues = aworkitem.getItemValue(aFieldname);
431 if (vValues.size() == 0)
432 return;
433 if (vValues.get(0) == null)
434 return;
435
436 for (Object singleValue : vValues) {
437
438
439 if (singleValue instanceof Calendar || singleValue instanceof Date) {
440 SimpleDateFormat dateformat = new SimpleDateFormat(
441 "yyyyMMddHHmm");
442
443
444 String sDateValue;
445 if (singleValue instanceof Calendar)
446 sDateValue = dateformat.format(((Calendar) singleValue)
447 .getTime());
448 else
449 sDateValue = dateformat.format((Date) singleValue);
450 sValue = sDateValue;
451
452 } else
453
454 sValue = singleValue.toString();
455
456 logger.fine(" add IndexField (analyse=" + analyzeValue + "): "
457 + aFieldname + " = " + sValue);
458 if (analyzeValue)
459 doc.add(new Field(aFieldname, sValue, Field.Store.NO,
460 Field.Index.ANALYZED));
461 else
462
463 doc.add(new Field(aFieldname, sValue, Field.Store.NO,
464 Field.Index.NOT_ANALYZED));
465
466 }
467
468 }
469
470
471
472
473
474
475
476
477 public static IndexWriter createIndexWriter(Properties prop)
478 throws Exception {
479
480 String sIndexDir = prop.get("IndexDir") + "";
481 String sFulltextFieldList = prop.get("FulltextFieldList") + "";
482 String sIndexFieldListAnalyse = prop.get("IndexFieldListAnalyze") + "";
483 String sIndexFieldListNoAnalyse = prop.get("IndexFieldListNoAnalyze")
484 + "";
485
486 logger.fine("IndexDir:" + sIndexDir);
487 logger.fine("FulltextFieldList:" + sFulltextFieldList);
488 logger.fine("IndexFieldListAnalyse:" + sIndexFieldListAnalyse);
489 logger.fine("IndexFieldListNoAnalyse:" + sIndexFieldListNoAnalyse);
490
491 StringTokenizer st = new StringTokenizer(sFulltextFieldList, ",");
492 searchFieldList = new ArrayList<String>();
493 while (st.hasMoreElements()) {
494 String sName = st.nextToken().toLowerCase();
495
496 if (!"$uniqueid".equals(sName) && !"$readaccess".equals(sName))
497 searchFieldList.add(sName);
498 }
499
500
501 st = new StringTokenizer(sIndexFieldListAnalyse, ",");
502 indexFieldListAnalyse = new ArrayList<String>();
503 while (st.hasMoreElements()) {
504 String sName = st.nextToken().toLowerCase();
505
506 if (!"$uniqueid".equals(sName) && !"$readaccess".equals(sName))
507 indexFieldListAnalyse.add(sName);
508 }
509
510
511 st = new StringTokenizer(sIndexFieldListNoAnalyse, ",");
512 indexFieldListNoAnalyse = new ArrayList<String>();
513 while (st.hasMoreElements()) {
514 String sName = st.nextToken().toLowerCase();
515
516 if (!"$uniqueid".equals(sName) && !"$readaccess".equals(sName))
517 indexFieldListNoAnalyse.add(sName);
518 }
519
520
521
522 Directory indexDir = FSDirectory.open(new File(sIndexDir));
523
524
525 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_31);
526 IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_31,
527 analyzer);
528
529
530
531 iwc.setWriteLockTimeout(10000);
532
533
534
535
536
537
538
539 return new IndexWriter(indexDir, iwc);
540
541 }
542
543 public static IndexSearcher createIndexSearcher() throws Exception {
544 Directory directory;
545
546 Properties properties = LucenePlugin.loadProperties();
547 String sIndexDir = properties.get("IndexDir") + "";
548
549 directory = FSDirectory.open(new File(sIndexDir));
550
551 return new IndexSearcher(directory, true);
552 }
553
554
555
556
557
558
559
560 public static Properties loadProperties() throws Exception {
561
562 Properties prop = new Properties();
563 try {
564 prop.load(Thread.currentThread().getContextClassLoader()
565 .getResource("imixs-search.properties").openStream());
566 } catch (Exception ep) {
567
568 }
569 return prop;
570 }
571
572 }