Shinsuke Sugaya 11 سال پیش
والد
کامیت
223787003e

+ 13 - 0
src/main/java/jp/sf/fess/synonym/SynonymException.java

@@ -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);
+    }
+
+}

+ 144 - 0
src/main/java/jp/sf/fess/synonym/SynonymFile.java

@@ -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;
+    }
+
+}

+ 106 - 0
src/main/java/jp/sf/fess/synonym/SynonymItem.java

@@ -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;
+    }
+
+}

+ 136 - 0
src/main/java/jp/sf/fess/synonym/SynonymManager.java

@@ -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;
+                }
+            }
+        }
+    }
+}

+ 24 - 0
src/main/java/jp/sf/fess/util/ResourceUtil.java

@@ -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();
+    }
 }

+ 1 - 0
src/main/resources/app.dicon

@@ -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"/>
 

+ 0 - 1
src/main/resources/fess.dicon

@@ -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>

+ 11 - 0
src/main/resources/fess_synonym.dicon

@@ -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>

+ 111 - 0
src/test/java/jp/sf/fess/synonym/SynonymManagerTest.java

@@ -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);
+    }
+}

+ 79 - 0
src/test/java/jp/sf/fess/util/ResourceUtilTest.java

@@ -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));
+
+    }
+
+}