View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.search.lucene;
25  
26  import java.util.UUID;
27  import java.util.concurrent.locks.Lock;
28  import java.util.concurrent.locks.ReentrantLock;
29  import net.jcip.annotations.Immutable;
30  import org.apache.lucene.analysis.Analyzer;
31  import org.apache.lucene.analysis.standard.StandardAnalyzer;
32  import org.apache.lucene.store.Directory;
33  import org.apache.lucene.util.Version;
34  import org.modeshape.graph.ExecutionContext;
35  import org.modeshape.graph.search.SearchEngineWorkspace;
36  
37  /**
38   * The {@link SearchEngineWorkspace} implementation for the {@link LuceneSearchEngine}.
39   */
40  @Immutable
41  public class LuceneSearchWorkspace implements SearchEngineWorkspace {
42  
43      /**
44       * Apparently Lucene indexes must always be optimized prior to committing, so this value is set to '1'.
45       */
46      protected static final int CHANGES_BEFORE_OPTIMIZATION = 1;
47  
48      protected static final String INDEX_NAME = "content";
49  
50      /**
51       * Given the name of a property field of the form "<namespace>:<local>" (where <namespace> can be zero-length), this
52       * provider also stores the value(s) for free-text searching in a field named ":ft:<namespace>:<local>". Thus, even if
53       * the namespace is zero-length, the free-text search field will be named ":ft::<local>" and will not clash with any other
54       * property name.
55       */
56      protected static final String FULL_TEXT_PREFIX = ":ft:";
57  
58      /**
59       * This index stores these fields <i>plus</i> all properties. Therefore, we have to worry about name clashes, which is why
60       * these field names are prefixed with '::', which is something that does appear in property names as they are serialized.
61       */
62      static class ContentIndex {
63          public static final String PATH = "::pth";
64          public static final String NODE_NAME = "::nam";
65          public static final String LOCAL_NAME = "::loc";
66          public static final String SNS_INDEX = "::sns";
67          public static final String LOCATION_ID_PROPERTIES = "::idp";
68          public static final String DEPTH = "::dep";
69          public static final String FULL_TEXT = "::fts";
70          public static final String REFERENCES = "::ref";
71      }
72  
73      private final String workspaceName;
74      private final String workspaceDirectoryName;
75      protected final IndexRules rules;
76      private final LuceneConfiguration configuration;
77      protected final Directory contentDirectory;
78      protected final Analyzer analyzer;
79      private final Lock changesLock = new ReentrantLock();
80      private int changes = 0;
81  
82      protected LuceneSearchWorkspace( String workspaceName,
83                                       LuceneConfiguration configuration,
84                                       IndexRules rules,
85                                       Analyzer analyzer ) {
86          assert workspaceName != null;
87          assert configuration != null;
88          this.workspaceName = workspaceName;
89          this.workspaceDirectoryName = workspaceName.trim().length() != 0 ? workspaceName : UUID.randomUUID().toString();
90          this.analyzer = analyzer != null ? analyzer : new StandardAnalyzer(Version.LUCENE_30);
91          this.rules = rules != null ? rules : LuceneSearchEngine.DEFAULT_RULES;
92          this.configuration = configuration;
93          this.contentDirectory = this.configuration.getDirectory(workspaceDirectoryName, INDEX_NAME);
94      }
95  
96      /**
97       * {@inheritDoc}
98       * 
99       * @see org.modeshape.graph.search.SearchEngineWorkspace#getWorkspaceName()
100      */
101     public String getWorkspaceName() {
102         return workspaceName;
103     }
104 
105     /**
106      * {@inheritDoc}
107      * 
108      * @see org.modeshape.graph.search.SearchEngineWorkspace#destroy(org.modeshape.graph.ExecutionContext)
109      */
110     public void destroy( ExecutionContext context ) {
111         configuration.destroyDirectory(workspaceDirectoryName, INDEX_NAME);
112     }
113 
114     /**
115      * @return rules
116      */
117     public IndexRules getRules() {
118         return rules;
119     }
120 
121     /**
122      * Give the number of changes that have been made in a session, determine whether optimization is required on the workspace
123      * indexes.
124      * 
125      * @param changesInSession the number of changes made within a session using this workspace
126      * @return true if the workspace indexes should be optimized, or false otherwise
127      */
128     protected boolean isOptimizationRequired( int changesInSession ) {
129         if (changesInSession == 0) return false;
130         assert changesInSession > 0;
131         try {
132             changesLock.lock();
133             changes += changesInSession;
134             if (changes >= CHANGES_BEFORE_OPTIMIZATION) {
135                 changes = 0;
136                 return true;
137             }
138             return false;
139         } finally {
140             changesLock.unlock();
141         }
142     }
143 }