pass password to junitcore - java

I am developing a standalone application to test a website using Selenium Webdriver. The application will use a JavaFX, or Swing, UI to allow a user to load a test suite file and select which tests to run and which browser to run them in. The UI will also capture data used by the tests, such as usernames and passwords.
I am planning on using JUnitCore to run the selected test classes but I will need to pass data from the UI to the test classes. For most of the data I can do this using System.setProperty(prop, value). However I will also need to pass admin, and other, passwords for the website to the tests
What would be the best way to pass the password(s) to the tests?
What would be the security concerns when using System.setProperty to store passwords? Should I instead encrypt the data and store it in a file and have the tests read the file and decrypt the data? This would obviously increase the amount of processing required to set up each test

If the user who runs the UI knows the passwords, then I don't really see your security concerns. Who are you defending from?
If the user who runs the UI does not know the passwords (and the passwords should be stored in the testcases themselves), then you're in a pickle - encrypting is good, but you have to store the password for decrypting somehow ... endless circle, there's no safe space. Even if the storing is safe, the weak points are definitely the transfer to browser (which will be done as plain string encapsulated in JSON no matter what) and the automated typing into the browser itself. Seriously, if you don't trust the machines the tests are run on, don't store the passwords.
However, for the first case (when the user has to type in the password into the UI), to minimize the possibility of threat and also for maximum convenience, I'd do this:
create a singleton class PasswordHolder
a PasswordHolder would remember the passwords (given to them by JPasswordField) in a Map<String, char[]> or Map<String, Password> where key is some password identifier and value is the password itself
once a password would be accessed via getPassword() method, it's contents would get nullified
A quick sample implementation (for further improvement as I hope I won't forget to include anything important ... but it could happen). I think it kinda speaks for itself:
public class PasswordHolder {
private static PasswordHolder instance = null;
private final Map<String, Password> map = new HashMap<String, Password>();
private PasswordHolder() {
// nothing to do
}
public static PasswordHolder getInstance() {
if (instance == null) {
instance = new PasswordHolder();
}
return instance;
}
public void addPassword(String name, char[] pass) {
if (map.containsKey(name)) {
// throw some sort of Exception("Duplicate password name, man.")
}
map.put(name, new Password(pass));
}
public Password getPassword(String name) {
return map.get(name);
}
}
As the most convenient thing, I wrote Password to be a CharSequence so is useful with sendKeys(CharSequence keys). Unfortunately, sendKeys() uses toString(), so you have to make a String out of the password anyway (which is considered a bad practice).
public class Password implements CharSequence {
private final char[] pass;
private final BitSet nulled;
Password(char[] pass) {
this.pass = pass;
nulled = new BitSet(pass.length);
}
private void nullify(int start, int end) {
for (int i = start; i < end; i++) {
pass[i] = '\0';
}
nulled.set(start, end);
}
#Override
public int length() {
return pass.length;
}
#Override
public char charAt(int index) {
if (nulled.get(index)) {
// throw some Exception("That character has already been read!")
}
char ret = pass[index];
pass[index] = '\0';
nulled.set(index);
return ret;
}
#Override
public CharSequence subSequence(int start, int end) {
if (nulled.get(start, end).cardinality() > 0) {
// throw some Exception("Some of the characters has already been read!")
}
Password subPass = new Password(Arrays.copyOfRange(pass, start, end));
nullify(start, end);
return subPass;
}
#Override
public String toString() {
if (nulled.cardinality() > 0) {
// throw some Exception("Some of the characters has already been read!")
}
String str = new String(pass);
nullify(0, pass.length);
return str;
}
}

Related

Resolve environment variables of property file Java ResourceBundle

Our application is using java8 and spring. We are working to moving to kubernetes. For that reason, I want to use environment variables in the properties file like as follow and declare the -
conf.dir.path = ${APPLICATION_CONF_PATH}
database.name = ${APPLICATION_DB_SCHEMA}
save.file.path = ${COMMON_SAVE_PATH}${APPLICATION_SAVE_PATH}
# And many more keys
But right now the values are not resolved/expanded by environment variable.
Application initialization of property is as below -
public enum ApplicationResource {
CONF_DIR_PATH("conf.dir.path"),
DB_NAME("database.name")
FILE_SAVE_PATH("save.file.path"),
// And many more keys
private final String value;
ApplicationResource(String value) {
this.value = value;
}
private static final String BUNDLE_NAME = "ApplicationResource";
private static Properties props;
static {
try {
Properties defaults = new Properties();
initEnvironment(defaults, BUNDLE_NAME);
props = new Properties(defaults);
} catch (Throwable t) {
t.printStackTrace();
}
}
private static void initEnvironment(Properties props, String bundleName) throws Throwable {
ResourceBundle rb = ResourceBundle.getBundle(bundleName);
Enumeration<?> enu = rb.getKeys();
String key = null;
String value = null;
while (enu.hasMoreElements()) {
key = (String) enu.nextElement();
value = rb.getString(key);
props.setProperty(key, value);
}
}
public String getString() {
return props.getProperty(value);
}
public int getInt() throws NumberFormatException {
String str = getString();
if (str == null || str.length() == 0) {
return 0;
} else {
return Integer.parseInt(str);
}
}
}
getString is used extensively. Right now when getString is called, it returns the literal string from the properties file. Is there any way to properly resolve environment variables without impacting the codebase?
Edit: By [without impacting the codebase], I meant only changing/editing code in the above enum/class file and the change being transparent in other areas.
The simplest variant based on the Regex engine would be:
private static final Pattern VARIABLE = Pattern.compile("\\$\\{(.*?)\\}");
public String getString() {
return VARIABLE.matcher(props.getProperty(value))
.replaceAll(mr -> Matcher.quoteReplacement(System.getenv(mr.group(1))));
}
This replaces all occurrences of ${VAR} with the result of looking up System.getenv("VAR"). If the string contains no variable references, the original string is returned. It does, however, not handle absent variables. If you want to handle them (in a different way than failing with a runtime exception), you have to add the policy to the function.
E.g. the following code keeps variable references in their original form if the variable has not been found:
public String getString() {
return VARIABLE.matcher(props.getProperty(value))
.replaceAll(mr -> {
String envVal = System.getenv(mr.group(1));
return Matcher.quoteReplacement(envVal != null? envVal: mr.group());
});
}
replaceAll(Function<MatchResult, String>) requires Java 9 or newer. For previous versions, you’d have to implement such a replacement loop yourself. E.g.
public String getString() {
String string = props.getProperty(value);
Matcher m = VARIABLE.matcher(string);
if(!m.find()) return string;
StringBuilder sb = new StringBuilder();
int last = 0;
do {
String replacement = System.getenv(m.group(1));
if(replacement != null) {
sb.append(string, last, m.start()).append(replacement);
last = m.end();
}
} while(m.find());
return sb.append(string, last, string.length()).toString();
}
This variant does not use appendReplacement/appendTail which is normally used to build such loops, for two reasons.
First, it provides more control over how the replacement is inserted, i.e. by inserting it literally via append(replacement) we don’t need Matcher.quoteReplacement(…).
Second, we can use StringBuilder instead of StringBuffer which might also be more efficient. The Java 9 solution uses StringBuilder under the hood, as support for it has been added to appendReplacement/appendTail in this version too. But for previous versions, StringBuilder can only be used when implementing the logic manually.
Note that unlike the replaceAll variant, the case of absent variables can be handled simpler and more efficient with a manual replacement loop, as we can simply skip them.
You said you don’t want to change the initialization code, but I still recommend bringing it into a more idiomatic form, i.e.
private static void initEnvironment(Properties props, String bundleName) {
ResourceBundle rb = ResourceBundle.getBundle(bundleName);
for(Enumeration<String> enu = rb.getKeys(); enu.hasMoreElements(); ) {
String key = enu.nextElement();
String value = rb.getString(key);
props.setProperty(key, value);
}
}
In the end, it’s still doing the same. But iteration loops should be using for, to keep initialization expression, loop condition and fetching the next element as close as possible. Further, there is no reason to use Enumeration<?> with a type cast when you can use Enumeration<String> in the first place. And don’t declare variables outside the necessary scope. And there’s no reason to pre-initialize them with null.
Spring support environment variable or system variable or application.property file
if you able to use kubernates configmap its better choice.
How to set environment variable dynamically in spring test

Why is the .equals() method not overriden for arrays of primitives in Java?

I am currently working on a project where I want to implement a login mechanism using a username and a password that gets compared against a database.
I had something like this in mind:
public boolean verifyUser( String username, char[] password )
{
List<char[]> dbpass = getPasswords( username );
if ( dbpass.contains( password ) )
{
overwriteWithNonsense( password );
return true;
}
overwriteWithNonsense( password );
return false;
}
when I noticed that my unit tests were failing. So I took a deeper look at it an noticed that the Object::equals method is not overriden for arrays of primitives which explained why List::contains would always evaluate to false.
I know there is a possible workaround using:
if ( dbpass.stream().anyMatch( pw -> Arrays.equals( pw, password ) ) )
{
overwriteWithNonsense( password );
return true;
}
My question is why the designer chose to keep the 'default implementation' of Object::equals? Wouldn't it be way more convenient than implementing a framework with static utility methods like Arrays.equals(array1,array2)?
Any Collections of arrays is mostly bad design. They should not be used together. You're probably better of by introducing a trivial, but needed class:
public class Password{
private final char[] password;
public Password(char[] password){
this.password = password;
}
#Override
public boolean equals(Object obj){
// equals logic
}
#Override
public int hashCode(){
// hashCode logic
}
}
And then having a List<Password>. (I also shortened your verifyUser method because it seemed a bit redundant):
public boolean verifyUser( String username, char[] password ){
List<Password> dbpass = getPasswords(username);
boolean contained = dbpass.contains(new Password(password));
overwriteWithNonsense(password);
return contained;
}
(Your other question regarding the why it is not overriden is mostly off-topic, because only the java-devs could really answer that.)
In Java array is not a class but from Java Doc section 4.3.1 it is treated as an object. Now as this is not a class there's no code for equals/hashCode.
Now, for your solution to compare arrays you can use Arrays.equals method to compare two arrays:
public boolean verifyUser( String username, char[] password )
{
List<char[]> dbpass = getPasswords( username );
for(char[] arr : dbpass)
{
if ( Arrays.equals(arr,password) )
{
overwriteWithNonsense( password );
return true;
}
}
overwriteWithNonsense( password );
return false;
}
I don't think there is a good reason. You're generally encouraged to use ArrayList as its a fully-fledged container. But there seems only good to be had by implementing equals() as per AbstractList.
There's always == to determine if two references are the same object.
Java arrays are a bit of an unusual beast in that they might contain primitives but that doesn't seem too much of a hurdle.
Are these arrays equal?
Integer objects[]={Integer.valueOf(1),Integer.valueOf(1000)};
int integers[]={1,1000};
I'd go with 'no' as most consistent. But if not you will get the potentially surprise semantics that these objects won't be equal:
int ai[]={1,2,3};
long al[]={1,2,3};
Perhaps no one really thought about it and changing something so basic like that will surely have some code-breaking consequences.

How can I turn my four perfect hash basic pseudocode methods into workable code?

I am trying to figure out how I can turn my 4 basic pseudocode methods inside of my perfect hash class into workable methods that will eventually be used inside of a main method of my PerfectHash class. I know I haven't created the instance of the class yet, but I will.
You know like when you call the 4 methods using a stack, for example, you input parameters inside the methods, but my application will be able to take the key that's entered by the user and insert, fetch, delete, or update on it inside of the structure.
Do I need to put more thought into these methods or is the pseudocode enough to work for now? Here's what I have done so far. I know the first class compiles fine, but I am having trouble with the second class as I have already indicated above.
public class PerfectHash
{
private StadiumTickets[] data;
public boolean insert(StadiumTickets newStadiumTicket)
{
// the direct insert hashed algorithm
pseudoKey = preProcessing(targetKey);
ip = pseudoKey; // direct hashing function
// insert the new ticket
data[ip] = newStadiumTicket.deepCopy();
}
public StadiumTickets fetch(String targetKey)
{
// the fetch algorithm
// access the primary storage area
pseudoKey = preprocessing(targetKey);
ip = pseudoKey; // direct hashing function
if(data[ip]== null)
{
return null;
}
else
{
return data[ip].deepCopy();
}
}
public boolean delete(String targetKey)
{
// the delete direct hashed algorithm
// access the primary storage area
pseudoKey = preprocessing(targetKey);
ip = pseudoKey; // direct hashing function
if(data[ip]== null)
{
return false;
}
else
{
data[ip]= null;
return true;
}
}
public boolean update(String targetKey, StadiumTickets newStadiumTicket)
{
// the update direct hashed algorithm
if(delete(targetKey) == false)
return false;
else
{
insert(newStadiumTicket)
return true;
}
}
}
import java.util.Scanner;
public class StadiumTickets
{
int ticketNumber; // keyfield
String purchaserName;
public void input()
{
Scanner input= new Scanner(System.in);
// key values ranges between 2000 to 100,000
System.out.print("Please enter a ticket number between 2000 to 100,000: ");
// a variable to hold the answer
ticketNumber= input.nextInt();
// error checking to make sure the user doesn't enter a key outside of the lower or upper ranges
if(ticketNumber < 2000 && ticketNumber > 100000)
System.out.println("This number is not a valid entry to input into the structure.");
}
public StadiumTickets(int ticketNumber, String purchaserName)
{
this.ticketNumber= ticketNumber;
this.purchaserName= purchaserName;
}
public StadiumTickets deepCopy()
{
StadiumTickets clone= new StadiumTickets(ticketNumber, purchaserName);
return clone;
}
}

In OOP, when writing a subclass are you meant to change the original main method code? What instead?

I know that the whole point of OOP is so that code doesnt have to be rewritten.
I have a class (lets call it E) and a main class (lets call it main) with a main method that validates some inputs and then feeds them into class E (which encrypts the inpts) to get a result to output to the user (the encrypted input).
Now I am writing a subclass from the perspective that I was someone else improving the program. I have a subclass of the encryption class which uses similar principles but an improved method of encryption (lets call it class ImprovedE).
As the new developer I am pretending to be, would I just write the subclass and then rewrite the main method from the original program to feed the inputs into Improved E instead of E?
I know I can't override the main method but I dont think I should be changing the original code either. Any help would be appreaciated.
Heres the code (EDIT), that i forgot to add.
package encryption;
import java.util.Scanner;
import static encryption.encryptionChoice.*;
public class main {
//These are all the reusable variables created to temporarily store information before pushing it to the Encryption class
//This is a simple starter message to inform the user of how to use the program
private String initialDisplayInformation = "Message Encryption/Decryption program for use with letters and spaces only. \nPress any key to continue...";
//These are the "scope" input and output variables visible to the user
private String inputString;
private String outputString;
//This creates the encryption class
private Encryption myEncryption = new Encryption();
//These are used to later create two loops that only break when an acceptable input has been input for the values
private Boolean inputValidated = false;
private Boolean cypherValidated = false;
private Boolean choiceValidated = false;
private void initialInfo() {
//These 2 lines make the user have to hit any key before continuing after reading the info
System.out.println(initialDisplayInformation);
String pressAnyKey = new Scanner(System.in).nextLine();
}
private void inputValidation(){
//This loop attepts to validate the message input and uses the boolean returned fromthe Encryption.setInput class to see the success
//It prints a detailed error and repeats if unsuccessful
do {
System.out.println("\nEnter your message: ");
try {
inputValidated = myEncryption.setInput(new Scanner(System.in).nextLine());
} catch(NotACharacterException e) {
System.out.println(e.errorMessage());
}
} while(!inputValidated);
}
private void cypherValidation(){
//This repeats the exact process as the previous loop, but for the cypher length
do {
System.out.println("\nEnter your cypher length (How much the message will be/was offset by: ");
try {
cypherValidated = myEncryption.setCypher(new Scanner(System.in).nextInt());
if(!cypherValidated) {
System.out.println("That is not an acceptable integer, please try again... ");
}
}catch(Exception e){
System.out.println("That is not a valid input, please try again... ");
}
} while(!cypherValidated);
}
private void encryptionDecision(){
do {
System.out.println("\nWould you like to 1)Encrypt 2)Decrypt the message: ");
String choiceString = new Scanner(System.in).nextLine();
encryptionChoice choice = ERROR;
if(choiceString.equalsIgnoreCase("Encrypt")|| choiceString.equalsIgnoreCase("1")){
choice = ENCRYPT;
}
if(choiceString.equalsIgnoreCase("Decrypt") || choiceString.equalsIgnoreCase("2")){
choice = DECRYPT;
}
try {
System.out.println(myEncryption.getInput());
System.out.println(myEncryption.EncryptionDecryption(choice));
choiceValidated = true;
} catch(IllegalArgumentException e) {
System.out.println("Please only enter: Encrypt, Decrypt, 1 or 2. Please try again... ");
}
} while(!choiceValidated);
}
public static void main(String[] args) {
boolean runProgram = true;
while(runProgram){
main thread = new main();
thread.initialInfo();
thread.inputValidation();
thread.cypherValidation();
thread.encryptionDecision();
runProgram=false;
Scanner keyboard = new Scanner(System.in);
System.out.println("\nPress 'r' to restart or 'enter' to exit");
if(new Scanner(System.in).nextLine().equals("r")) {
runProgram=true;
}
}
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package encryption;
/**
*
* #author Alex
*/
public class SingleKeywordEncryption extends Encryption {
protected String keywordString;
protected Integer getKeywordCypher(String keyword, Integer term){
Character letterInKeyword = 0;
Integer cypherLength;
if(term<keyword.length()){
letterInKeyword = keyword.charAt(term);
}
else if(term>=keyword.length()) {
letterInKeyword = keyword.charAt(term%keyword.length());
}
cypherLength = termOf(letterInKeyword);
return cypherLength;
}
#Override
public String EncryptionDecryption(encryptionChoice choice) {
String outputString = "";
switch(choice){
case ENCRYPT:
for(int i=0;i<inputString.length();i++) {
outputString = outputString + letterOf(termOf(inputString.charAt(i)) + getKeywordCypher(keywordString, i));
}
break;
case DECRYPT:
for(int i=0;i<inputString.length();i++) {
outputString = outputString + letterOf(termOf(inputString.charAt(i)) - getKeywordCypher(keywordString, i));
}
break;
default:
throw new IllegalArgumentException();
};
return outputString;
}
}
package encryption;//Includes the class int he encryption package of classes for my project
//This creates a class called Encryption where the majority of my project is stored
public class Encryption {
//These create the variables needed throughout the class
//This is final so that it is immutable and usable in all the methods
//I made it a Character array to save memory use and to allow simpler comparison methods in other areas of the program
private final Character[] alphabet = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
//These are private for good coding practice, so that they can only be modified from within the class
private Integer cypher;
protected String inputString;
public String getInput() {
return inputString;
}
//This class simply validates and saves the cypher length to its variable and returns a boolean of its success
public boolean setCypher(Integer cypherToAdd) {
while(cypherToAdd>26 || cypherToAdd<0){
return false;
}
//It tries to save it and returns false if it cannot be saved as an integer
try {
cypher = cypherToAdd;
}
catch (Exception e) {
return false;
}
return true;
}
//This class validates andsaves the input to its variable and removes any whitespace from it
public boolean setInput(String inputToAdd) throws NotACharacterException {
//This uses replaceAll to remove whitespace and saves the object that is rejurned from the method to String input
String input = inputToAdd.replaceAll("\\s","");
//This iterates through every character in the input and checks that it is a letter
for (int term=0; term<input.length();term++) {
//If a term is not a letter, it throws the custom NotACharacterException and passes information of which non-letter character caused it
if(!Character.isLetter(input.charAt(term))){
throw new NotACharacterException("'" + String.valueOf(input.charAt(term)) + "' This character can not be used in this program...\nStick to letters and spaces only please.");
}
}
inputString = input;
return true;
}
//This class returns the term of a passed letter in the alphabet
protected int termOf(Character letter) {
//The term variable to be returned is initialised immediately
int term =0;
//The for loop iterates through every letter in the alphabet and compares it with the letter passed to the method to find its term
for (int currentTerm=0; currentTerm<alphabet.length;currentTerm++) {
//When the letters match, the term is returned to where the method is called
if(letter.toLowerCase(letter)==alphabet[currentTerm]){
term = currentTerm;
}
}
return term;
}
//This class returns the letter of a passed term in the alphabet
protected Character letterOf(int inputTerm) {
if(inputTerm>25){
return alphabet[inputTerm-26];
} else if (inputTerm<0) {
return alphabet[inputTerm+26];
}
else {
//It recieves the character by gathering the character in the inputTerm's place in the array, and returns it
return alphabet[inputTerm];
}
}
public String EncryptionDecryption(encryptionChoice choice){
String outputString = "";
switch(choice){
case ENCRYPT:
for(int i=0;i<inputString.length();i++) {
outputString = outputString + letterOf(termOf(inputString.charAt(i))+ cypher);
}
break;
case DECRYPT:
for(int i=0;i<inputString.length();i++) {
outputString = outputString + letterOf(termOf(inputString.charAt(i))- cypher);
}
break;
default:
throw new IllegalArgumentException();
};
return outputString;
}
}
No matter what, you will have to change something somewhere in your code so that the main uses your ImprovedE class instead of the E class. Otherwise, the program will not know by itself if you want to use E or ImprovedE.
If you will not use the encryption method in E and will always use ImprovedE, it would be better to include all the methods from E into ImprovedE. This way, you will not have to make any decisions on which class to use.
If you still want to be able to switch between the two classes, I recommend using the Strategy design pattern. You would put any methods shared between the two classes (such as termOf(Character letter), letterOf(int inputTerm) and setInput(String inputToAdd)) in a class accessible by both encyption strategy, and you would have two ConcreteStrategy: E and ImprovedE.
This way, you can switch between the two strategies quickly, just by changing the ConcreteStrategy (In your case, if you want to use the old or the new encryption method). You could also prompt the user to know which encryption method he wants if that's a valid use case.
I know that the whole point of OOP is so that code doesnt have to be rewritten...
Actually, the purpose of OOP is to make the organization of code easier to conceptualize. extends is considered by many to be an antipattern.
Consider using implements on an abstract base class to define an interface and letting future devs worry about 'improvements'.
In Java, the main method is static, which means it cannot be overriden. This is by design, but not necessarily optimal design. There is no universal reason why this could not be done in principle, but Java does not do it.
The thing is: you should not have to need this in the first place. Your former developer mixed responsabilities by putting all of these encryption methods and state information in the same class which represents the computation itself: your main class.
So, what you have to do is actually decouple these things because the main method has to very small and narrow-purposed. When this happens, it will not feel like there is so much repetition in place. If there is, there is always more room for refactoring.

Java web service seemingly not storing variable?

EDIT: The problem was two-fold, first dictionary should be static and also i was using .contains() where i should have used .containsKey()
I'm trying to do a simple java client and server set up, this is what i have got and i can't seem to find anything wrong with the way i have done it but whenever i run the code i get the output:
Result = Added
Result = This word is not in the dictionary, please use the add function.
Which tells me that the server isn't storing the change made the the map when i am adding a word, is there something really simple i am missing here?
I can add anymore information needed if asked.
This is my client code:
public class Client {
#WebServiceRef(wsdlLocation =
"http://localhost:8080/P1Server/ServerService?wsdl")
public static void main(String[] args) {
try {
package1.ServerService service = new package1.ServerService();
package1.Server port = service.getServerPort();
String result = port.addWord("Test", "This is a test.");
System.out.println("Result = " + result);
result = port.getDefiniton("Test");
System.out.println("Result = " + result);
}catch(Exception ex)
{
System.out.println("Gone Wrong");
}
This is my relevant server code:
#WebService
public class Server {
private **static**ConcurrentHashMap<String,String> dictionary;
public Server() {
this.dictionary = new ConcurrentHashMap<>();
}
#WebMethod
public String addWord(String word, String definition){
if(dictionary.contains(word.toLowerCase())){
return "This word is already in the dictionary, "
+ "please use the update function.";
}else{
dictionary.put(word.toLowerCase(), definition);
return "Added";
}
}
#WebMethod
public String getDefiniton(String word){
if(dictionary.contains(word.toLowerCase())){
return dictionary.get(word);
}else{
return "This word is not in the dictionary, "
+ "please use the add function.";
}
}
Your problem has noting to with webservice.
Issue is with you logic
Modify your methods as follows :
public String addWord(String word, String definition) {
if (dictionary.containsKey(word.toLowerCase())) {
return "This word is already in the dictionary, "
+ "please use the update function.";
} else {
dictionary.put(word.toLowerCase(), definition);
return "Added";
}
}
public String getDefiniton(String word) {
if (dictionary.containsKey(word.toLowerCase())) {
return dictionary.get(word.toLowerCase());
} else {
return "This word is not in the dictionary, "
+ "please use the add function.";
}
}
It will work.
Hope this helps.
Web services are stateless by nature. Every web request will get its own contexts and instances. So, the instance of Server that served the port.addWord() request can be different than the one that served port.getDefinition(). In that case, the dictionary map that had the result placed into it is different than the one used to retrieve the results.
In order for this to work, the data needs to be persisted somehow on the Server side. This can be done via a database. Or, if you're just doing this for testing purposes, change the definition of dictionary to be static so that all instances of Server share the same map.
Define dictionary as static variable. So that every instance of Web service instances that get created in the server side will use same dictionary to put/get data.
private static ConcurrentHashMap<String,String> dictionary;

Categories