working on #72

This commit is contained in:
Shinsuke Sugaya 2013-12-20 23:38:50 +09:00
parent 86b599332e
commit 223787003e
10 changed files with 625 additions and 1 deletions

View file

@ -0,0 +1,13 @@
package jp.sf.fess.synonym;
import jp.sf.fess.FessSystemException;
public class SynonymException extends FessSystemException {
private static final long serialVersionUID = 1L;
public SynonymException(final String message, final Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,144 @@
package jp.sf.fess.synonym;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import jp.sf.fess.Constants;
import org.apache.commons.io.IOUtils;
public class SynonymFile {
private final File file;
final List<SynonymItem> synonymItemList = new ArrayList<SynonymItem>();
public SynonymFile(final File file) {
this.file = file;
}
public void clear() {
synchronized (synonymItemList) {
synonymItemList.clear();
}
}
public void load() {
BufferedReader reader = null;
synchronized (synonymItemList) {
try {
reader = new BufferedReader(new InputStreamReader(
new FileInputStream(file), Constants.UTF_8));
int id = 0;
String line = null;
while ((line = reader.readLine()) != null) {
if (line.length() == 0 || line.charAt(0) == '#') {
continue; // ignore empty lines and comments
}
String inputs[];
String outputs[];
final String sides[] = split(line, "=>");
if (sides.length > 1) { // explicit mapping
if (sides.length != 2) {
throw new IllegalArgumentException(
"more than one explicit mapping specified on the same line");
}
final String inputStrings[] = split(sides[0], ",");
inputs = new String[inputStrings.length];
for (int i = 0; i < inputs.length; i++) {
inputs[i] = unescape(inputStrings[i]).trim();
}
final String outputStrings[] = split(sides[1], ",");
outputs = new String[outputStrings.length];
for (int i = 0; i < outputs.length; i++) {
outputs[i] = unescape(outputStrings[i]).trim();
}
if (inputs.length > 0 && outputs.length > 0) {
final SynonymItem item = new SynonymItem(id,
inputs, inputs);
id++;
synonymItemList.add(item);
}
} else {
final String inputStrings[] = split(line, ",");
inputs = new String[inputStrings.length];
for (int i = 0; i < inputs.length; i++) {
inputs[i] = unescape(inputStrings[i]).trim();
}
if (inputs.length > 0) {
final SynonymItem item = new SynonymItem(id,
inputs, inputs);
id++;
synonymItemList.add(item);
}
}
}
} catch (final IOException e) {
throw new SynonymException("Failed to parse "
+ file.getAbsolutePath(), e);
} finally {
IOUtils.closeQuietly(reader);
}
}
}
private static String[] split(final String s, final String separator) {
final List<String> list = new ArrayList<String>(2);
StringBuilder sb = new StringBuilder();
int pos = 0;
final int end = s.length();
while (pos < end) {
if (s.startsWith(separator, pos)) {
if (sb.length() > 0) {
list.add(sb.toString());
sb = new StringBuilder();
}
pos += separator.length();
continue;
}
char ch = s.charAt(pos++);
if (ch == '\\') {
sb.append(ch);
if (pos >= end) {
break; // ERROR, or let it go?
}
ch = s.charAt(pos++);
}
sb.append(ch);
}
if (sb.length() > 0) {
list.add(sb.toString());
}
return list.toArray(new String[list.size()]);
}
private String unescape(final String s) {
if (s.indexOf("\\") >= 0) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
final char ch = s.charAt(i);
if (ch == '\\' && i < s.length() - 1) {
sb.append(s.charAt(++i));
} else {
sb.append(ch);
}
}
return sb.toString();
}
return s;
}
}

View file

