1
19
20 package com.liferay.portal.search.lucene;
21
22 import com.liferay.portal.SystemException;
23 import com.liferay.portal.kernel.log.Log;
24 import com.liferay.portal.kernel.log.LogFactoryUtil;
25 import com.liferay.portal.kernel.util.GetterUtil;
26 import com.liferay.portal.model.Company;
27 import com.liferay.portal.model.CompanyConstants;
28 import com.liferay.portal.service.CompanyLocalServiceUtil;
29 import com.liferay.portal.util.PropsKeys;
30 import com.liferay.portal.util.PropsUtil;
31 import com.liferay.portal.util.PropsValues;
32 import com.liferay.util.SystemProperties;
33
34 import java.io.File;
35 import java.io.IOException;
36
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.concurrent.Semaphore;
41
42 import org.apache.lucene.analysis.SimpleAnalyzer;
43 import org.apache.lucene.index.IndexReader;
44 import org.apache.lucene.index.IndexWriter;
45 import org.apache.lucene.index.Term;
46 import org.apache.lucene.store.Directory;
47 import org.apache.lucene.store.FSDirectory;
48
49
69 public class IndexWriterFactory {
70
71 public IndexWriterFactory() {
72 if (PropsValues.INDEX_READ_ONLY) {
73 return;
74 }
75
76
78 try {
79 List<Company> companies = CompanyLocalServiceUtil.getCompanies();
80
81 for (Company company : companies) {
82 _lockLookup.put(company.getCompanyId(), new Semaphore(1));
83 }
84
85 _lockLookup.put(CompanyConstants.SYSTEM, new Semaphore(1));
86 }
87 catch (SystemException se) {
88 _log.error(se);
89 }
90 }
91
92 public void acquireLock(long companyId, boolean needExclusive)
93 throws InterruptedException {
94
95 if (PropsValues.INDEX_READ_ONLY) {
96 return;
97 }
98
99 Semaphore lock = _lockLookup.get(companyId);
100
101 if (lock != null) {
102
103
107 if (needExclusive) {
108 synchronized (_lockLookup) {
109 _needExclusiveLock++;
110 }
111 }
112
113 try {
114 lock.acquire();
115 }
116 finally {
117 if (needExclusive) {
118 synchronized (_lockLookup) {
119 _needExclusiveLock--;
120 }
121 }
122 }
123 }
124 else {
125 if (_log.isWarnEnabled()) {
126 _log.warn("IndexWriterFactory lock not found for " + companyId);
127 }
128 }
129 }
130
131 public void deleteDocuments(long companyId, Term term)
132 throws InterruptedException, IOException {
133
134 if (PropsValues.INDEX_READ_ONLY) {
135 return;
136 }
137
138 try {
139 acquireLock(companyId, true);
140
141 IndexReader reader = null;
142
143 try {
144 reader = IndexReader.open(LuceneUtil.getLuceneDir(companyId));
145
146 reader.deleteDocuments(term);
147 }
148 finally {
149 if (reader != null) {
150 reader.close();
151 }
152 }
153 }
154 finally {
155 releaseLock(companyId);
156 }
157 }
158
159 public IndexWriter getWriter(long companyId, boolean create)
160 throws IOException {
161
162 if (PropsValues.INDEX_READ_ONLY) {
163 return getReadOnlyIndexWriter();
164 }
165
166 boolean hasError = false;
167 boolean newWriter = false;
168
169 try {
170
171
174 if (_needExclusiveLock > 0) {
175 acquireLock(companyId, false);
176 releaseLock(companyId);
177 }
178
179 synchronized (this) {
180 IndexWriterData writerData = _writerLookup.get(companyId);
181
182 if (writerData == null) {
183 newWriter = true;
184
185 acquireLock(companyId, false);
186
187 IndexWriter writer = new IndexWriter(
188 LuceneUtil.getLuceneDir(companyId),
189 LuceneUtil.getAnalyzer(), create);
190
191 writer.setMergeFactor(_MERGE_FACTOR);
192
193 writerData = new IndexWriterData(companyId, writer, 0);
194
195 _writerLookup.put(companyId, writerData);
196 }
197
198 writerData.setCount(writerData.getCount() + 1);
199
200 return writerData.getWriter();
201 }
202 }
203 catch (Exception e) {
204 hasError = true;
205
206 _log.error("Unable to create a new writer", e);
207
208 throw new IOException("Unable to create a new writer");
209 }
210 finally {
211 if (hasError && newWriter) {
212 try {
213 releaseLock(companyId);
214 }
215 catch (Exception e) {
216 }
217 }
218 }
219 }
220
221 public void releaseLock(long companyId) {
222 if (PropsValues.INDEX_READ_ONLY) {
223 return;
224 }
225
226 Semaphore lock = _lockLookup.get(companyId);
227
228 if (lock != null) {
229 lock.release();
230 }
231 }
232
233 public void write(long companyId) {
234 if (PropsValues.INDEX_READ_ONLY) {
235 return;
236 }
237
238 IndexWriterData writerData = _writerLookup.get(companyId);
239
240 if (writerData != null) {
241 decrement(writerData);
242 }
243 else {
244 if (_log.isWarnEnabled()) {
245 _log.warn("IndexWriterData not found for " + companyId);
246 }
247 }
248 }
249
250 public void write(IndexWriter writer) throws IOException {
251 if (PropsValues.INDEX_READ_ONLY) {
252 return;
253 }
254
255 boolean writerFound = false;
256
257 synchronized (this) {
258 if (!_writerLookup.isEmpty()) {
259 for (IndexWriterData writerData : _writerLookup.values()) {
260 if (writerData.getWriter() == writer) {
261 writerFound = true;
262
263 decrement(writerData);
264
265 break;
266 }
267 }
268 }
269 }
270
271 if (!writerFound) {
272 try {
273 _optimizeCount++;
274
275 if ((_OPTIMIZE_INTERVAL == 0) ||
276 (_optimizeCount >= _OPTIMIZE_INTERVAL)) {
277
278 writer.optimize();
279
280 _optimizeCount = 0;
281 }
282 }
283 finally {
284 writer.close();
285 }
286 }
287 }
288
289 protected void decrement(IndexWriterData writerData) {
290 if (writerData.getCount() > 0) {
291 writerData.setCount(writerData.getCount() - 1);
292
293 if (writerData.getCount() == 0) {
294 _writerLookup.remove(writerData.getCompanyId());
295
296 try {
297 IndexWriter writer = writerData.getWriter();
298
299 try {
300 _optimizeCount++;
301
302 if ((_OPTIMIZE_INTERVAL == 0) ||
303 (_optimizeCount >= _OPTIMIZE_INTERVAL)) {
304
305 writer.optimize();
306
307 _optimizeCount = 0;
308 }
309 }
310 finally {
311 writer.close();
312 }
313 }
314 catch (Exception e) {
315 _log.error(e, e);
316 }
317 finally {
318 releaseLock(writerData.getCompanyId());
319 }
320 }
321 }
322 }
323
324 protected IndexWriter getReadOnlyIndexWriter() {
325 if (_readOnlyIndexWriter == null) {
326 try {
327 if (_log.isInfoEnabled()) {
328 _log.info("Disabling writing to index for this process");
329 }
330
331 _readOnlyIndexWriter = new ReadOnlyIndexWriter(
332 getReadOnlyLuceneDir(), new SimpleAnalyzer(), true);
333 }
334 catch (IOException ioe) {
335 throw new RuntimeException(ioe);
336 }
337 }
338
339 return _readOnlyIndexWriter;
340 }
341
342 protected Directory getReadOnlyLuceneDir() throws IOException {
343 if (_readOnlyLuceneDir == null) {
344 String tmpDir = SystemProperties.get(SystemProperties.TMP_DIR);
345
346 File dir = new File(tmpDir + "/liferay/lucene/empty");
347
348 dir.mkdir();
349
350 _readOnlyLuceneDir = FSDirectory.getDirectory(dir.getPath(), false);
351 }
352
353 return _readOnlyLuceneDir;
354 }
355
356 private static final int _MERGE_FACTOR = GetterUtil.getInteger(
357 PropsUtil.get(PropsKeys.LUCENE_MERGE_FACTOR));
358
359 private static final int _OPTIMIZE_INTERVAL = GetterUtil.getInteger(
360 PropsUtil.get(PropsKeys.LUCENE_OPTIMIZE_INTERVAL));
361
362 private static Log _log = LogFactoryUtil.getLog(IndexWriterFactory.class);
363
364 private FSDirectory _readOnlyLuceneDir = null;
365 private IndexWriter _readOnlyIndexWriter = null;
366 private Map<Long, Semaphore> _lockLookup = new HashMap<Long, Semaphore>();
367 private Map<Long, IndexWriterData> _writerLookup =
368 new HashMap<Long, IndexWriterData>();
369 private int _needExclusiveLock = 0;
370 private int _optimizeCount = 0;
371
372 }