1 /*****************************************************************************
5 * Creation date 05.10.2012
8 *****************************************************************************/
10 package org.splat.service;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
18 import org.apache.log4j.Logger;
19 import org.apache.lucene.index.IndexWriter;
20 import org.apache.lucene.index.Term;
21 import org.apache.lucene.search.BooleanClause;
22 import org.apache.lucene.search.BooleanFilter;
23 import org.apache.lucene.search.BooleanQuery;
24 import org.apache.lucene.search.FilterClause;
25 import org.apache.lucene.search.IndexSearcher;
26 import org.apache.lucene.search.ScoreDoc;
27 import org.apache.lucene.search.Sort;
28 import org.apache.lucene.search.SortField;
29 import org.apache.lucene.search.TermQuery;
30 import org.apache.lucene.search.TermsFilter;
31 import org.apache.lucene.search.TopFieldDocs;
32 import org.apache.lucene.store.Directory;
33 import org.apache.lucene.store.FSDirectory;
34 import org.hibernate.Criteria;
35 import org.hibernate.Hibernate;
36 import org.hibernate.criterion.DetachedCriteria;
37 import org.hibernate.criterion.Disjunction;
38 import org.hibernate.criterion.Junction;
39 import org.hibernate.criterion.Order;
40 import org.hibernate.criterion.Restrictions;
41 import org.hibernate.type.Type;
42 import org.splat.dal.bo.kernel.User;
43 import org.splat.dal.bo.som.KnowledgeElement;
44 import org.splat.dal.bo.som.KnowledgeElementType;
45 import org.splat.dal.bo.som.ProgressState;
46 import org.splat.dal.bo.som.Scenario;
47 import org.splat.dal.bo.som.SimulationContext;
48 import org.splat.dal.bo.som.Study;
49 import org.splat.dal.bo.som.Visibility;
50 import org.splat.dal.dao.som.KnowledgeElementDAO;
51 import org.splat.dal.dao.som.StudyDAO;
52 import org.splat.service.dto.ImportedStudyDTO;
53 import org.splat.service.dto.KnowledgeSearchFilterDTO;
54 import org.splat.service.dto.Proxy;
55 import org.splat.service.dto.SearchFilterDTO;
56 import org.splat.service.dto.StudyDTO;
57 import org.splat.service.dto.StudySearchFilterDTO;
58 import org.splat.service.technical.IndexService;
59 import org.splat.service.technical.IndexServiceImpl;
60 import org.splat.service.technical.RepositoryService;
61 import org.splat.util.BeanHelper;
62 import org.springframework.transaction.annotation.Transactional;
65 * Search service implementation.
67 * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
69 public class SearchServiceImpl implements SearchService {
74 public final static Logger LOG = Logger
75 .getLogger(org.splat.service.SearchServiceImpl.class);
78 * Injected repository service.
80 private RepositoryService _repositoryService;
82 * Injected index service.
84 private IndexService _indexService;
86 * Injected study service.
88 private StudyService _studyService;
92 private StudyDAO _studyDAO;
94 * Injected knowledge element DAO.
96 private KnowledgeElementDAO _knowledgeElementDAO;
99 * Get a list of studies which are currently not presented in the lucene index.
101 * @return list of ImportedStudy DTO
103 @Transactional(readOnly = true)
104 public List<ImportedStudyDTO> selectStudies() {
105 List<ImportedStudyDTO> table = new ArrayList<ImportedStudyDTO>();
106 Study.Properties sprop = new Study.Properties();
107 List<Study> dbStudies = getStudyDAO().getAll();
109 for (Study aStudy : dbStudies) {
112 if (selectStudiesWhere(
113 sprop.setReference(aStudy.getReference())).size() != 0) {
114 // If this study was already indexed and found in the lucene index
115 // then skip it to avoid adding to the index it again.
118 } catch (Exception error) {
121 // Add the study to the list of studies which are
122 // currently not presented in the lucene index.
123 table.add(BeanHelper.copyBean(aStudy, ImportedStudyDTO.class));
129 * Refresh lucene index for studies.
132 * list of studies id's
134 @Transactional(readOnly = true)
136 public void reindexStudies(final String[] ridlist) {
137 for (int i = 0; i < ridlist.length; i++) {
138 long index = Long.valueOf(ridlist[i].trim());
139 Study study = getStudyService().selectStudy(index);
147 * @see org.splat.service.SearchService#selectKnowledgeElementsWhere(org.splat.service.dto.KnowledgeSearchFilterDTO)
149 public List<Proxy> selectKnowledgeElementsWhere(
150 final KnowledgeSearchFilterDTO filter) {
151 List<Proxy> result = new ArrayList<Proxy>();
153 // Search matching all criteria
154 DetachedCriteria query = DetachedCriteria.forClass(
155 KnowledgeElement.class, "kelm");
156 query.createAlias("kelm.owner", "scen", Criteria.INNER_JOIN)
157 .createCriteria("scen.owner", "study", Criteria.INNER_JOIN)
158 .add(visibleStudyFilter(filter));
160 // Creation of the query
161 Junction topJunction = initQuery(filter);
163 addByWordsCriteria(topJunction, filter);
165 List<SimulationContext> context = filter.getSimContexts();
166 if (context != null && (!context.isEmpty())) {
167 // Get only studies which have given contexts
168 query.createAlias("study.contex", "ctx", Criteria.INNER_JOIN);
170 if (filter.isMatchAllContexts()) { // AND
171 critctx = Restrictions.conjunction();
173 critctx = Restrictions.disjunction();
175 for (SimulationContext seltext : context) {
176 // (simctxType = seltext.getType() AND simctxValue = seltext.getValue())
177 critctx.add(Restrictions.and(Restrictions.eq("ctx.value",
178 seltext.getValue()), Restrictions.eq("ctx.type",
179 seltext.getType())));
182 topJunction.add(critctx);
185 query.add(topJunction);
187 query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
188 // Creation of the sort criteria
189 query.addOrder(Order.asc("title"));
191 if (LOG.isInfoEnabled()) {
192 LOG.info("Searching knowledge elements: \"" + query.toString());
196 List<KnowledgeElement> found = getKnowledgeElementDAO()
197 .getFilteredList(query);
199 // Construction of the result list
200 for (KnowledgeElement kelm : found) {
201 result.add(new StudyDTO(kelm.getIndex(), kelm.getReference(), kelm
202 .getProgressState(), kelm.getTitle(), kelm.getAuthor()
209 * Add search criteria for filtering by title contents.
212 * the search condition to be appended
216 private void addByWordsCriteria(final Junction topJunction,
217 final SearchFilterDTO filter) {
218 String title = filter.getWords(); // Title
219 if (title != null && (!title.isEmpty())) {
220 // Look for given words in titles
222 if (filter.isMatchAllCriteria()) { // AND
223 critext = Restrictions.conjunction();
225 critext = Restrictions.disjunction();
228 String[] word = title.split(" ");
229 for (int j = 0; j < word.length; j++) {
230 critext.add(Restrictions.like("title", "%" + word[j] + "%"));
232 topJunction.add(critext);
239 * @see org.splat.service.SearchService#selectKnowledgeElementsWhere(org.splat.dal.bo.som.KnowledgeElement.Properties[])
242 public List<Proxy> selectKnowledgeElementsWhere(
243 final KnowledgeElement.Properties... kprop) {
244 List<Proxy> result = new ArrayList<Proxy>();
248 // Creation of the Lucene query
249 File indir = getRepositoryService().getRepositoryIndexDirectory();
250 Directory index = FSDirectory.open(indir);
251 IndexSearcher searcher = new IndexSearcher(index, true);
252 BooleanQuery fulquery = new BooleanQuery();
254 for (int i = 0; i < kprop.length; i++) {
255 BooleanQuery query = new BooleanQuery();
256 Term input; // Supposed initialized below at least by the visibility
258 Visibility area = kprop[i].getVisibility(); // Visibility
260 input = new Term("area");
261 query.add(new TermQuery(input.createTerm(area.toString())),
262 BooleanClause.Occur.MUST);
264 ProgressState state = kprop[i].getProgressState(); // State
266 input = new Term("state");
268 new TermQuery(input.createTerm(state.toString())),
269 BooleanClause.Occur.MUST);
271 String refid = kprop[i].getReference(); // Reference
273 input = new Term("ref");
274 query.add(new TermQuery(input.createTerm(refid)),
275 BooleanClause.Occur.MUST);
277 KnowledgeElementType type = kprop[i].getType(); // Type
279 input = new Term("type");
280 query.add(new TermQuery(input.createTerm(type.getName())),
281 BooleanClause.Occur.MUST);
283 User manager = kprop[i].getAuthor(); // Author
284 if (manager != null) {
285 input = new Term("author");
286 query.add(new TermQuery(input
287 .createTerm(manager.toString())),
288 BooleanClause.Occur.MUST);
290 User actor = kprop[i].getActor(); // Contributor, Reviewer or Approver of the owner study
292 input = new Term("actor");
294 new TermQuery(input.createTerm(actor.toString())),
295 BooleanClause.Occur.MUST);
297 String title = kprop[i].getTitle(); // Title
299 input = new Term("contents");
300 BooleanQuery critext = new BooleanQuery();
301 String operator = "AND"; // Future user input
302 BooleanClause.Occur clause = BooleanClause.Occur.MUST;
303 if (operator.equals("OR")) {
304 clause = BooleanClause.Occur.SHOULD;
306 String[] word = title.split(" ");
307 for (int j = 0; j < word.length; j++) {
308 critext.add(new TermQuery(input.createTerm(word[j])),
311 query.add(critext, BooleanClause.Occur.MUST);
313 List<SimulationContext> context = kprop[i]
314 .getSimulationContexts();
315 if (context != null && context.size() > 0) {
316 BooleanQuery critext = new BooleanQuery();
317 for (Iterator<SimulationContext> j = context.iterator(); j
319 SimulationContext seltext = j.next();
320 input = new Term(String.valueOf(seltext.getType()
322 critext.add(new TermQuery(input.createTerm(seltext
323 .getValue())), BooleanClause.Occur.MUST);
325 query.add(critext, BooleanClause.Occur.MUST);
327 fulquery.add(query, BooleanClause.Occur.SHOULD);
329 if (LOG.isInfoEnabled()) {
330 LOG.info("Searching knowledges by Lucene query \""
331 + fulquery.toString());
333 // Creation of the knowledge filter
334 BooleanFilter filter = new BooleanFilter();
335 TermsFilter select = new TermsFilter();
336 Term mytype = new Term("class");
337 select.addTerm(mytype.createTerm("KnowledgeElement"));
338 filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD));
340 // Creation of the sort criteria
341 Sort sort = new Sort(new SortField("title", SortField.STRING));
344 TopFieldDocs found = searcher.search(fulquery, filter, hitsize,
347 if (found.totalHits < 1) {
348 return result; // No study found
351 // Construction of the result list
352 ScoreDoc[] hits = found.scoreDocs;
353 for (int i = 0; i < hits.length; i++) {
354 result.add(new IndexServiceImpl.ObjectProxy(searcher
358 } catch (Exception error) {
359 LOG.error("Error during Lucene search, reason:", error);
367 * @see org.splat.service.SearchService#selectStudiesWhere(org.splat.service.dto.StudySearchFilterDTO)
369 public List<Proxy> selectStudiesWhere(final StudySearchFilterDTO filter) {
370 List<Proxy> result = new ArrayList<Proxy>();
372 DetachedCriteria query = DetachedCriteria
373 .forClass(Study.class, "study").add(visibleStudyFilter(filter));
375 // Creation of the query
376 Junction topJunction = initQuery(filter);
378 addByWordsCriteria(topJunction, filter);
380 List<SimulationContext> context = filter.getSimContexts();
381 if (context != null && (!context.isEmpty())) {
382 // Get only studies which have given contexts
383 query.createAlias("contex", "ctx", Criteria.INNER_JOIN);
385 if (filter.isMatchAllContexts()) { // AND
386 critctx = Restrictions.conjunction();
388 critctx = Restrictions.disjunction();
390 for (SimulationContext seltext : context) {
391 // (simctxType = seltext.getType() AND simctxValue = seltext.getValue())
392 critctx.add(Restrictions.and(Restrictions.eq("ctx.value",
393 seltext.getValue()), Restrictions.eq("ctx.type",
394 seltext.getType())));
397 topJunction.add(critctx);
400 query.add(topJunction);
402 query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
403 // Creation of the sort criteria
404 query.addOrder(Order.asc("title"));
406 if (LOG.isInfoEnabled()) {
407 LOG.info("Searching studies: \"" + query.toString());
411 List<Study> found = getStudyDAO().getFilteredList(query);
413 // Construction of the result list
414 for (Study std : found) {
415 result.add(new StudyDTO(std.getIndex(), std.getReference(), std
416 .getProgressState(), std.getTitle(), std.getAuthor()
423 * Initialize query with base criteria.
427 * @return top junction of the search filter
429 private Junction initQuery(final StudySearchFilterDTO filter) {
430 Junction topJunction;
431 if (filter.isMatchAllCriteria()) { // AND
432 topJunction = Restrictions.conjunction();
434 topJunction = Restrictions.disjunction();
436 if (!StudySearchFilterDTO.ANY_STATE.equals(filter.getState())) {
437 ProgressState state = ProgressState.valueOf(filter.getState()); // State
439 topJunction.add(Restrictions.eq("state", state));
442 String refid = filter.getReference(); // Reference
443 if (refid != null && !refid.isEmpty()) {
444 topJunction.add(Restrictions.eq("sid", refid));
447 addDatesCriteria(topJunction, filter);
449 // Filter by study author
450 long authorId = Long.valueOf(filter.getAuthor());
452 topJunction.add(Restrictions.eq("manager.rid", authorId));
459 * Initialize query with base criteria.
463 * @return top junction of the search filter
465 private Junction initQuery(final KnowledgeSearchFilterDTO filter) {
466 Junction topJunction;
467 if (filter.isMatchAllCriteria()) { // AND
468 topJunction = Restrictions.conjunction();
470 topJunction = Restrictions.disjunction();
473 // Filter by knowledge type
474 long ktypeId = Long.valueOf(filter.getKtype());
476 topJunction.add(Restrictions.eq("kelm.type.rid", ktypeId));
479 String refid = filter.getReference(); // Reference
480 if (refid != null && !refid.isEmpty()) {
481 long id = Long.valueOf(refid.replaceAll("^KE(0)*", ""));
483 topJunction.add(Restrictions.eq("kelm.rid", id));
487 addCreationDateCriteria(topJunction, filter, "date");
489 // Filter by knowledge author
490 long authorId = Long.valueOf(filter.getAuthor());
492 topJunction.add(Restrictions.eq("kelm.author.rid", authorId));
499 * Filter for visible studies.
503 * @return search condition for studies to get only visible studies
505 private Junction visibleStudyFilter(final SearchFilterDTO filter) {
506 Junction topJunction;
507 if (filter.isMatchAllCriteria()) { // AND
508 topJunction = Restrictions.conjunction();
510 topJunction = Restrictions.disjunction();
513 // Filter by connected user
514 long actorId = filter.getConnectedUserId(); // Contributor, Reviewer or Approver
516 // User is loggen in - show public studies and studies where he is participating
517 Disjunction orCrit = Restrictions.disjunction();
521 /* If the user is a validation cycle participant */
524 "{alias}.rid in (select vcrel.owner from cycle_rel vcrel inner join cycle vc on vcrel.refer = vc.rid where {alias}.rid = vcrel.owner AND (vc.publisher = ? OR vc.reviewer = ? OR vc.approver = ? OR vc.signatory = ?) group by vcrel.owner)",
525 new Object[] { actorId,
534 /* If the user is contributor */
537 "{alias}.rid in (select rel.owner from contributor_rel rel where {alias}.rid = rel.owner AND rel.refer = ?)",
538 actorId, Hibernate.LONG))
540 /* If the user is author */
541 Restrictions.eq("study.manager.rid", actorId)).add(
542 /* If the study is public */
543 Restrictions.eq("study.visibility",
544 Visibility.PUBLIC)));
546 // User is not logged in - show only public studies
547 topJunction.add(Restrictions.eq("study.visibility",
554 * Add search criteria by dates to the junction filter condition.
557 * the junction filter condition
561 private void addDatesCriteria(final Junction topJunction,
562 final StudySearchFilterDTO filter) {
563 // Filter by creation date
564 addCreationDateCriteria(topJunction, filter, "credate");
565 // Filter by modification date
566 if (filter.getUpdatedAfter() != null) {
567 topJunction.add(Restrictions
568 .gt("lasdate", filter.getUpdatedAfter()));
570 if (filter.getUpdatedBefore() != null) {
571 topJunction.add(Restrictions.lt("lasdate", filter
572 .getUpdatedBefore()));
578 * Add search criteria by dates to the junction filter condition.
581 * the junction filter condition
585 private void addCreationDateCriteria(final Junction topJunction,
586 final SearchFilterDTO filter, final String propName) {
587 // Filter by creation date
588 if (filter.getCreatedAfter() != null) {
589 topJunction.add(Restrictions
590 .gt(propName, filter.getCreatedAfter()));
592 if (filter.getCreatedBefore() != null) {
593 topJunction.add(Restrictions.lt(propName, filter
594 .getCreatedBefore()));
601 * @see org.splat.service.SearchService#selectStudiesWhere(org.splat.dal.bo.som.Study.Properties[])
604 public List<Proxy> selectStudiesWhere(final Study.Properties... sprop) {
605 List<Proxy> result = new ArrayList<Proxy>();
609 // Creation of the Lucene query
610 File indir = getRepositoryService().getRepositoryIndexDirectory();
611 Directory index = FSDirectory.open(indir);
612 IndexSearcher searcher = new IndexSearcher(index, true);
613 BooleanQuery fulquery = new BooleanQuery();
615 for (int i = 0; i < sprop.length; i++) {
616 BooleanQuery query = new BooleanQuery();
617 Term input; // Supposed initialized below at least by the visibility
619 Visibility area = sprop[i].getVisibility(); // Visibility
621 input = new Term("area");
622 query.add(new TermQuery(input.createTerm(area.toString())),
623 BooleanClause.Occur.MUST);
625 ProgressState state = sprop[i].getProgressState(); // State
627 input = new Term("state");
628 if (state == ProgressState.inPROGRESS) {
629 BooleanQuery cristate = new BooleanQuery();
630 cristate.add(new TermQuery(input.createTerm("inWORK")),
631 BooleanClause.Occur.SHOULD);
633 new TermQuery(input.createTerm("inDRAFT")),
634 BooleanClause.Occur.SHOULD);
636 new TermQuery(input.createTerm("inCHECK")),
637 BooleanClause.Occur.SHOULD);
638 query.add(cristate, BooleanClause.Occur.MUST);
640 query.add(new TermQuery(input.createTerm(state
641 .toString())), BooleanClause.Occur.MUST);
644 String refid = sprop[i].getReference(); // Reference
646 input = new Term("ref");
647 query.add(new TermQuery(input.createTerm(refid)),
648 BooleanClause.Occur.MUST);
650 User manager = sprop[i].getManager(); // Author
651 if (manager != null) {
652 input = new Term("author");
653 query.add(new TermQuery(input
654 .createTerm(manager.toString())),
655 BooleanClause.Occur.MUST);
657 User actor = sprop[i].getActor(); // Contributor, Reviewer or Approver
659 input = new Term("actor");
661 new TermQuery(input.createTerm(actor.toString())),
662 BooleanClause.Occur.MUST);
664 String title = sprop[i].getTitle(); // Title
666 input = new Term("contents");
667 BooleanQuery critext = new BooleanQuery();
668 String operator = "AND"; // Future user input
669 BooleanClause.Occur clause = BooleanClause.Occur.MUST;
670 if (operator.equals("OR")) {
671 clause = BooleanClause.Occur.SHOULD;
673 String[] word = title.split(" ");
674 for (int j = 0; j < word.length; j++) {
675 critext.add(new TermQuery(input.createTerm(word[j])),
678 query.add(critext, BooleanClause.Occur.MUST);
680 List<SimulationContext> context = sprop[i]
681 .getSimulationContexts();
682 if (context != null && context.size() > 0) {
683 BooleanQuery critext = new BooleanQuery();
684 for (Iterator<SimulationContext> j = context.iterator(); j
686 SimulationContext seltext = j.next();
687 input = new Term(String.valueOf(seltext.getType()
689 critext.add(new TermQuery(input.createTerm(seltext
690 .getValue())), BooleanClause.Occur.MUST);
692 query.add(critext, BooleanClause.Occur.MUST);
694 fulquery.add(query, BooleanClause.Occur.SHOULD);
696 if (LOG.isInfoEnabled()) {
697 LOG.info("Searching studies by Lucene query \""
698 + fulquery.toString());
700 // Creation of the studies filter
701 BooleanFilter filter = new BooleanFilter();
702 TermsFilter select = new TermsFilter();
703 Term mytype = new Term("class");
704 select.addTerm(mytype.createTerm("Study"));
705 filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD));
707 // Creation of the sort criteria
708 Sort sort = new Sort(new SortField("title", SortField.STRING));
711 TopFieldDocs found = searcher.search(fulquery, filter, hitsize,
714 if (found.totalHits < 1) {
715 return result; // No study found
718 // Construction of the result list
719 ScoreDoc[] hits = found.scoreDocs;
720 for (int i = 0; i < hits.length; i++) {
721 result.add(new IndexServiceImpl.ObjectProxy(searcher
725 } catch (Exception error) {
726 LOG.error("Error during Lucene search, reason:", error);
734 * @see org.splat.service.SearchService#indexStudy(org.splat.dal.bo.som.Study)
737 public void indexStudy(final Study study) {
738 LOG.debug("Index study: id=" + study.getRid() + "; reference="
739 + study.getReference());
741 Study.Properties sprop = new Study.Properties();
742 List<Proxy> index = selectStudiesWhere(sprop.setReference(study
745 if (index.size() != 0) {
746 LOG.debug("The given study is already indexed.");
747 return; // The given study is already indexed
750 IndexService lucin = getIndex();
751 Scenario[] scenes = study.getScenarii();
753 LOG.debug("Number of study " + study.getReference() + " actors: "
754 + study.getActor().size());
756 if (study.getProgressState() != ProgressState.inWORK) {
757 for (int i = 0; i < scenes.length; i++) {
758 List<KnowledgeElement> list = scenes[i]
759 .getAllKnowledgeElements();
760 for (Iterator<KnowledgeElement> j = list.iterator(); j
763 LOG.debug("Knowlegge added: id=" + j.next().getIndex());
767 } catch (Exception error) {
768 LOG.error("Unable to index the study '" + study.getIndex()
769 + "', reason:", error);
774 * Get lucene index handler. Create the index if it is not exist.
776 * @return IndexService
777 * @throws IOException
778 * if error when creating a new lucene index
780 private IndexService getIndex() throws IOException {
781 IndexService lucin = getIndexService();
782 if (IndexWriter.isLocked(FSDirectory.open(getRepositoryService()
783 .getRepositoryIndexDirectory()))) {
784 IndexWriter.unlock(FSDirectory.open(getRepositoryService()
785 .getRepositoryIndexDirectory()));
787 if (!lucin.exists()) {
788 lucin.create(); // Happens when re-indexing all studies
794 * Get the repositoryService.
796 * @return the repositoryService
798 public RepositoryService getRepositoryService() {
799 return _repositoryService;
803 * Set the repositoryService.
805 * @param repositoryService
806 * the repositoryService to set
808 public void setRepositoryService(final RepositoryService repositoryService) {
809 _repositoryService = repositoryService;
813 * Get the indexService.
815 * @return the indexService
817 public IndexService getIndexService() {
818 return _indexService;
822 * Set the indexService.
824 * @param indexService
825 * the indexService to set
827 public void setIndexService(final IndexService indexService) {
828 _indexService = indexService;
832 * Get the studyService.
834 * @return the studyService
836 public StudyService getStudyService() {
837 return _studyService;
841 * Set the studyService.
843 * @param studyService
844 * the studyService to set
846 public void setStudyService(final StudyService studyService) {
847 _studyService = studyService;
853 * @return the studyDAO
855 public StudyDAO getStudyDAO() {
863 * the studyDAO to set
865 public void setStudyDAO(final StudyDAO studyDAO) {
866 _studyDAO = studyDAO;
870 * Get the knowledgeElementDAO.
872 * @return the knowledgeElementDAO
874 public KnowledgeElementDAO getKnowledgeElementDAO() {
875 return _knowledgeElementDAO;
879 * Set the knowledgeElementDAO.
881 * @param knowledgeElementDAO
882 * the knowledgeElementDAO to set
884 public void setKnowledgeElementDAO(
885 final KnowledgeElementDAO knowledgeElementDAO) {
886 _knowledgeElementDAO = knowledgeElementDAO;