@ -0,0 +1,106 @@
package jp.sf.fess.synonym;
import java.util.Arrays;
public class SynonymItem {
private final String[] inputs;
private final String[] outputs;
private String[] newInputs;
private String[] newOutputs;
private final int id;
public SynonymItem(final int id, final String[] inputs,
final String[] outputs) {
this.id = id;
this.inputs = inputs;
this.outputs = outputs;
Arrays.sort(inputs);
if (inputs != outputs) {
Arrays.sort(outputs);
}
}
public int getId() {
return id;
}
public String[] getNewInputs() {
return newInputs;
}
public void setNewInputs(final String[] newInputs) {
this.newInputs = newInputs;
}
public String[] getNewOutputs() {
return newOutputs;
}
public void setNewOutputs(final String[] newOutputs) {
this.newOutputs = newOutputs;
}
public String[] getInputs() {
return inputs;
}
public String[] getOutputs() {
return outputs;
}
public boolean isUpdated() {
return newInputs != null && newOutputs != null;
}
public void sort() {
if (inputs != null) {
Arrays.sort(inputs);
}
if (outputs != null) {
Arrays.sort(outputs);
}
if (newInputs != null) {
Arrays.sort(newInputs);
}
if (newOutputs != null) {
Arrays.sort(newOutputs);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(inputs);
result = prime * result + Arrays.hashCode(outputs);
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SynonymItem other = (SynonymItem) obj;
sort();
other.sort();
if (!Arrays.equals(inputs, other.inputs)) {
return false;
}
if (!Arrays.equals(outputs, other.outputs)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,136 @@
package jp.sf.fess.synonym;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jp.sf.fess.util.ResourceUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.AbstractFileFilter;
import org.seasar.extension.timer.TimeoutManager;
import org.seasar.extension.timer.TimeoutTarget;
import org.seasar.extension.timer.TimeoutTask;
import org.seasar.framework.container.annotation.tiger.DestroyMethod;
import org.seasar.framework.container.annotation.tiger.InitMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SynonymManager {
private static final Logger logger = LoggerFactory
.getLogger(SynonymManager.class);
protected List<String> searchPathList = new ArrayList<String>();
protected Map<String, SynonymFile> synonymFileMap;
public String synonymFilePrefix = "synonym";
// public String[] excludedSynonymDirs = new String[] { "data", "txlog",
// "lib", "bin", "contrib" };
public Set<String> excludedSynonymSet;
public long keepAlive = 5 * 60 * 1000; // 5min
public int watcherTimeout = 60; // 1min
protected volatile long lifetime = 0;
protected TimeoutTask watcherTargetTask;
@InitMethod
public void init() {
// start
final WatcherTarget watcherTarget = new WatcherTarget();
watcherTargetTask = TimeoutManager.getInstance().addTimeoutTarget(
watcherTarget, watcherTimeout, true);
}
@DestroyMethod
public void destroy() {
if (watcherTargetTask != null && !watcherTargetTask.isStopped()) {
watcherTargetTask.stop();
}
}
public SynonymFile[] getSynonymFiles() {
final Map<String, SynonymFile> fileMap = getSynonymFileMap();
final Collection<SynonymFile> values = fileMap.values();
return values.toArray(new SynonymFile[values.size()]);
}
public SynonymFile getSynonymFile(final String uri) {
final Map<String, SynonymFile> fileMap = getSynonymFileMap();
return fileMap.get(uri);
}
protected Map<String, SynonymFile> getSynonymFileMap() {
synchronized (this) {
if (lifetime > System.currentTimeMillis() && synonymFileMap != null) {
lifetime = System.currentTimeMillis() + keepAlive;
return synonymFileMap;
}
final Map<String, SynonymFile> newFileMap = new LinkedHashMap<String, SynonymFile>();
for (final String path : searchPathList) {
final File[] files = findFiles(path);
for (final File file : files) {
if (logger.isInfoEnabled()) {
logger.info("Synonym File: " + file.getAbsolutePath());
}
newFileMap.put(file.getAbsolutePath(),
new SynonymFile(file));
}
}
synonymFileMap = newFileMap;
lifetime = System.currentTimeMillis() + keepAlive;
return synonymFileMap;
}
}
protected File[] findFiles(final String path) {
final Collection<File> files = FileUtils.listFiles(new File(path),
new AbstractFileFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.startsWith(synonymFilePrefix);
}
}, new AbstractFileFilter() {
@Override
public boolean accept(final File dir, final String name) {
return excludedSynonymSet == null
|| !excludedSynonymSet.contains(name);
}
});
return files.toArray(new File[files.size()]);
}
public void addSearchPath(final String path) {
searchPathList.add(ResourceUtil.resolve(path));
}
protected class WatcherTarget implements TimeoutTarget {
@Override
public void expired() {
synchronized (SynonymManager.this) {
if (lifetime <= System.currentTimeMillis()
&& synonymFileMap != null) {
if (logger.isDebugEnabled()) {
logger.debug("Cleaning synonym files: "
+ synonymFileMap);
}
synonymFileMap = null;
}
}
}
}
}

View file

@ -18,6 +18,8 @@ package jp.sf.fess.util;
import java.io.File;
import java.io.FilenameFilter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
@ -85,4 +87,26 @@ public class ResourceUtil {
}
});
}
public static String resolve(final String value) {
if (value == null) {
return null;
}
final StringBuffer tunedText = new StringBuffer(value.length());
final Pattern pattern = Pattern.compile("(\\$\\{([\\w\\.]+)\\})");
final Matcher matcher = pattern.matcher(value);
while (matcher.find()) {
final String key = matcher.group(2);
String replacement = System.getProperty(key);
if (replacement == null) {
replacement = matcher.group(1);
}
matcher.appendReplacement(tunedText,
replacement.replace("$", "\\$"));
}
matcher.appendTail(tunedText);
return tunedText.toString();
}
}

View file

@ -9,6 +9,7 @@
<include path="fess.dicon"/>
<include path="fess_suggest.dicon"/>
<include path="fess_job.dicon"/>
<include path="fess_api.dicon"/>
<include path="mobylet.dicon"/>

View file

@ -4,7 +4,6 @@
<components>
<include path="solrlib.dicon"/>
<include path="fess_ds.dicon"/>
<include path="fess_api.dicon"/>
<component name="crawlerProperties" class="org.codelibs.core.util.DynamicProperties">
<arg>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component name="synonymManager" class="jp.sf.fess.synonym.SynonymManager">
<initMethod name="addSearchPath">
<arg>"${solr.solr.home}"</arg>
</initMethod>
</component>
</components>

