A java class was developed to obtain JWT Token using the Singleton pattern.
Following is the overall class template:
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
public class JWTSingleton {
private static JWTSingleton _instance=null;
private static String baseURLString=null;
private static String authPartURLString = null;
...
//Decalre the other static variables which are the input needed to call the authentication web service
...
private JWTResponse _jwt;
private StringBuilder sbErrMsg = new StringBuilder();
private boolean isTimeoutException=false;
private Instant dateTime=null;
public static synchronized void injectDependencies(
boolean doGetFresToken,
String baseURLString, String authPartURLString, ...other parameters...) {
if (doGetFresToken) {
//Clear the saved parameters needed by the web service
JWTSingleton.the_last_param_initilized = null;
}
injectDependencies(
baseURLString, authPartURLString, ...other parameters...
);
}
public static synchronized void injectDependencies(String baseURLString, String authPartURLString, ...other parameters) {
if (JWTSingleton.the_last_param_initilized != null)
return; //No need to initialize the static variables more than once
JWTSingleton.baseURLString = baseURLString;
JWTSingleton.authPartURLString = authPartURLString;
...
//Other static initializations which are the input values needed for the web service like secret and username
...
JWTSingleton.the_last_param_initilized = value_passed_for_the_last_parameter;
}
public synchronized static JWTSingleton getInstance(boolean doGetFreshToken) {
long durMils;
if ((_instance == null || doGetFreshToken) ||
(_instance != null &&
(_instance._jwt==null || _instance.sbErrMsg.length()>0)
)
) {
//Check if a fresh token is requested
//Only create an instance if it is null
if (_instance == null) {
_instance = new JWTSingleton();
}else {
//Calculate the duration between now and the last time the token was processed
//Only refresh the token if more that retryInterval seconds has passed
try {
durMils = Duration.between(_instance.dateTime, Instant.now()).toMillis();
} catch (Exception e) {
//An error occurred, just force to call the auth web service.
durMils = (retryInterval*2000)+1;
}
//If there was an error in the last run, and didn't pass more the retryInterval, then don't call the service to get a new token.
//This is to avoid calling the auth service too quickly before giving the change for the errors to be resolved.
//Using retryInterval is a reasonable delay needed between two attempts if there was an error in the last run.
if (!doGetFreshToken && _instance.sbErrMsg.length()>0 && durMils < (retryInterval*1000)) {
return _instance; //If there was an error in the last run, and less than retryInterval has passed, then return the last instance
}
//Reset the saved instance
if (_instance.sbErrMsg.length()>0) {
_instance.sbErrMsg.setLength(0);
}
_instance._jwt = null;
}
//Invoke the the method getToken() here to refresh the token
_instance.getToken(baseURLString, authPartURLString, ...other parameters);
} else {
}
return _instance;
}
public synchronized static JWTSingleton getInstance() {
return getInstance(false);
}
private JWTSingleton() {}
/**
* This private method will get a fresh token every time it is invoked. It will call the authentication service to obtain a token.
* <p>
* TODO: Possible improvement is to implement the retry logic using Lambda Expressions and Command Pattern Implementation.
*/
private void getToken(String baseURLString, String authPartURLString, ... other parameters) {
Service authService = null;
String errMsg = null;
try {
_jwt = null;
authService = new Service(doDecodePasswords);
...
...
//Init the web service needed to get the JWT Token
...
...
int attempNo=0;
for (int iRetyrCount = 0; iRetyrCount <= JWTSingleton.maxRetries; iRetyrCount++) {
attempNo = iRetyrCount+1;
if (attempNo > JWTSingleton.maxRetries) {
errMsg = "Obtain JWT Access Token will stop after attempts reached max retries: " + attempNo + ".";
errMsg = "Attempts to obtain JWT exceeded retry limit: " + attempNo + ".";
sbErrMsg.append(sbErrMsg.length()==0?errMsg:" - " + errMsg);
sbErrMsg.append(authService.getSbErrMsg().length()==0?"":" - " + authService.getSbErrMsg().toString());
break;
}
isTimeoutException = false;
//Call the authentication web service and get the response
_jwt = authService.callWSAuthGetJWTResponse();
if (authService.isTimeoutException()) {
errMsg = "Timeout exception occurred in 'JWTSingleton()' after retry attempt: " + attempNo + ". Will NOT retry again ...";
isTimeoutException = true;
errMsg = "JWTSingleton()-Timeout error in auth service: " + authService.getSbErrMsg().toString();
sbErrMsg.append(sbErrMsg.length()==0?errMsg:" - " + errMsg);
break;
}
else if (_jwt == null) {
errMsg = "Error in 'JWTSingleton()' - JWT response is null and will stop before reaching max - retry attempt: " + attempNo + ".";
errMsg = "JWTSingleton(): Error; JWT response is null.";
sbErrMsg.append(sbErrMsg.length()==0?errMsg:" - " + errMsg);
sbErrMsg.append(authService.getSbErrMsg().length()==0?"":" - " + authService.getSbErrMsg().toString());
break;
}
if (_jwt != null && ATSUtils.isHttpURLConResponseOk(_jwt.getHttpResponseCode()) && _jwt.getAccess_token() != null) {
break; //This is what we want, the happy path
}
if (_jwt != null && (_jwt.getError() != null || !ATSUtils.isHttpURLConResponseOk(_jwt.getHttpResponseCode()))) {
errMsg = MessageFormat
.format(
"An error occurred while obtaining JWT, will try again after attempt: {0}. JWT error = {1} - Response code ={2}.",
attempNo,_jwt.getError(), _jwt.getHttpResponseCode()
);
}
try {
Thread.sleep(JWTSingleton.retryInterval * 1000);
continue;
} catch (InterruptedException iex) {
}
}
} catch (Exception e) {
errMsg = "Unexpected error occurred in JWTSingleton(): " + e.toString();
System.out.println(errMsg);
e.printStackTrace();
sbErrMsg.append(sbErrMsg.length()==0?errMsg:" - " + errMsg);
}
try {
dateTime = Instant.now();
} catch (Exception e) {
}
}
public String getAccess_token() {
return _jwt.getAccess_token();
}
public JWTResponse getJWT() {
return _jwt;
}
public StringBuilder getSbErrMsg() {
return sbErrMsg;
}
public boolean isTimeoutException() {
return isTimeoutException;
}
public Instant getDateTime() {
return dateTime;
}
}
The client code will simply get the token as follows:
JWTSingleton.injectDependencies(...parameters needed for the web service...);
String theToken = JWTSingleton.getInstance().getAccess_token()
The Question:
I want to follow the best recommendations about when to fill the value for the static member _instance
and the private member _jwt
. See the code around _instance.getToken(...)
.
Is it better to ONLY create the instance _instance = new JWTSingleton()
inside the static method getInstance()
, and call the web service outside the singleton class and then use a setter method to set the token inside the singleton instance?
Or it's better to call the web service inside the singleton class in the static method getInstance()
using _instance.getToken(...)
(this is the current implementation)?