I have the following Singleton class
public class TestFileEngine {
private static int counter = 0;
private static List<GeneratedFile> generatedFileList;
private static Optional<TestFileEngine> engine = Optional.empty();
private TestFileEngine() {
generatedFileList = Collections.synchronizedList(new ArrayList<>());
}
public static synchronized TestFileEngine getInstance() {
if (!engine.isPresent()) {
engine = Optional.of(new TestFileEngine());
}
return ruEngine.get();
}
public synchronized void generateFile() {
counter++;
String timestamp = LocalDateTime.now().toString();
String content = "";//insert random content generation
GeneratedFile gf = new GeneratedFile(counter + ".txt", timestamp, content);
generatedFileList.add(gf);
System.out.println("Generated file " + counter + ".txt");
}
public GeneratedFile findFileByName(String filename) {
for (GeneratedFile file : generatedFileList){
if(file.getFileName().equals(filename)){
return file;
}
}
return null;
}
}
Now I want to have two separate engines (and possibly more in the future), for tracking purpose and I stumbled upon the multiton pattern, still using the lazy implementation. So I will change the following:
//Getting rid of Optional, as it can get funky with Maps
private static final Map<String, TestFileEngine> _engines = new HashMap<String, TestFileEngine>();
public static synchronized TestFileEngine getInstance(String key) {
if (_engines.get(key) == null) {
_engines.put(key, new TestFileEngine());
System.out.println("Create engine " + key);
}else {
System.out.println("Using engine " + key);
}
return _engines.get(key);
}
I want to be sure that each of the engines has its separate counter and file list. However after I ran the following code, it seems they share counter and list:
TestFileEngine.getInstance("http").generateFile();
TestFileEngine.getInstance("http").generateFile();
TestFileEngine.getInstance("http").generateFile();
TestFileEngine.getInstance("ftp").generateFile();
TestFileEngine.getInstance("ftp").generateFile();
System.out.println(TestFileEngine.getInstance("http").findFileByName("4.txt").getFileName());
System.out.println(TestFileEngine.getInstance("ftp").findFileByName("2.txt"));
Console:
Create engine http
Generated file 1.txt
Using engine http
Generated file 2.txt
Using engine http
Generated file 3.txt
Create engine ftp
Generated file 4.txt
Using engine ftp
Generated file 5.txt
Using engine http
4.txt
Using engine ftp
null
What should I do to the counter and generatedFileList fields so that each TestFileEngine created from the Multiton is completely separated?
Actually you declare the 3 fields of the class with the static modifier :
private static int counter = 0;
private static List<GeneratedFile> generatedFileList;
private static final Map<String, TestFileEngine> _engines = new HashMap<String, TestFileEngine>();
static fields are shared among all instances of the current class.
You want that for engines field.
But you don't want that for counter and generatedFileList fields that have to be attached to a specific instance of TestFileEngine.
So make them instance fields instead of static.
As a side note, int fields are by default valued to 0 and you should avoid _ to prefix your variables that doesn't make part of the naming conventions.
So you could write :
private int counter;
private List<GeneratedFile> generatedFileList;
private static final Map<String, TestFileEngine> engines = new HashMap<String, TestFileEngine>();
Related
I have two environment PROD and STAGING. In prod environment we have three datacenters ABC, DEF and PQR and staging has one datacenter CORP. Each datacenter has few machines and I have constant defined for them as shown below:
// NOTE: I can have more machines in each dc in future
public static final ImmutableList<String> ABC_SERVERS = ImmutableList.of("tcp://machineA:8081", "tcp://machineA:8082");
public static final ImmutableList<String> DEF_SERVERS = ImmutableList.of("tcp://machineB:8081", "tcp://machineB:8082");
public static final ImmutableList<String> PQR_SERVERS = ImmutableList.of("tcp://machineC:8081", "tcp://machineC:8082");
public static final ImmutableList<String> STAGING_SERVERS = ImmutableList.of("tcp://machineJ:8087","tcp://machineJ:8088");
Now I have another constant defined in the same class which groups by DC to List of machines for each environment type.
public static final ImmutableMap<Datacenter, ImmutableList<String>> PROD_SERVERS_BY_DC =
ImmutableMap.<Datacenter, ImmutableList<String>>builder()
.put(Datacenter.ABC, ABC_SERVERS).put(Datacenter.DEF, DEF_SERVERS)
.put(Datacenter.PQR, PQR_SERVERS).build();
public static final ImmutableMap<Datacenter, ImmutableList<String>> STAGING_SERVERS_BY_DC =
ImmutableMap.<Datacenter, ImmutableList<String>>builder()
.put(Datacenter.CORP, STAGING_SERVERS).build();
Now in some other class, basis on what environment I am in (Utils.isProd()), I get either PROD_SERVERS_BY_DC or STAGING_SERVERS_BY_DC.
Map<Datacenter, ImmutableList<String>> machinesByDC = Utils.isProd() ? Utils.PROD_SERVERS_BY_DC : Utils.STAGING_SERVERS_BY_DC;
Now I think this can be represented in much better way in some sort of Enum instead of having constants defined like I have above but I am not able to figure out how can I do that? I started off with this but got confuse on how can I have single key for each DC and then multiple values as List of machines for that DC and then I need to group them by environment as well.
// got confuse on how can I make key (DC) and list of values (machines) for each environment type.
public enum DCToMachines {
abc(tcp://machineA:8081", "tcp://machineA:8082"),
private final List<String> machines;
private final Datacenter datacenter;
...
}
I started off with this but got confuse on how can I have single key
for each DC and then multiple values as List of machines for that DC
and then I need to group them by environment as well.
You forgot an important just : enum may store members (method and fields) specific to each enum value but it may also store static members if required.
What you need is moving the maps that groups by environment directly in the enum class :
private static final ImmutableMap<Datacenter, ImmutableList<String>> PROD_SERVERS_BY_DC;
private static final ImmutableMap<Datacenter, ImmutableList<String>> STAGING_SERVERS_BY_DC;
You could use an inner map to return the expected map according to the user request :
private static final Map<Env, ImmutableMap<Datacenter, ImmutableList<String>>> SERVERS_BY_ENV;
Where Env is another enum :
public static enum Env {
PROD, STAGING
}
In the code that I will present I added as an enclosing type of Datacenter but it would be probably better in its own file as the environment is probably not a concept used exclusively by datacenters.
At last, the way you are using to retrieve servers by environment splits related responsibilities in 2 classes (Datacenter enum and the current class) :
Map<Datacenter, ImmutableList<String>> machinesByDC = Utils.isProd() ? Utils.PROD_SERVERS_BY_DC : Utils.STAGING_SERVERS_BY_DC;
introducing in the enum a method to do this operation would be nicer :
public static ImmutableMap<Datacenter, ImmutableList<String>> getServers(Env env){...}
Which would increase the cohesion of the enum class.
Here is the enum updated :
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
public enum Datacenter {
ABC(Env.PROD, "tcp://machineA:8081", "tcp://machineA:8082"),
DEF(Env.PROD, "tcp://machineB:8081", "tcp://machineB:8082"),
PQR(Env.PROD, "tcp://machineA:8081", "tcp://machineA:8082"),
CORP(Env.STAGING, "tcp://machineC:8081", "tcp://machineC:8082");
public static enum Env {
PROD, STAGING
}
private Env env;
private String[] url;
private static final ImmutableMap<Datacenter, ImmutableList<String>> PROD_SERVERS_BY_DC;
private static final ImmutableMap<Datacenter, ImmutableList<String>> STAGING_SERVERS_BY_DC;
private static final Map<Env, ImmutableMap<Datacenter, ImmutableList<String>>> SERVERS_BY_ENV;
static {
Builder<Datacenter, ImmutableList<String>> prodDataCenterBuilder = ImmutableMap.<Datacenter, ImmutableList<String>>builder();
Builder<Datacenter, ImmutableList<String>> stagingDataCenterBuilder = ImmutableMap.<Datacenter, ImmutableList<String>>builder();
for (Datacenter datacenter : Datacenter.values()) {
if (datacenter.env == Env.PROD) {
prodDataCenterBuilder.put(datacenter, ImmutableList.of(datacenter.url));
} else if (datacenter.env == Env.STAGING) {
stagingDataCenterBuilder.put(datacenter, ImmutableList.of(datacenter.url));
}
else {
throw new IllegalArgumentException("not exepected env " + datacenter.env);
}
}
PROD_SERVERS_BY_DC = prodDataCenterBuilder.build();
STAGING_SERVERS_BY_DC = stagingDataCenterBuilder.build();
SERVERS_BY_ENV = new HashMap<>();
SERVERS_BY_ENV.put(Env.PROD, PROD_SERVERS_BY_DC);
SERVERS_BY_ENV.put(Env.STAGING, STAGING_SERVERS_BY_DC);
}
Datacenter(Env env, String... url) {
this.env = env;
this.url = url;
}
public static ImmutableMap<Datacenter, ImmutableList<String>> getServers(Env env) {
ImmutableMap<Datacenter, ImmutableList<String>> map = SERVERS_BY_ENV.get(env);
if (map == null) {
throw new IllegalArgumentException("not exepected env " + env);
}
return map;
}
}
And here how to use it :
public static void main(String[] args) {
ImmutableMap<Datacenter, ImmutableList<String>> servers = Datacenter.getServers(Env.PROD);
servers.forEach((k, v) -> System.out.println("prod datacenter=" + k + ", urls=" + v));
servers = Datacenter.getServers(Env.STAGING);
servers.forEach((k, v) -> System.out.println("staging datacenter=" + k + ", urls=" + v));
}
Output :
prod datacenter=ABC, urls=[tcp://machineA:8081, tcp://machineA:8082]
prod datacenter=DEF, urls=[tcp://machineB:8081, tcp://machineB:8082]
prod datacenter=PQR, urls=[tcp://machineA:8081, tcp://machineA:8082]
staging datacenter=CORP, urls=[tcp://machineC:8081,
tcp://machineC:8082]
While this is a bit tricky without knowing full implementation I might be able to get you started.
Enums are interesting in that they are Immutable which solves the declaring constant fields and variables. Here is my attempt to solve what you are looking for.
public enum Server {
// Constant style naming because Enums are Immutable objects
ABC_SERVERS("tcp://machineA:8081", "tcp://machineA:8082"),
DEF_SERVERS("tcp://machineB:8081", "tcp://machineB:8082"),
PQR_SERVERS("tcp://machineC:8081", "tcp://machineC:8082"),
CORP("tcp://machineZ:8081");
/* After the name declaration you can essentially do normal class
development
*/ Fields
private List<String> servers = new ArrayList<>();
public boolean isProd; // you could use something as simple as a
//boolean to determine the environment
// Enums use a private constructor because they are never
//instantiated outside of creation of this Enum class
private Server(String... dataCenter){
// because of the varargs parameter there is a potential for
//multiple Strings to be passed
for (String tcp :
dataCenter) {
this.servers.add(tcp);
}
// You can access the name property of the Enum that is being created
if (this.name() == "CORP")
this.isProd = false;
else
this.isProd = true;
}
// You could make the List public but this demonstrates some encapsulation
public List<String> getServers() {
return servers;
}
}
I then checked that each "machine" was being added in another class
// these are what I used to check to make sure that each string is being "mapped" correctly
List<String> prodServer = new ArrayList<>();
List<String> stagingServer = new ArrayList<>();
for (Server server :
Server.values()) {
if (server.isProd) {
for (String machine :
server.getServers()) {
prodServer.add(machine);
}
}
else {
for (String machine:
server.getServers()) {
stagingServer.add(machine);
}
}
}
System.out.println(prodServer);
System.out.println(stagingServer);
Also note that to get the actual enum names you use the .values() function of the Enum class.
I have a library which parse URLs and extract some data. There is one class per URL. To know which class should handle the URL provided by the user, I have the code below.
public class HostExtractorFactory {
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
switch (host) {
case HostExtractorABC.HOST_NAME:
return HostExtractorAbc.getInstance();
case HostExtractorDEF.HOST_NAME:
return HostExtractorDef.getInstance();
case HostExtractorGHI.HOST_NAME:
return HostExtractorGhi.getInstance();
default:
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
}
The problem is users are requesting more URL to be parsed, which means my switch statement is growing. Every time someone comes up with a parser, I have to modify my code to include it.
To end this, I've decided to create a map and expose it to them, so that when their class is written, they can register themselves to the factory, by providing the host name, and the extractor to the factory. Below is the factory with this idea implemented.
public class HostExtractorFactory {
private static final Map<String, HostExtractor> EXTRACTOR_MAPPING = new HashMap<>();
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
if(EXTRACTOR_MAPPING.containsKey(host)) {
return EXTRACTOR_MAPPING.get(host);
} else {
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
public static void register(String hostname, HostExtractor extractor) {
if(StringUtils.isBlank(hostname) == false && extractor != null) {
EXTRACTOR_MAPPING.put(hostname, extractor);
}
}
}
And the user would use it that way:
public class HostExtractorABC extends HostExtractor {
public final static String HOST_NAME = "www.abc.com";
private static class HostPageExtractorLoader {
private static final HostExtractorABC INSTANCE = new HostExtractorABC();
}
private HostExtractorABC() {
if (HostPageExtractorLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
HostExtractorFactory.register(HOST_NAME, this);
}
public static HostExtractorABC getInstance() {
return HostPageExtractorLoader.INSTANCE;
}
...
}
I was patting my own back when I realized this will never work: the user classes are not loaded when I receive the URL, only the factory, which means their constructor never runs, and the map is always empty. So I am back to the drawing board, but would like some ideas around getting this to work or another approach to get rid of this pesky switch statement.
S
Another option is to use the Service Loader approach.
Having your implementers add something like the following in ./resources/META-INF/services/your.package.HostExtractor:
their.package1.HostExtractorABC
their.package2.HostExtractorDEF
their.package3.HostExtractorGHI
...
Then in your code, you can have something like:
HostExtractorFactory() {
final ServiceLoader<HostExtractor> loader
= ServiceLoader.load(your.package.HostExtractor.class);
for (final HostExtractor registeredExtractor : loader) {
// TODO - Perform pre-processing which is required.
// Add to Map? Extract some information and store? Etc.
}
}
I would advice for you to learn about dependency injection (I love spring implementation). You will then be able to write an interface like
public interface HostExtractorHandler {
public String getName();
public HostExtractor getInstance();
}
Than your code can "ask" for all classes that implements this interface, you then would be able to build your map in the initialization phase of your class.
I would use the Reflections library to locate the parsers. They all appear to derive from the HostExtractor class, so use the library to locate all subtypes:
Reflections reflections = new Reflections("base.package");
Set<Class<? extends HostExtractor>> extractorTypes =
reflections.getSubTypesOf(HostExtractor.class);
Use the results to create the instances in your factory:
for (Class<? extends HostExtractor> c : extractorTypes) {
HostExtractor he = c.newInstance();
EXTRACTOR_MAPPING.put(he.getHostName(), he);
}
I made up the getHostName method, but it should be trivial to add to the HostExtractor base class.
Given the following code:-
//setup code and import statements, including:
private static String baseURL = Environment.getTestWebsiteURL();
public static String articleOneName = ArticleCreationTest.getArticleOneName();
public static String articleTwoName = ArticleCreationTest.getArticleTwoName();
public static String articleThreeName = ArticleCreationTest.getArticleThreeName();
public static String articleOnePath = ArticleCreationTest.getArticleOnePath();
public static String articleTwoPath = ArticleCreationTest.getArticleTwoPath();
public static String articleThreePath = ArticleCreationTest.getArticleThreePath();
public static String[] articlesPathArray = {articleOnePath, articleTwoPath, articleThreePath}
#BeforeClass
public static void setup() {
driver = Driver.getURL();
for (String s : articlesArray) {
if (s == null) {
//tell me which articles could not be found
System.out.println("Could not find an article for: " + s + " , perhaps it wasn't created in the prior test");
} else {
//assuming array holds some path values, append to the baseURL
driver.get(baseURL + s);
}
}
#Test...
//run some test assertions against the baseURL + path website page that is returned
I need the code to loop through wherever the path variable holds a value and run tests. The current solution is not helpful wherever the prior ArticleCreationTest fails to generate the article, because the variable simply contains null. So the text is: "Could not find an article for: null, perhaps it wasn't created in the prior test".
What I really need is to associate the articleName with the articlePath so the message is something like: "Could not find ArticleOne: perhaps is wasn't created", and then run the tests against all that were created. Perhaps some kind of hashmap or 2D array?
Based on the code given,
public static String articleOneName = ArticleCreationTest.getArticleOneName();
public static String articleTwoName = ArticleCreationTest.getArticleTwoName();
public static String articleThreeName = ArticleCreationTest.getArticleThreeName();
public static String articleOnePath = ArticleCreationTest.getArticleOnePath();
public static String articleTwoPath = ArticleCreationTest.getArticleTwoPath();
public static String articleThreePath = ArticleCreationTest.getArticleThreePath();
public static String[] articlesPathArray = {articleOnePath, articleTwoPath, articleThreePath}
It seems like, it is a list of articleNames and articlePaths
List<String> acticleNames = new ArrayList<String>();
List<String> acticlePaths = new ArrayList<String>();
The List will contain the Strings to be checked, which can be used for the tests.
I need the code to loop through wherever the path variable holds a
value and run tests
You can check this condition by checking if (s != null), currently you are checking for
if (s == null)
I was asked this question in an interview to improve the code that was provided. The provided code used lot of if statements and therefore I decided to use HashMap as retrieval would be faster. Unfortunately, I was not selected for the position. I am wondering if someone knows a better way than what I did to improve the code?
/* The following Java code is responsible for creating an HTML "SELECT" list of
U.S. states, allowing a user to specify his or her state. This might be used,
for instance, on a credit card transaction screen.
Please rewrite this code to be "better". Submit your replacement code, and
please also submit a few brief comments explaining why you think your code
is better than the sample. (For brevity, this sample works for only 5
states. The real version would need to work for all 50 states. But it is
fine if your rewrite shows only the 5 states here.)
*/
/* Generates an HTML select list that can be used to select a specific U.S.
state.
*/
public class StateUtils {
public static String createStateSelectList() {
return
"<select name=\"state\">\n"
+ "<option value=\"Alabama\">Alabama</option>\n"
+ "<option value=\"Alaska\">Alaska</option>\n"
+ "<option value=\"Arizona\">Arizona</option>\n"
+ "<option value=\"Arkansas\">Arkansas</option>\n"
+ "<option value=\"California\">California</option>\n"
// more states here
+ "</select>\n"
;
}
/* Parses the state from an HTML form submission, converting it to the
two-letter abbreviation. We need to store the two-letter abbreviation
in our database.
*/
public static String parseSelectedState(String s) {
if (s.equals("Alabama")) { return "AL"; }
if (s.equals("Alaska")) { return "AK"; }
if (s.equals("Arizona")) { return "AZ"; }
if (s.equals("Arkansas")) { return "AR"; }
if (s.equals("California")) { return "CA"; }
// more states here
}
/* Displays the full name of the state specified by the two-letter code. */
public static String displayStateFullName(String abbr) {
{
if (abbr.equals("AL")) { return "Alabama"; }
if (abbr.equals("AK")) { return "Alaska"; }
if (abbr.equals("AZ")) { return "Arizona"; }
if (abbr.equals("AR")) { return "Arkansas"; }
if (abbr.equals("CA")) { return "California"; }
// more states here
}
}
My solution
/* Replacing the various "if" conditions with Hashmap<key, value> combination
will make the look-up in a constant time while using the if condition
look-up time will depend on the number of if conditions.
*/
import java.util.HashMap;
public class StateUtils {
/* Generates an HTML select list that can be used to select a specific U.S.
state.
*/
public static String createStateSelectList() {
return "<select name=\"state\">\n"
+ "<option value=\"Alabama\">Alabama</option>\n"
+ "<option value=\"Alaska\">Alaska</option>\n"
+ "<option value=\"Arizona\">Arizona</option>\n"
+ "<option value=\"Arkansas\">Arkansas</option>\n"
+ "<option value=\"California\">California</option>\n"
// more states here
+ "</select>\n";
}
/* Parses the state from an HTML form submission, converting it to the
two-letter abbreviation. We need to store the two-letter abbreviation
in our database.
*/
public static String parseSelectedState(String s) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("Alabama", "AL");
map.put("Alaska", "AK");
map.put("Arizona", "AZ");
map.put("Arkansas", "AR");
map.put("California", "CA");
// more states here
String abbr = map.get(s);
return abbr;
}
/* Displays the full name of the state specified by the two-letter code. */
public static String displayStateFullName(String abbr) {
{
HashMap<String, String> map2 = new HashMap<String, String>();
map2.put("AL", "Alabama");
map2.put("AK", "Alaska");
map2.put("AZ", "Arizona");
map2.put("AR", "Arkansas");
map2.put("CA", "California");
// more state abbreviations here here
String full_name = map2.get(abbr);
return full_name;
}
}
}
I think there are many things wrong with your code, not least the recreation of the Map for each method call.
I would start at the very beginning, with interfaces. We need two things; a State and a StateResolver. The interfaces would look like this:
public interface State {
String fullName();
String shortName();
}
public interface StateResolver {
State fromFullName(final String fullName);
State fromShortName(final String shortName);
Set<? extends State> getAllStates();
}
This allows the implementation to be swapped out for something more sensible at a later stage, like a database. But lets stick with the hardcoded states from the example.
I would implement the State as an enum like so:
public enum StateData implements State {
ALABAMA("Alabama", "AL"),
ALASKA("Alaska", "AK"),
ARIZONA("Arizona", "AZ"),
ARKANSAS("Arkansas", "AR"),
CALIFORNIA("Californiaa", "CA");
private final String shortName;
private final String fullName;
private StateData(final String shortName, final String fullName) {
this.shortName = shortName;
this.fullName = fullName;
}
#Override
public String fullName() {
return fullName;
}
#Override
public String shortName() {
return shortName;
}
}
But, as mentioned above, this can be replaced with a bean loaded from a database. The implementation should be self-explanatory.
Next onto the resolver, lets write one against our enum:
public final class EnumStateResolver implements StateResolver {
private final Set<? extends State> states;
private final Map<String, State> shortNameSearch;
private final Map<String, State> longNameSearch;
{
states = Collections.unmodifiableSet(EnumSet.allOf(StateData.class));
shortNameSearch = new HashMap<>();
longNameSearch = new HashMap<>();
for (final State state : StateData.values()) {
shortNameSearch.put(state.shortName(), state);
longNameSearch.put(state.fullName(), state);
}
}
#Override
public State fromFullName(final String fullName) {
final State s = longNameSearch.get(fullName);
if (s == null) {
throw new IllegalArgumentException("Invalid state full name " + fullName);
}
return s;
}
#Override
public State fromShortName(final String shortName) {
final State s = shortNameSearch.get(shortName);
if (s == null) {
throw new IllegalArgumentException("Invalid state short name " + shortName);
}
return s;
}
#Override
public Set<? extends State> getAllStates() {
return states;
}
}
Again this is self explanatory. Variables are at the instance level. The only dependency on the StateData class is in the initialiser block. This would obviously need to be rewritten for another State implementation but that should be not big deal. Notice this class throws an IllegalArgumentException if the state is invalid - this would need to be handled somewhere, somehow. It is unclear where this would happen but something that needs to be considered.
Finally we implement the required methods in the class
public final class StateUtils {
private static final StateResolver STATE_RESOLVER = new EnumStateResolver();
private static final String OPTION_FORMAT = "<option value=\"%1$s\">%1$s</option>\n";
public static String createStateSelectList() {
final StringBuilder sb = new StringBuilder();
sb.append("<select name=\"state\">\n");
for (final State s : STATE_RESOLVER.getAllStates()) {
sb.append(String.format(OPTION_FORMAT, s.fullName()));
}
sb.append("</select>\n");
return sb.toString();
}
public static String parseSelectedState(final String s) {
return STATE_RESOLVER.fromFullName(s).shortName();
}
public static String displayStateFullName(final String abbr) {
return STATE_RESOLVER.fromShortName(abbr).fullName();
}
}
Notice we only reference the implementation at the top of the utility class, this makes swapping out the implementation quick and painless. We use a static final reference to that the StateResolver is created once and only once. I have also replaced the hardcoded creation of the select with a dynamic loop based one. I have also used a formatter to build the select.
It should be noted that it is never a good idea to build HTML in Java and anyone that does so should have unspeakable things done to them.
Needless to say you should have thorough unit tests against each and every line of the above code.
In short your answer doesn't really come close to a proper, extensible, enterprise solution to the problem at hand. My solution might seem overkill, and you may be right. But I think it's the correct approach because abstraction is key to reusable code.
To avoid manually maintaining 2 maps and keeping them in sync I would just create the second one as the first one inverted. See here on how to do it.
Also as pointed out by others you need to create your maps only once outside of method call.
** Just for fun a way to do it in Scala **
val m = Map("AL" -> "Alabama", "AK" -> "Alaska")
m map { case (k, v) => (v, k) }
// gives: Map(Alabama -> AL, Alaska -> AK)
Everyone seems focused on the parse, but the create can be improved, too. Get all of the state names, sort them alphabetically, and iterate over that collection to create each option. That way, the states used for parsing are always in sync with the states used for cresting. If you add a new state, you only need to add it to the "master" Enum (or whatever), and both methods will reflect the change.
The only mistake you made was to rebuild the map every time around. If you had built the Map just once - perhaps in a constructor I suspect you would have done fine.
public class StateUtils {
class State {
final String name;
final String abbreviation;
public State(String name, String abbreviation) {
this.name = name;
this.abbreviation = abbreviation;
}
}
final List<State> states = new ArrayList<State>();
{
states.add(new State("Alabama", "AL"));
states.add(new State("Alaska", "AK"));
states.add(new State("Arizona", "AZ"));
states.add(new State("Arkansas", "AR"));
states.add(new State("California", "CA"));
}
final Map<String, String> nameToAbbreviation = new HashMap<String, String>();
{
for (State s : states) {
nameToAbbreviation.put(s.name, s.abbreviation);
}
}
final Map<String, String> abbreviationToName = new HashMap<String, String>();
{
for (State s : states) {
nameToAbbreviation.put(s.abbreviation, s.name);
}
}
public String getStateAbbreviation(String s) {
return nameToAbbreviation.get(s);
}
public String getStateName(String abbr) {
return abbreviationToName.get(abbr);
}
}
One thing I don't like about your code is that you create a hashmap each time the method is called. The map should be created just once, at class init time, and referenced from the method.
What you did wrong is what guys are saying - you are creating a new HashMap every time the method is invoked - a static field could rather congaing the data, and populating it only once the class is being loaded my the JVM.
I'd rather use simple switch on strings - the search is not worse than that of HashMap (at least asymptotically) but you don't use extra memory. Though you need two long switches - more code.
But than again HashMap solution the the later one would be the same for me.
this is the issue at hand, when trying to serialize the class below with the code below i'm getting is the below xml file without all the strings in the class.
The Class (some static values have changed but basically it), I left out all the generated get\set but they are all there with public access modifiers.
public class NotificationConfiguration implements Serializable
{
public static final String PORT_KEY = "mail.smtp.port";
public static final String DEFAULT_PORT_VALUE = "587";
public static final String TTL_KEY = "mail.smtp.starttls.enable";
public static final String DEFAULT_TTL_VALUE = "true";
public static final String AUTH_KEY = "mail.smtp.auth";
public static final String DEFAULT_AUTH_VALUE = "true";
public static final String MAIL_SERVER_KEY = "mail.smtp.host";
public static final String DEFAULT_MAIL_CLIENT_HOST = "smtp.gmail.com";
public static final String DEFAULT_MAIL_CLIENT_USERNAME = "*********";
public static final String DEFAULT_MAIL_CLIENT_PASSWORD = "*********";
public static final String DEFAULT_MAIL_CLIENT_ADDRESS = "*********";
public static final String DEFAULT_ADMIN_EMAIL = "*********";
public static final long DEFAULT_MAIL_INTERVAL = 24*60*60*1000; //One time a day default
public static final String SAVED_FOLDER_NAME = "C:\\Library";
public static final String SAVED_FILE_NAME = "C:\\Library\\NotificationCfg.xml";
private String portValue = DEFAULT_PORT_VALUE;
private String ttlValue = DEFAULT_TTL_VALUE;
private String authValue = DEFAULT_AUTH_VALUE;
private String mailClientHost = DEFAULT_MAIL_CLIENT_HOST;
private String mailClientUserName = DEFAULT_MAIL_CLIENT_USERNAME;
private String mailClientPassword = DEFAULT_MAIL_CLIENT_PASSWORD;
private String mailClientAddress = DEFAULT_MAIL_CLIENT_ADDRESS;
private String adminEMail = DEFAULT_ADMIN_EMAIL;
private boolean overdueSubsNotificationEnabled = false;
private boolean adminReportNotificationEnabled = false;
private long mailInterval =
}
The code used to serialize, which also creates the folder if missing.
public void storeChanges()
{
try
{
try
{
File f = new File(NotificationConfiguration.SAVED_FOLDER_NAME);
f.mkdir();
}
catch (Exception e){}
XMLEncoder encoder = new XMLEncoder( new BufferedOutputStream(new FileOutputStream(NotificationConfiguration.SAVED_FILE_NAME)));
encoder.writeObject(notificationConfig);
encoder.close();
System.out.println(LOG_CONFIGURATION_STORED);
}
catch (Exception ex)
{
System.out.println(LOG_CONFIGURATION_NOT_STORED + ex.getMessage());
}
}
The XML file received, with no exceptions thrown while serializing.
It basically just has the long value.
XMLEncoder encodes information about how to restore your object. If field values haven't changed from their defaults, XMLEncoder doesn't store anything.
This can cause confusion.
Hence, my rules of thumb when using XMLEncoder are:
1. don't initialize fields. don't do private String foo = DEFAULT_FOO;
2. don't do anything in the default constructor.
3. have some other method, or factory that will give you a "default" setup if needed.
I highly recommend to read again the XMLEncoder Javadoc
I will point out the main differences with the binary serialization we all know.
to restore the instance it need the class definition available to the JVM
It serializes only the data. And only the modified from default data.
As result of the 2 points above - is that there is no reason to serialize Static final values - they are part of the class definition.
The binary serialization on the other hand does serialize the class definition and can load from byte stream a class that was not available to the JVM before.
That is why you got results that you see. It Ok this is behavior by design and you use it right. It seems just not to be what you need.
By the way see what Xstream has to offer.
What is SAVED_FOLDER_NAME ? Is that like a factory object and did you by any chance call setMailInterval on that object?
Could that be that only mailInterval has a getter?
Just looked again the question apparently there is getter for all fields so ...