View file

@ -0,0 +1,111 @@
/*
* Copyright 2009-2013 the Fess Project and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package jp.sf.fess.synonym;
import java.io.File;
import java.util.HashSet;
import jp.sf.fess.Constants;
import org.apache.commons.io.FileUtils;
import org.seasar.extension.unit.S2TestCase;
import org.seasar.framework.util.FileUtil;
public class SynonymManagerTest extends S2TestCase {
private File testDir;
private File testDataDir;
private File testCoreDir;
private File synonymFile1;
private File synonymFile2;
private File synonymFile3;
private File dummyFile1;
private File dummyFile2;
@Override
protected void setUp() throws Exception {
testDir = File.createTempFile("synonymtest", "_dir");
testDir.delete();
testDir.mkdirs();
testDataDir = new File(testDir, "data");
testDataDir.mkdirs();
testCoreDir = new File(testDir, "core");
testCoreDir.mkdirs();
synonymFile1 = new File(testDir, "synonym.txt");
FileUtil.write(synonymFile1.getAbsolutePath(),
"abc=>123\nxyz,890".getBytes(Constants.UTF_8));
synonymFile2 = new File(testDataDir, "synonym_data.txt");
FileUtil.write(synonymFile2.getAbsolutePath(),
"abc=>123\nxyz,890".getBytes(Constants.UTF_8));
synonymFile3 = new File(testCoreDir, "synonym_core.txt");
FileUtil.write(synonymFile3.getAbsolutePath(),
"abc=>123\nxyz,890".getBytes(Constants.UTF_8));
dummyFile1 = new File(testDir, "dummy.txt");
FileUtil.write(dummyFile1.getAbsolutePath(),
"dummy 1".getBytes(Constants.UTF_8));
dummyFile2 = new File(testCoreDir, "dummy.txt");
FileUtil.write(dummyFile2.getAbsolutePath(),
"dummy 2".getBytes(Constants.UTF_8));
}
@Override
protected void tearDown() throws Exception {
FileUtils.deleteDirectory(testDir);
}
public void test_findFiles() {
final SynonymManager synonymManager = new SynonymManager();
synonymManager.excludedSynonymSet = new HashSet<String>();
synonymManager.excludedSynonymSet.add("data");
final File[] files = synonymManager
.findFiles(testDir.getAbsolutePath());
assertEquals(2, files.length);
assertEquals(synonymFile1.getAbsolutePath(), files[0].getAbsolutePath());
assertEquals(synonymFile3.getAbsolutePath(), files[1].getAbsolutePath());
}
public void test_getSynonymFiles() throws Exception {
final SynonymManager synonymManager = new SynonymManager();
synonymManager.keepAlive = 1000;
synonymManager.watcherTimeout = 1;
synonymManager.excludedSynonymSet = new HashSet<String>();
synonymManager.excludedSynonymSet.add("data");
synonymManager.addSearchPath(testDir.getAbsolutePath());
synonymManager.init();
final SynonymFile[] synonymFiles = synonymManager.getSynonymFiles();
assertEquals(2, synonymFiles.length);
assertNotNull(synonymManager.synonymFileMap);
Thread.sleep(2000);
assertNull(synonymManager.synonymFileMap);
final SynonymFile[] synonymFiles2 = synonymManager.getSynonymFiles();
assertEquals(2, synonymFiles2.length);
assertNotNull(synonymManager.synonymFileMap);
Thread.sleep(2000);
assertNull(synonymManager.synonymFileMap);
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright 2009-2013 the Fess Project and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package jp.sf.fess.util;
import org.seasar.extension.unit.S2TestCase;
public class ResourceUtilTest extends S2TestCase {
public void test_resolve() {
String value;
value = null;
assertNull(ResourceUtil.resolve(value));
value = "";
assertEquals("", ResourceUtil.resolve(value));
value = "a";
assertEquals(value, ResourceUtil.resolve(value));
value = "${a}";
assertEquals(value, ResourceUtil.resolve(value));
value = "$a";
assertEquals(value, ResourceUtil.resolve(value));
value = "${a";
assertEquals(value, ResourceUtil.resolve(value));
value = "$a}";
assertEquals(value, ResourceUtil.resolve(value));
value = "${abc}";
assertEquals(value, ResourceUtil.resolve(value));
value = "${abc.xyz}";
assertEquals(value, ResourceUtil.resolve(value));
System.setProperty("abc", "123");
value = "${abc}";
assertEquals("123", ResourceUtil.resolve(value));
value = "xxx${abc}zzz";
assertEquals("xxx123zzz", ResourceUtil.resolve(value));
value = "${abc.xyz}";
assertEquals(value, ResourceUtil.resolve(value));
System.setProperty("abc.xyz", "789");
value = "${abc.xyz}";
assertEquals("789", ResourceUtil.resolve(value));
value = "${abc}${abc.xyz}";
assertEquals("123789", ResourceUtil.resolve(value));
value = "xxx${abc.xyz}zzz";
assertEquals("xxx789zzz", ResourceUtil.resolve(value));
value = "${\\$}";
assertEquals(value, ResourceUtil.resolve(value));
}
}