Introduction
Spring contains some useful design patterns for accessing local resources at runtime in a platform-independent manner.
Properties File
I debated initially whether the dictionary paths should be abstracted to a properties file. To do so without reason beyond "that's how it's always done" isn't sufficient. However, a maintenance justification may be found that any path changes will not require Spring bean changes, potentially eliminating the introduction of configuration errors.
Locations to the dictionary files are listed here:
properties.adjectives.path = dictionaries/adjectives.dat
properties.measurementWords.path = dictionaries/measurement-words.dat
properties.nounPhrases.path = dictionaries/noun-phrases.dat
properties.stopWords.path = dictionaries/stop-words.dat
I've added this properties file to this path:
commons-dict\src\main\resources\config\paths.properties
and the actual sources files (not listed here) exist at that location.
Dictionary Bean
package org.swtk.common.dict.beans; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import org.swtk.common.dict.DictionaryBase; import org.swtk.common.env.LogManager; @Lazy @Component("
nounPhrasesDictionary
") public class
NounPhrasesDictionary
extends DictionaryBase { public static LogManager logger = new LogManager(
NounPhrasesDictionary
.class); @Autowired @Value("
${properties.nounPhrases.path}
") private Resource resource; @Override public Set<String> entries() { return set(resource); } }
Path:
commons-dict\src\main\java\org\swtk\common\dict\beans\NounPhrasesDictionary.java
Dictionary Base
All dictionary beans extend this base class:
package org.swtk.common.dict;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.core.io.Resource;
import org.swtk.common.env.LogManager;
public abstract class DictionaryBase implements Dictionary {
public static LogManager logger = new LogManager(DictionaryBase.class);
private String beanName;
public String getBeanName() {
return beanName;
}
public Set<String> set(Resource resource) {
Set<String> set = new TreeSet<String>();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
for (String line = reader.readLine(); line != null; line = reader.readLine())
set.add(line);
} catch (IOException e) {
logger.error(e, "Failed to Load Dictionary (name = %s)", getBeanName());
}
return set;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
application-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:repository="http://www.springframework.org/schema/data/repository" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.6.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="
org.swtk.common.dict.beans
" /> <context:property-placeholder location="classpath:
config/paths.properties
" /> </beans>
Path:
commons-dict\src\main\resources\config\application-context.xml
Container Access
If you have other projects in your workspace that are not Spring-enabled, you may find it useful to manage the application-context transparently.
I've encapsulated the context in a factory, and exposed each dictionary via an enum parameter:
package org.swtk.common.dict;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.swtk.common.env.LogManager;
public class DictionaryContext {
private static DictionaryContext instance;
public static LogManager logger = new LogManager(DictionaryContext.class);
public static DictionaryContext getInstance() {
if (null == instance) instance = new DictionaryContext();
return instance;
}
private ApplicationContext context;
private DictionaryContext() {}
public void close() {
((ConfigurableApplicationContext) getContext()).close();
setContext(null);
}
private ApplicationContext getContext() {
if (null == context) setContext(new ClassPathXmlApplicationContext("config/application-context.xml"));
return context;
}
public Dictionary getDictionary(DictionaryName dictionary) {
Dictionary dict = getContext().getBean(dictionary.toString(), Dictionary.class);
return dict;
}
private void setContext(ApplicationContext context) {
this.context = context;
}
}
Dictionary Name Enum
Unfortunately, we are maintaining the beanId in two places now: this enum, and within the @Component annotation on each Spring Bean. This increases our maintenance burden slightly, but does make life easier for the consumer. Ultimately, I think this is worth it.
package org.swtk.common.dict;
public enum DictionaryName {
ADJECTIVES("adjectivesDictionary"),
MEASUREMENT_WORDS("measurementWordsDictionary"),
NOUN_PHRASES("nounPhrasesDictionary"),
STOP_WORDS("stopWordsDictionary");
private String beanId;
private DictionaryName(String beanId) {
setBeanId(beanId);
}
private String getBeanId() {
return beanId;
}
private void setBeanId(String beanId) {
this.beanId = beanId;
}
@Override
public String toString() {
return getBeanId();
}
}
Test Client
The test client is simple, and the use of Spring transparent to the consumer:
Dictionary dictionary = DictionaryContext.getInstance().getDictionary(DictionaryName.NOUN_PHRASES);
assertNotNull(dictionary.entries());
assertFalse(dictionary.entries().isEmpty());
No comments:
Post a Comment