How to load text file to HashMap at application startup? - java

I'm developing a little project, a web application where I run some analysis on data based on user text input.
To do so, I need to use a Map where I have words and corresponding scores for that word.
This is what I tried:
public class EnDict {
private static Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("abandon", -2);
put("ability", 2);
put("abuse", -3);
//and so on, thousands of pairs
}
}
}
It works, but this way I need to have the key/value pairs hard-coded in my class. So, if I want to add more pairs, I have to write code, instead of just adding pairs do a text file. Doesn't seem good.
So I want to obtain this pairs from a text file. Also, I need this Map to be created when the application starts, so when any user makes a request the Map is already loaded, and can be used by the analysis logic. I mean, the Map must be in memory before the first request happens, and last in memory after that, to be used in subsequent requests. And it need to be visible from anywhere in my application (maybe this part wasn't very clear, but I don't know how to explain it better).
I've tried some research, but haven't found answers to this specific part of keeping the Map in memory since the application start. It's something similar to ASP.NET Application_Start method, in the Global class.
I'm very new to programming and specially to Java, so maybe I'm completely misled about how would be the best way of achieving this task. If that is the case, some tip would be appreciated.
I'm using Servlets, JSP and Tomcat.
Edit:
Actually, it would not be only one Map. There will be several Maps, and those Maps can have some keys that are identical.

Define this map as static - it will be in memory until class loader who loaded this class is not garbage collected.
I say above by refering : http://docs.oracle.com/javase/specs/jls/se5.0/html/execution.html#12.7
Static member are linked to class and above specification says that classes will not be unloaded until class loader is in place.
Whereas objects do get garbage collected. Hence suggested to make map static(make it public too in case needs access from outside).
And for loading file into map
store it in file as
key1=value1
key2=value2
....
....
now use BufferedReader as below
BufferedReader reader = new BufferedReader(new FileReader(new File("pathname")));
String line = null;
Map<String, Integer> map = new HashMap<String, Integer>();// it should be static - whereever you define
while ((line = reader.readLine()) != null) {
if (line.contains("=")) {
String[] strings = line.split("=");
map.put(strings[0], Integer.parseInt(strings[1]));
}
}
Class loader is something which loads classes in memory while starting the application. Tomcat also has its classloader which loads required classes in memory(Classes and not objects). Now we know that static variables are associated with class and not object. So static members are loaded in memory along with class. IN many other cases you would be creating object of the class and use it. If you have millions of objects loaded in memory- You will be soon short of it. So java have something called garbage collector. This garbage collector removes unwanted/old objects from memory to recycle it. Garbage collector removes objects not classes and hence static member still remains in memory.

You can staticaly initialize static variable in static block like this:
private static Map<String, Integer> map = new HashMap<String,Integer>();
static {
fillMap(map, "filename.txt");
}
private static void fillMap(Map<String, Integer> map, String fileName) {
// here comes file reading code with loop
}
How to read file see something like this Reading a plain text file in Java.
As far as its all static map will be initialized on application startup.

Try this for loading the text file into your application: Read from a Text File into a hash map or list
When I was just starting to program, I know I was tempted to use a lot of global variables. As it turns out, this is usually not the best strategy (see http://c2.com/cgi/wiki?GlobalVariablesAreBad).
Perhaps you can load your dictionary first thing in your main method, and pass it through to other methods that need later on.

You can define a Listener on web.xml file:
<listener>
<listener-class>my.Listener</listener-class>
</listener>
and you implement the class:
package my;
public class Listener implements javax.servlet.ServletContextListener {
public void contextInitialized(ServletContext context) {
File file = new File();
fileEntries = ... // load your entries
for (Object[] line : fileEntries) {
YourClass.get().addElement((String) line[0], Integer.parseInt(line[1].toString());
}
}
}
if you want to access your Map application-wide, just create a singleton or use Spring to have the class managed, if a singleton do something like:
public class YourClass {
private static final YourClass INSTANCE = new YourClass();
private Map<String, Integer> yourMap;
private YourClass() {
yourMap = new HashMap<>();
}
public static final YourClass get() {
return INSTANCE;
}
public void addElement(String key, Integer value) {
yourMap.put(key, value);
}
public Integer getValueForKey(String key) {
return yourMap.get(key);
}
}
and so you can access the elements from anywhere in the application via:
YourClass.get().getValueForKey("yourKey");

I would suggest you use Properties to store/load key/value pairs and implement Singleton pattern to access these properties. Something like this:
public class EnDict {
private Properties properties;
private static EnDict enDictInstance;
private EnDict {
properties = new Properties();
FileInsputStream fis = null;
try{
fis = new FileInputStream("yourPropertiesFile.properties");
properties.load(fis);
fis.close();
} catch(IOException ex) {
/* log the exception */
} finally {
try {
fis.close();
} catch (IOException ignored) {}
}
}
public static EnDict getEnDictInstance(){
if(enEdictInstance == null) {
enEdictInstance = new EnEdict();
}
return enEdictInstance;
}
public Integer getValue(String key){
String value = properties.getProperty(key);
return Integer.valueOf(value);
}
public void setNewWord(String word, Integer value){
properties.setProperty(word, value.toString());
}
public void saveProperties() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("yourPropertiesFile.properties");
properties.store(fos, "Some comments");
fos.close();
} catch (IOException ex) {
/* log the exception */
} finally {
try{
fos.close();
} catch(IOException ignored){}
}
}
}
As #Mauren pointed out just keep in mind Properties doesn't allow null values.
Also instead of .properties files you can use XML files. See Loading Properties from XML

