Good afternoon controls people of LinkedIn! Monday, I released the article of how to calculate the global capacity metrics into a program object! For simplicity purposes, we used all integer slots to output all of our values! However, one thing that we had to go was take the columns that said “none” and convert them to an integer format for out slots to use appropriately! For a refresh, here are those blocks of code:
Beginning of Try block:
This is what we used to look at the columns of our global capacity and if any of them said “none” we used “0” instead. Why did we do it this way? Because all of our slots we created originally were of the type “baja: integer.” So, in order to accommodate for this, we added this code.
If Statements inside Try Block
Since we used Integers for all of our slots and we are reading String values, those values needed to be converted to the proper primitive. In order to accomplish this, we used the method Integer.parseInt(String s). So if the String contains a valid Integer (like “42”), it returns the corresponding int value.
So if the string cannot be parsed as an integer (like “none”) it throws a NumberFormatException.
Can we have slots that are Strings and slots that are Integers?
Absolutely! There will be some slot changes and code changes we need to make, but both are minor!
First thing you would want to is delete the networksLimit, devicesLimit, pointsLimit, linksLimit, historiesLimit, and schedulesLimit slots and add them back again as baja:String instead.
Note: I changed all of them to limits to string, but you certainly don’t have it. The code supplied in the last article will always work as intended, but this is if you want to use bound labels for everything, not just some of them.
Replacing the for loop
So with original for loop (I showed the array so you know exactly where and what to replace) we just searched for all columns. However, we are going to make a very key difference here:
This keeps the code robust in case the metrics occasionally returns “none” in any slot. However, we know that the “limits” column can return a string of None, so we want to accommodate that output. However, the “used” column will still be used as an integer, so in case “none” is there, we can return 0.
However the code:
gcNumers[j] = (j == 2) ? “0” : “None”;
Makes all the difference. What is different here is I’m using a shorthand version of the if-else statement. This is called a ternary operator. So if j equals 2 (usually the “Used” column), it replaces “None” with “0” so parsing to Integer won’t fail. If it’s any other index, it leaves it as “None” to be passed through as a string.
Replacing the If Statements
Any of the Limit setters you replaced with a string slot need to have the Integer.parseInt() removed from the statement:
You can see the differences now between the statements! Now all my limits can be read as a string!
Since we’ve done this, do we need the NumberFormatException?
That is a very fair point! Technically you don’t need it anymore… HOWEVER
I think we can still keep it and here’s why!
If spy:/metrics source unexpectedly returns a malformed valued like “12,3x” or “12a” parseInt will still fail. So keeping this as a backup is good defensive programming practice, especially when reading a dynamic input!
What about the graphics?
Well, this actually becomes easier! You don’t need to have NumerWritables anymore! You can just use all StringWritables and be done!
Why don’t we optimize ahead of time?
I purposely broke down this way to conceptualize making code that works the first time, and after some perspective changing some things for better functionality.
Another reason why I want people who are learning how to code to just code and not optimize is once you have the fundamentals down, that’s when we can do better things! The key is crawling before you can walk!
Here’s what I mean:
It’s important to understand before optimizing because if you don’t fully grasp it, while optimizing might make it faster… but also harder to debug or maintain.
Premature optimization is often called “the root of all evil” in programming – because you can spend tons of time tweaking things that you don’t to yet.
Make it work. Then make it clean. Then make it fast (if needed).
That’s all I have for now! As before, here is the code below:
public void onStart() throws Exception
{
// Call the onExecute method when the component starts to execute the metric collection
onExecute();
}
/**
* Executes the onExecute method to retrieve and process station metrics for Global Capacity
*/
public void onExecute() throws Exception
{
// Create a new instance of StringWriter to capture the formatted output from "spy:/metrics"
StringWriter stringWriter = new StringWriter();
// Create a new instance of SpyWriter to retrieve the system metrics from the ORD spy:/metrics
SpyWriter spyWriter = new SpyWriter(stringWriter, new FilePath("/"));
// Casting BOrd as BSpy and fetch the metrics
((BSpy) BOrd.make("spy:/metrics").get(getComponent())).get().write(spyWriter);
// Split the captured data into lines for processing
String[] lines = TextUtil.splitAndTrim(stringWriter.toString(), '\n');
// Iterate through each line to extract relevant information
for (int i = 0; i < lines.length; ++i)
{
// Remove unnecessary HTML-like formatting from the text
String line = TextUtil.replace(lines[i], "<tr><td align='left' nowrap='true'>", "");
line = TextUtil.replace(line, "</td><td align='right' nowrap='true'>", " ");
line = TextUtil.replace(line, "</td></tr>", "");
try
{
String[] gcNumbers = TextUtil.splitAndTrim(line, ' ');
// If "None" appears in the expected numerical fields, replace with "0"
for (int j = 1; j < gcNumbers.length; j++)
{
if ("None".equalsIgnoreCase(gcNumbers[j]))
{
gcNumbers[j] = (j == 2) ? "0" : "None"; // Default value for missing metrics
}
}
// Extract and process the "Networks" metrics
if (line.startsWith("Networks"))
{
setNetworksLimit((TextUtil.replace(gcNumbers[1], ",", "")));
setNetworksUsed(Integer.parseInt(TextUtil.replace(gcNumbers[2], ",", "")));
}
// Extract and process the "Devices" metrics
else if (line.startsWith("Devices"))
{
setDevicesLimit((TextUtil.replace(gcNumbers[1], ",", "")));
setDevicesUsed(Integer.parseInt(TextUtil.replace(gcNumbers[2], ",", "")));
}
// Extract and process the "Points" metrics
else if (line.startsWith("Points"))
{
setPointsLimit((TextUtil.replace(gcNumbers[1], ",", "")));
setPointsUsed(Integer.parseInt(TextUtil.replace(gcNumbers[2], ",", "")));
}
// Extract and process the "Links" metrics
else if (line.startsWith("Links"))
{
setLinksLimit((TextUtil.replace(gcNumbers[1], ",", "")));
setLinksUsed(Integer.parseInt(TextUtil.replace(gcNumbers[2], ",", "")));
}
// Extract and process the "Histories" metrics
else if (line.startsWith("Histories"))
{
setHistoriesLimit((TextUtil.replace(gcNumbers[1], ",", "")));
setHistoriesUsed(Integer.parseInt(TextUtil.replace(gcNumbers[2], ",", "")));
}
// Extract and process the "Schedules" metrics
else if (line.startsWith("Schedules"))
{
setSchedulesLimit((TextUtil.replace(gcNumbers[1], ",", "")));
setSchedulesUsed(Integer.parseInt(TextUtil.replace(gcNumbers[2], ",", "")));
}
} // curly brackets that close try block
catch (NumberFormatException nfe)
{
System.out.println(getComponent().getSlotPath() + " - Error parsing number.");
nfe.printStackTrace();
}
catch (Exception e)
{
System.out.println(getComponent().getSlotPath() + " - Unexpected error.");
e.printStackTrace();
}
} //closes for loop
// Update the last execution time with the current timestamp
setLastExecutionTime(BAbsTime.make());
} // end onExecute() block