I am working on an application in which I have list of machines and for each machine I need to hit the URL which will give me response back in XML and I need to parse the XML and extract one field (dataKey is the field name) value from it for each server and then check whether that field value is matching my threshold value or not. If it is matching my threshold value three times continuously then add that server in my another List.
So if machineA dataKey field is matching my threshold value continuously for three times, then add machineA to my list and if my machineB dataKey field value is also matching my same thresold value continuously for three times, then add machineB to the same list.
And after adding the machines to the list, reset the counter and start it again.
Below is my MachineStats class which holds each machine information -
public class MachineStats {
private String serverName;
private String serverURL;
private int dataKey = 0;
public MachineStats(String serverName, String serverURL) {
this.serverName = serverName;
this.serverURL = serverURL;
}
// getters here
public boolean isEligibleForEmail(Integer dataKeyCount, int thresholdLimit) {
try {
if (!TestUtils.isEmpty(dataKeyCount)) {
if (dataKeyCount >= thresholdLimit) {
dataKey++;
} else {
dataKey = 0;
}
if (dataKey > 3) {
dataKey = 0;
return true;
}
return false;
} else {
return false;
}
} catch (NumberFormatException ex) {
// log an exception
return false;
}
}
}
And below is the code I have in my main method which is the starting point where serverList holds list of machines.
// this holds machineA and machineB information
private static List<MachineStats> serverList = new LinkedList<MachineStats>();
while(true) {
ServerMetricsTask task = new ServerMetricsTask(serverList, thresholdLimit);
task.run();
TimeUnit.MINUTES.sleep(2); // sleep for 2 minutes
}
And below is my ServerMetricsTask class -
public class ServerMetricsTask {
private List<MachineStats> listOfServers;
private int thresholdLimit;
public ServerMetricsTask(List<MachineStats> listOfServers, int thresholdLimit) {
this.listOfServers = listOfServers;
this.thresholdLimit = thresholdLimit;
}
public void run() {
try {
List<String> holder = new LinkedList<String>();
for (MachineStats stats : listOfServers) {
Integer dataKeyCount = TestUtils.extractKey(stats.getServerName(), stats.getServerURL());
if (stats.isEligibleForEmail(dataKeyCount, thresholdLimit)) {
holder.add(stats.getServerName());
}
}
if (!holder.isEmpty()) {
// send an email with the machine name which has met the threshold
}
} catch (Exception ex) {
// log an exception
}
}
}
Problem Statement:-
Now I have above code working fine. As of now I am only checking for one property which is dataKey for each server. Now I need to do the same thing for two fields for each server. One field is dataKey and second field is customerKey and these two fields will be present in the XML response for each server.
So I have a fieldHolder map which holds my field name for which I need to check.
Here fieldHolder map holds -
key - field which we want to extract from the response coming from the URL.
value - is the threshold for that field which is a key.
Now I have modified my code like this -
// this holds machineA and machineB information
private static List<MachineStats> serverList = new LinkedList<MachineStats>();
private static Map<String, String> fieldHolder;
while(true) {
ServerMetricsTask task = new ServerMetricsTask(serverList, fieldHolder);
task.run();
TimeUnit.MINUTES.sleep(2); // sleep for 2 minutes
}
And below is my ServerMetricsTask class -
public class ServerMetricsTask {
private List<MachineStats> listOfServers;
private int thresholdLimit;
public ServerMetricsTask(List<MachineStats> listOfServers, Map<String, String> fieldHolder) {
this.listOfServers = listOfServers;
this.fieldHolder = fieldHolder;
}
public void run() {
try {
List<String> holder = new LinkedList<String>();
for (MachineStats stats : listOfServers) {
//now what should I do here?
// I guess I need to iterate fieldHolder map
}
if (!holder.isEmpty()) {
// send an email with the machine name which has met the threshold
}
} catch (Exception ex) {
// log an exception
}
}
}
The only problem I am facing is I don't how should I add logic so that it can extract two fields value by executing the URL after iterating fieldHolder map and then also check the whether those two field or either of them is meeting the threshold limit three times continuously. And if they are meeting the threshold limit three times continuously then add that machine name to the list holder.
I already know how to extract those fields values given an URL by parsing XML response.
first, change dataKey in MachineStats to keyMap
public class MachineStats {
...
private HashMap<String,int> keyMap = new HashMap<String,int>;
...
public HashMap<String,int> getKeyMap() {
return this.keyMap;
}
}
each time you create a new MachineStats, populate its keyMap with keys needed.
keyMap.put("Key1", 0);
keyMap.put("Key2", 0);
// etc. I don't know how you will do it
Now, you need to change TestUtils.extractKey() so it will accept and extract any arbitrary key name. Contrary to what I said in my comments, it's better not to change its inner logic so we won't make any more changes there.
TestUtils.extractKey(String keyName, String serverName, string serverURL);
Now, ServerMetricsTask iterates the machine list, and also iterates the machine's keymap:
public class ServerMetricsTask {
...
for (MachineStats stats : listOfServers) {
//now what should I do here?
// I guess I need to iterate fieldHolder map
HashMap<String, int> keymap = stats.getKeyMap();
int key_count = keymap.size();
int valid_count = 0;
Set<String> keys = keymap.keySet();
for (String key : keys) {
int count = keymap.get(key);
int result = TestUtils.extractKey(key, stats.getServerName(), stats.getServerURL());
if (stats.isEligibleForEmail(result, thresholdLimit)) {
count++;
} else {
// here you can do count = 0 if you need strictly continuous appearance of the key
}
keymap.put(key, count);
if (count >= 3) valid_count++;
}
// now decide if you need all keys to be valid or just some
if (valid_count == key_count) holder.add(stats.getServerName());
...
Related
I am writing test method like setTask(Task task). And Task object has several fields, e.g.
public String vehicle;
Method setTask should be used in different test-cases, so I'd like to have an options for this field to accept values:
null - the method should not do anything in this particulare case;
some string value - e.g. "", "Hello, World!", "Iso Isetta", ...
random - a value that indicates (as well as null indicates "no changes") that a random value should be selected for a drop-down list corresponding to this field.
So what can I do to make String to be SpecialString which could accept values null, random & some string value? (BTW: I don't want to set it to string value "RANDOM", and chech whether the value is equal to "RANDOM"-string)
UPDATE: I don't mean random like random value from a set of values, I mean random as well as null and this is for setTask() to handle random (select random from drop-down), and not to pass a random string from a set of values.
Pseudocode:
Task task = new Task();
task.vehicle = random; // as well as null
setTask(task)
in setTask(Task task):
if (task.vehicle == null) {
//skip
} else if (task.vehicle == random) {
// get possible values from drop-down list
// select one of them
} else {
// select value from drop-down list which is equal to task.vehicle
}
Don't assign a fixed String but use a Supplier<String> which can generate a String dynamically:
public Supplier<String> vehicleSupplier;
This, you can assign a generator function as you request:
static Supplier<String> nullSupplier () { return () -> null; }
static Supplier<String> fixedValueSupplier (String value) { return () -> value; }
static Supplier<String> randomSupplier (String... values) {
int index = ThreadLocalRandom.current().nextInt(values.length) -1;
return index > 0 && index < values.length ? values[index] : null;
}
In use, this looks like:
task.setVehicleSupplier(nullSupplier()); // or
task.setVehicleSupplier(fixedValueSupplier("value")); // or
task.setVehicleSupplier(randomSupplier("", "Hello, World!", "Iso Isetta"));
and you can get the String by
String value = task.vehicleSupplier().get();
or hide the implementation in a getter function
class Task {
// ...
private Supplier<String> vehicleSupplier;
public void setVehicleSupplier(Supplier<String> s) {
vehicleSupplier = s;
}
public String getVehicle() {
return vehicleSupplier != null ? vehicleSupplier.get() : null;
}
// ...
}
What you may want to do is to create an object that wraps a string as well as some information about whether or not it's a special value. Something along the lines of...
public class Special<T> {
public enum Type {
NOTHING, RANDOM, SPECIFIC
}
private final Type type;
private final T specificValue;
public Special(Type type, T specificValue) {
this.type = type;
this.specificValue = specificValue;
}
public Type getType() {
return type;
}
public T getSpecificValue() {
if (type != SPECIFIC) {
throw new IllegalStateException("Value is not specific");
}
return specificValue;
}
}
The class above could be used like so:
Special<String> a = new Special<>(Special.Type.NOTHING, null);
Special<String> b = new Special<>(Special.Type.SPECIFIC, "Hello");
if (b.getType() == Special.Type.RANDOM) {
// do something
}else if (b.getType() == Special.Type.SPECIFIC) {
String val = b.getSpecificValue();
// do something else
}
A slightly more polished variant of the thing above is probably the best way, but there is a way, a much uglier way, to do it using nothing but a String field.
What you could do is to have a "magical" string instance that behaves differently from all other string instances, despite having the same value. This would be done by having something like
static final String SPECIAL_VALUE_RANDOM = new String("random");
Note the use of the String constructor, which ensures that the string becomes a unique, non-interned instance. You can then say if (vehicle == SPECIAL_VALUE_RANDOM) { ... } (note the use of == instead of .equals()) to check if that specific instance (rather than any other string that says "random") was used.
Again, this is not a particularly good way of doing this, especially if you intend to do this more than once ever. I would strongly suggest something closer to the first way.
I have this class that serves as a container which I will use the instance variable for processing later
class Data{
static int counter= 0;
boolean boolean1;
String string1;
public Data() {
counter++;
}
}
And I have this method that sets the values of Data
public Data setData()
{
Data data = null;
for (int i = 0; i < somecoutnerhere; i++) {
Data = new Data();
Data.boolean1 = some boolean put here;
Data.string1 = "some string to be put here";
}
return ProcessData(Data);
}
I also have this class ProcessData that will make use of Data and will construct the response
private class ProcessData
{
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data)
{
map.put("boolean1", data.boolean1);
map.put("String1", data.string1);
counter = data.counter;
}
public String someMethodToGenerateReturnData(){
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
According to this your problem is not "returning all instance one variables in one call", as your title states, but rather a question about how returning all Data-Objects created in your for-loop, which is easier.
Your code is erronous though, so I went ahead & corrected it (I hope I didn't mess up). I also renamed a few things.
The changes I made are:
renamed "boolean1" and "string1" to "trueOrFalse" and "string"
added a public, fully parameterized constructor to the Data-class
added a ProcessData-list to the setData()-method, which is filled in the for-loop
(+ a comment)
However, I'd strongly recommend you to check your architecture, and also to learn a bit about naming conventions, or coding conventions in general. Names should point out the purpose or content of the method/variable/class, and "boolean1" isn't really doing that.
Regarding the architecture: The Data-class seems to exist solely for the counter, and you could easily change that, making the Data-class obsolete (unless it's used somewhere else).
Data class:
class Data {
static int counter = 0;
boolean trueOrFalse;
String string;
public Data() {
counter++;
}
public Data(boolean someBoolean, String someString) {
this.trueOrFalse= someBoolean;
this.string = someString;
counter++;
}
}
setData()-Method:
public List<ProcessData> setData() {
List<ProcessData> processedDataList = new ArrayList<ProcessData>();
for (int i = 0; i < someCounterHere; i++) {
processedDataList.add(new ProcessData(new Data(true, "testString"));
// a new Data-object is created (parameters true and "testString")
// a new ProcessData-object is created (parameter is the newly created Data-Object)
// the newly created ProcessData-object is added to the list
}
return processedDataList;
}
ProcessData-class:
private class ProcessData {
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data) {
map.put("trueOrFalse", data.trueOrFalse);
map.put("string", data.string);
counter = data.counter;
}
public String someMethodToGenerateReturnData() {
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
I am busy with a project that extracts data from a xml file and displays it in a word document. I have created a method for this extraction, but I want to simplify it by using an array of methods.
This is just an example of how I test for certain information at the moment:
for (int i = 0; i < nodeMap.getLength(); i++) {
Node node = nodeMap.item(i);
if (node.getNodeName().equalsIgnoreCase("maximumRedeliveries")) {
if (node.getNodeValue().startsWith("{{")) {
retryLogic.setMaximumRedeliveries(extractPropertyName(node.getNodeValue(), propFileLocation));
} else {
retryLogic.setMaximumRedeliveries(node.getNodeValue());
}
}
if (node.getNodeName().equalsIgnoreCase("asyncDelayedRedelivery")) {
if (node.getNodeValue().startsWith("{{")) {
retryLogic.setAsyncDelayedRedelivery(extractPropertyName(node.getNodeValue(), propFileLocation));
} else {
retryLogic.setAsyncDelayedRedelivery(node.getNodeValue());
}
}
}
I am aiming to create an array for the if statement values, for example "maximumRedeliveries" and "asyncDelayedRedelivery" and an array for their corresponding methods, for example setMaximumRedeliveries(),setAsyncDelayedRedelivery(). I am unsure of how to create an array of methods, or if it's even possible?
This problem differs form Java - Creating an array of methods, because I use set methods and don't know how to implement it in that way.
First, ensure that extractPropertyName takes names with and without curly braces, and behaves like this:
String extractOptionalPropertyName(String name, String propFileLocation) {
return name..startsWith("{{") ? extractPropertyName(name, propFileLocation) : name;
}
This moves conditionals from your XML processing code into a helper:
String nodeName = node.getNodeName();
if (nodeName.equalsIgnoreCase("maximumRedeliveries")) {
retryLogic.setMaximumRedeliveries(extractOptionalPropertyName(node.getNodeValue(), propFileLocation));
} else if (nodeName.equalsIgnoreCase("asyncDelayedRedelivery")) {
retryLogic.setAsyncDelayedRedelivery(extractOptionalPropertyName(node.getNodeValue(), propFileLocation));
} ... // and so on
With these changes in place, you can follow the recipe from this other Q&A and make a Map<String,ValSetter> objects, like this:
interface ValSetter {
void set(RetryLogic logic, String val);
}
// The map can be made static in a class
Map<String,ValSetter> setterForName = new HashMap<>();
{ // Initializer block
setterForName.put("maximumredeliveries", new ValSetter() {public void set(RetryLogic logic, String val) { logic.setMaximumRedeliveries(val);}} );
setterForName.put("asyncrelayedredelivery", new ValSetter() {public void set(RetryLogic logic, String val) { logic.setAsyncDelayedRedelivery(val);}} );
}
Now your XML handler could look like this:
String nodeName = node.getNodeName();
ValSetter setter = setterForName.get(nodeName.toLowerCase());
if (setter != null) {
String val = extractOptionalPropertyName(node.getNodeValue(), propFileLocation);
setter.set(retryLogic, val);
} else {
// report an error
}
I've already made another question close to this one several minutes ago, and there were good answers, but it was not what I was looking for, so I tried to be a bit clearer.
Let's say I have a list of Thread in a class :
class Network {
private List<Thread> tArray = new ArrayList<Thread>();
private List<ObjectInputStream> input = new ArrayList<ObjectInputStream>();
private void aMethod() {
for(int i = 0; i < 10; i++) {
Runnable r = new Runnable() {
public void run() {
try {
String received = (String) input.get(****).readObject(); // I don't know what to put here instead of the ****
showReceived(received); // random method in Network class
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
tArray.add(new Thread(r));
tArray.get(i).start();
}
}
}
What should I put instead of ** ?
The first thread of the tArray list must only access the first input of the input list for example.
EDIT : Let's assume my input list has already 10 elements
It would work if you put i. You also need to add an ObjectInputStream to the list for each thread. I recommend you use input.add for that purpose. You also need to fill the tArray list with some threads, use add again there.
Here's the solution:
private void aMethod() {
for(int i = 0; i < 10; i++) {
final int index = i; // Captures the value of i in a final varialbe.
Runnable r = new Runnable() {
public void run() {
try {
String received = input.get(index).readObject().toString(); // Use te final variable to access the list.
showReceived(received); // random method in Network class
} catch (Exception exception) {
exception.printStackTrace();
}
}
};
tArray.add(new Thread(r));
tArray.get(i).start();
}
}
As you want each thread to access one element from the input array you can use the value of the i variable as an index into the list. The problem with using i directly is that an inner class cannot access non-final variables from the enclosing scope. To overcome this we assign i to a final variable index. Being final index is accessible by the code of your Runnable.
Additional fixes:
readObject().toString()
catch(Exception exception)
tArray.add(new Thread(r))
i'm having a bit of a problem. Here's the situation. I have an amount field in my main class that gets incremented when certain buttons are clicked on. There is a method provided through which you can delete any order (I'm basically programming for a restaurant terminal) and the amount gets decremented. The delete method is placed in another class.
public void posdel(int pos, JTextField amountFieldGot, int amountGot)
{
if(slist==null)
{
JOptionPane.showMessageDialog(null, "No order has been placed yet.",null,JOptionPane.WARNING_MESSAGE);
}
else
{
if(pos==1)
{
reductionAmount = (slist.quantity*slist.price);
amountGot = amountGot - reductionAmount;
slist=slist.next;
}
else
{
int i=1;
Node temp=slist;
Node prev=null;
while(temp.next!=null && i<pos)
{
prev=temp;
temp=temp.next;
i++;
}
if(pos==i)
{
prev.next=temp.next;
}
else
{
JOptionPane.showMessageDialog(null, "Invalid order", null, JOptionPane.ERROR_MESSAGE);
}
}
}
amountFieldGot.setText(Integer.toString(amountGot));
}
So basically, I have an amountField in my GUI that i pass as a parameter to the posdel method. I also pass the amount value as a parameter. The new amount that i get is amountGot after the deletion of the first order. ( I haven't written the code for other positions.)
Suppose the amount value i pass into the method is 30 (14+16) 14 = order 1, 16 = order2.
And my first order has a value of 14.
So amountGot = 30 - 14 which is 16.
And the amountField in the GUI gets updated to 16.
Now my order 2 becomes my order 1. And if i try to delete this,
my amountField gets update to 14. (30-16 = 14).
So i'm guessing the amount value stays the same itself as 30 and does not get updated to the new amountGot value. Can someone please help me solve this problem ?
below is the code for my delete button.
deleteButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dishDelPos = JOptionPane.showInputDialog("Enter the position of the order to be deleted");
try
{
dishDeletePosition = Integer.parseInt(dishDelPos);
order1.posdel(dishDeletePosition, amountField, amount);
repaint();
}
catch(NumberFormatException ex1)
{
JOptionPane.showMessageDialog(null,"This is not a valid position");
}
}
});
A few things.
You can make the delete method in the class static. The you would reference it
value = MyClass.deleteMethod();
You can create a new class to perform the method
MyClass myClass = new MyClass();
value = myClass.deleteMethod();
You can do it using a pointer of sorts, by passing in a reference to an already existing instance of the class holding the delete method, to where you want to call it.
myFunction(MyClass myClass)
{
value = myClass.deleteMethod();
}
basically set up your function to return a value
public static int deleteMethod()
{
}
this function returns an int.
or if you need to return more than that then set the class up with global variables of information
class MyClass
{
public int value1;
public int value2;
public String value3;
public void deleteMethod()
{
//does something with global variables
}
}
now fetch the info after calling delete like so
Myclass myClass = new MyClass();
myClass.deleteMethod();
value1 = myClass.value1
value2 = myClass.Value2
value3 = myClass.Value3