For loading the constants from different resources to different java classes you can use apache commons configuration library http://commons.apache.org/proper/commons-configuration/
For application start up you can use
<servlet>
<servlet-name>StartUp</servlet-name>
<display-name>StartUp Servlet</display-name>
<servlet-class>foo.bar.YourStartUpServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>

Related

Writing child instance variables to JSONObject

So recently I figured out how to work with JSON in java, and created code that writes, reads and updates information (mostly classes) to and from my JSON database. In my class 'Activity' I have a method that writes the class object to JSON:
public void createActivity() {
File file = database;
JSONObject newActivity = new JSONObject();
setRegDate(LocalDate.now());
try {
actID = IO.getJsonArray(file, "Activities").length() + 1;
} catch (Exception e) {
System.out.println("Exception: Could not set new activity ID.");
}
newActivity.put("userID", userID);
newActivity.put("actDate", actDate);
newActivity.put("regDate", regDate);
newActivity.put("actID", actID);
newActivity.put("description", description);
newActivity.put("coach", coach);
try {//Writes new user JSONObject to account file.
IO.putObjInArr(file, newActivity, "Activities");
} catch (Exception e) {
System.out.println("Exception: Creating activity failed.");
}
}
As the learning process continue, I have now added child-classes to my project. One child-class may contain the instance variables 'distance' and 'time'. There are several child-classes.
Now of course, I do not want to copy the above method to every child-class and there add the specific variables to it. I want all of this centralised in one parent-class.
I wonder, is it possible to somehow loop over all of the possible child-classes' variables, so that I can write those to JSON? Or are child-variables simply not visible to the parent, let alone if I don't specifiy to the parent which variables those might be?
For now, all that I can think of is to put all instance variables of the child class in a hashmap, send them as arguments to Activity.createActivity, and there loop over all the elements of the hashmap.
The problem you've faced has two main causes:
The functionality of this method is tightly coupled to the needs of a particular class. And as a consequence can be difficult to reuse in other classes.
The method itself violates the first principle of SOLID, the Single responsibility principle, which states that a class should have only one reason to change. But there's a whole lot of things happening in createActivity(): it reads JSON from a file, it alters the JSONobject, it updates the file.
Basically, all pieces of functionality that can be observed createActivity() should not be coupled to any class. Instead, this method can be split into several static methods, each representing a separate responsibility. And this method can be grouped into a Utility class.
That's how such class might look like:
public class JsonUtils {
private JsonUtils() {} // no way and no need to invoke the constructor of this class
public JSONObject readFromFile(Path path) throws IOException {
String json = Files.readString(path);
return new JSONObject(json);
}
public void updateJsonObject(Map<String, ?> map, JSONObject json) {
map.forEach(json::put);
}
public void writeToFile(Path path, JSONObject json) throws IOException {
Files.writeString(path,json.toString());
}
public void writeToFile(Path path, JSONObject json, OpenOption... options) throws IOException {
Files.writeString(path,json.toString(), options);
}
// other methods
}
Note: File class is legacy. The recommended alternative is to use Path from NIO API. And if you make use of Files utility class from NIO.2 as shown in the example above, it would immensely simplify code of IO-processing methods.

Design issue on static initialization block

I have a design question: let me expalin in simple example:
Public class A()
{
public static HashMap map = new HashMap();
public static String url = "default";
static {
getJson();
}
//url getters and setters are defined
public static getJson() {
//code which uses url to get json and populate hashmap
}
public string getresult(String key) {
//uses hashmap to send result.
}
I am using static initialization block because i want to get json only once.
public class B {
//here I want to change url and call getJson method. If i call A.setUrl() then before setting url, A.getJson () method is called as it is in static initialization block.how can i set url first and then call getJson().
//is this a bad design?
}
Yes, it is bad design:
It is impossible to customize where A gets its data from without modifying the definition of A. Among other things, this prevents unit testing (as you probably don't want to fail the unit test if the network is unavailable ...).
If initialization fails (for instance because the remote URL is currently unavailable), you can't easily catch that exception, as you don't know which access triggered loading. You can't throw a checked exception from a static initializer. You can't retry initialization either (all subsequent access immediately result in an exception).
If you must access A through a static field, I'd recommend:
public class A {
private static Map<String, String> map;
/** must be invoked before get is first called */
public static void init(Map<String, String> newmap) {
map = newmap;
}
public static String get(String key) {
return map.get(key);
}
}
This separates the concern of using the data from the concern of obtaining it, allowing each to be replaced and tested independently.
Also consider getting rid of static, as it enforces there is only ever one map in your entire application at the same time, which is quite inflexible. (See the second code sample in Ajay's answer for how)
This should work I guess. Add a new method.
public static void getJson(String url) {
setUrl(url);
getJSon();
}
Static Initializers are generally a bad idea because unit testing becomes difficult .
Check out Misko Hevery's Guide to writing Testable Code.
You can rework the design by doing something like this :
public class A {
//Add generics
private Map map = new HashMap();
public A(Map map){
this.map = map;
}
public String getresult(String key) {
//uses hashmap to send result.
}
}
//Helper Class
public class URLToJSon() {
//Add private constructor
public static Map convertUrlToJSon(String url) {
//do the conversion and return a hashmap
}
}
In this manner we get to follow the Single Responsibility Principle.
Now both the classes are testable as well.
Where is the URL set? In the constructor? If so, simply do
//Normal init stuff like set url here, followed by
if (! some check for if json is set) {
setJson();
}

How to hold collection of files throughout application?

In my mobile application I need to hold a collection of File objects (pictures, documents) that can be accessed throughout the whole application and users can make various operations over the collection:
view all/individual files
upload subsets of the collection to a server
share individual files
...
The collection is initialized only once.
I was wondering if it is a good idea to use a singleton pattern as an object that holds the collection so I do not have to initialize the collection every time user opens a particular screen?
Absolutely, that's the purpose of the singleton pattern.
From Wikipedia, The Singleton Pattern is
useful when exactly one object is
needed to coordinate actions across
the system.
Example:
public class SingletonCollection {
private Collection<File> fileCollection;
private static SingletonCollection instance;
private SingletonCollection() {
fileCollection = new ArrayList<File>();
}
public static SingletonCollection getInstance() {
if (instance == null) {
instance = new SingletonCollection();
}
reutrn instance;
}
public void addFile(File f) {
fileCollection.add(f);
}
public Collection<File> getFiles() {
return fileCollection;
}
}
For Java >=1.5
public enum FileCollector
{
INSTANCE;
private FileCollector()
{
List _temp = new ArrayList();
File f = new File("properties");
_temp.add(f);
fileContainer = Collections.unmodifiableList(_temp);
}
private final Collection<File> fileContainer;
public Collection<File> getFiles() {
return fileContainer;
}
}
If collection is initialized only once then use singleton. No doubt.
If you are using Java ME try RecordStore. You can access it from anywhere in the application.

Java: Reflection overuse?

I am wondering whether I am overusing java reflection.
I have a class which is a data holder for a couple of maps. I have public get(...) methods which given a key as input return the value associated with it in the corresponding map.
Since the maps are large I load them only when I actually want to access them. So, in every get(...) methods, I check whether the map is null. If it is, I call the corresponding loadMap(..) method.
Here is a sample code snippet
public getId(String name)
{
try
{
if(nameMap1 == null)
loadNameMap1();
} catch(...) {....}
return nameMap1.getId(name);
}
The problem is that I have multiple maps. So, for loading each map I have a different loadMap(..) method and the try catch block in the get(...) methods. So, instead of that I wrote a method called loadMap(Object map, String methodName) which uses reflection to call the appropriate method, and handles all exceptions.
private synchronized void loadMap(Object map, String methodName)
{
if (map == null)
try
{
Method method = this.getClass().getDeclaredMethod(methodName, new Class[0]);
method.invoke(this, new Object[0]);
}
catch (..)
}
Am I overusing reflection here? Is there a better way to do this? Does this qualify as "limited use of reflection" as written in Effective Java by Joshua Bloch
(Side note: I cannot refactor the class into multiple classes )
// could also be static
private Map<String, Callable<Map>> myLoaders;
private synchronized void loadMap(Object map, String mapName)
{
if (map == null)
try
{
Callable<Map> mapLoader = myLoaders.get(mapName);
map = mapLoader.call();
}
catch (..)
}
// and in the constructor or other init code
myLoaders.put("map1", new Callable<Map>(){
Map call(){
// load map 1
}});
I think, though that if all you are doing is move a common try/catch logic from a couple of methods were it needs to be repeated to a single place, this is the wrong approach. You lose a lot of compiler error checking support this way. Some people would use a tool like Aspect/J for this, but I think you just have to live with the fact that Java has no real facility for this, reduce the clutter to a minimum by using shared private functions, and accept the couple of copy/pasted lines. As long as there is no "real code" in those lines, it is not really harmful code duplication.
So:
public getId(String name){
try{
if (nameMap1 == null)
loadNameMap1();
}
catch (....){
privateHelperFunctionThatCutsThisDownToOneLine(name, "id", "nameMap1");
}
}
// you are left with the above repetitive three (or seven) lines,
// but that is Java for you...
// in return, you get nice, static compile-time error checking
private void privateHelperFunctionThatCutsThisDownToOneLine(){
// all the long repeated code in the exception handler
// goes here.
}
You don't want to load all the maps because they are too large. But using your method you're gonna end up with everything loaded in memory eventually. You may have a look at ehcache which may be configured a a lazy map system with element eviction when no longer needed.
I'd say yes you are overusing reflection.
Perhaps you should take a more OO approach
public interface MapMaker <K,V> {
public Map<K,V> create();
}
public class LazyMap<K,V> implements Map<K,V> {
private MapMaker<K,V> creation;
private Map<K,V> theMap = null;
public LazyMap( MapMaker<K,V> creation) {
this.creation=creation;
}
protected Map<K,V> getMap() {
if( theMap == null) {
synchronized(this) {
if( theMap == null ) {
theMap = creation.create();
}
}
}
return theMap;
}
//Map interface
public V get(Object key) { return getMap().get(key); }
//repeat for all
}

How build my own Application Setting

I want to build a ApplicationSetting for my application. The ApplicationSetting can be stored in a properties file or in a database table. The settings are stored in key-value pairs. E.g.
ftp.host = blade
ftp.username = dummy
ftp.pass = pass
content.row_pagination = 20
content.title = How to train your dragon.
I have designed it as follows:
Application settings reader:
interface IApplicationSettingReader {
Map read();
}
DatabaseApplicationSettingReader implements IApplicationSettingReader {
dao appSettingDao;
Map read() {
List<AppSettingEntity> listEntity = appSettingsDao.findAll();
Map<String, String> map = new HaspMap<String, String>();
foreach (AppSettingEntity entity : listEntity) {
map.put(entity.getConfigName(), entity.getConfigValue());
}
return new AppSettings(map);
}
}
DatabaseApplicationSettingReader implements IApplicationSettingReader {
dao appSettingDao;
Map read() {
//read from some properties file
return new AppSettings(map);
}
}
Application settings class:
AppSettings {
private static AppSettings instance = new AppSettings();
private Map map;
private AppSettings() {
}
public static AppSettings getInstance() {
if (instance == null) {
throw new RuntimeException("Object not configure yet");
}
return instance;
}
public static configure(IApplicationSettingReader reader) {
this.map = reader.read();
}
public String getFtpSetting(String param) {
return map.get("ftp." + param);
}
public String getContentSetting(String param) {
return map.get("content." + param);
}
}
Test class:
AppSettingsTest {
IApplicationSettingReader reader;
#Before
public void setUp() throws Exception {
reader = new DatabaseApplicationSettingReader();
}
#Test
public void getContentSetting_should_get_content_title() {
AppSettings.configure(reader);
Instance settings = AppSettings.getInstance();
String title = settings.getContentSetting("title");
assertNotNull(title);
Sysout(title);
}
}
My questions are:
Can you give your opinion about my code, is there something wrong ?????
I configure my application setting once, while the application start, I configure the application setting with appropriate reader (DbReader or PropertiesReader), I make it singleton because the application just have one instance of ApplicationSettngs. The problem is, when some user edit the database or file directly to database or file, I can't get the changed values. Now, I want to implement something like ApplicationSettingChangeListener. So if the data changes, I will refresh my application settings. Do you have any suggestions how this can be implementedb ????
I haven't throughly inspected your code, but there seems to be a concurrency issue. The map is thread-unsafe (HashMap), so if you mutate it through config() and have other threads access map, you have a problem.
Though you could use a ConcurrentHashMap instead HashMap, a batch operation on ConcurrentHashMap is not atomic. Meaning that, if you use it, you will see a "half-way" modified config. That could not be okay depending on your app.
So, the solution for this is to use this:
private volatile ImmutableMap map;
public config(){
ImmutableMap newMap = createNewMap();
this.map = newMap;
}
This will change your configs atomically (no intermediate state is visible).
As for updating your config on the fly, log4j does it using a background thread that monitors the config file. You could of course monitor a db table instead by polling it periodically.
In that case, your Config class will have preferably a ScheduledExecutor with a task that will monitor files/db and call config() periodically.
The answer to question #2 is to use a thread and check periodically if the file has been changed or to simply reinitialize your settings with the file contents.

Categories

Resources