I have written some REST APIs using Java Servlets on Tomcat. These are my first experiences with Java and APIs and Tomcat. As I research and read about servlets, methods and parameter passing, and more recently thread safety, I realize I need some review, suggestions, and tutorial guidance from those of you who I see are far more experienced. I have found many questions / answers that seem to address pieces but my lack of experience clouds the clarity I desire.
The code below shows the top portion of one servlet example along with an example private method. I have "global" variables defined at the class level so that I may track the success of a method and determine if I need to send an error response. I do this because the method(s) already return a value.
Are those global variables creating an unsafe thread environment
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe
Though clipped for space, should I be doing all of the XML handling in the doGet method
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Should each method that accesses the same database open a Connection or should the doGet method create a Connection and pass it to each method
Assist, suggest, teach, guide to whatever you feel appropriate, or point me to the right learning resources so I may learn how to do better. Direct and constructive criticism welcome -- bashing and derogatory statements not preferred.
#WebServlet(name = "SubPlans", urlPatterns = {"*omitted*"})
public class SubPlans extends HttpServlet {
private transient ServletConfig servletConfig;
private String planSpecialNotes,
planAddlReqLinks,
legalTermsHeader,
legalTermsMemo,
httpReturnMsg;
private String[] subPlanInd = new String[4];
private boolean sc200;
private int httpReturnStatus;
private static final long serialVersionUID = 1L;
{
httpReturnStatus = 0;
httpReturnMsg = "";
sc200 = true;
planAddlReqLinks = null;
planSpecialNotes = null;
legalTermsHeader = "";
legalTermsMemo = null;
}
#Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
#Override
public ServletConfig getServletConfig() {
return servletConfig;
}
#Override
public String getServletInfo() {
return "SubPlans";
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<HashMap<String, Object>> alSubDeps = new ArrayList<HashMap<String, Object>>();
String[] coverageDates = new String[6],
depDates = new String[8];
String eeAltId = null,
eeSSN = null,
carrier = null,
logosite = null,
fmtSSN = "X",
subSQL = null,
healthPlan = null,
dentalPlan = null,
visionPlan = null,
lifePlan = null,
tier = null,
healthGroupNum = null,
effdate = null,
holdEffDate = null,
planDesc = "",
planYear = "",
summaryBenefitsLink = null;
int[][] effdates = new int[6][4];
int holdDistrictNumber = 0,
districtNumber = 0,
holdUnit = 0,
unit = 0;
boolean districtHasHSA = false;
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
try {
eeAltId = request.getParameter("*omitted*");
if ( eeAltId != null ) {
Pattern p = Pattern.compile(*omitted*);
Matcher m = p.matcher(eeAltId);
if ( m.find(0) ) {
eeSSN = getSSN(eeAltId);
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID format incorrect.";
System.err.println("Bad alternate id format " + eeAltId);
sc200 = false;
}
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID missing.";
System.err.println("alternate id not provided.");
sc200 = false;
}
if ( sc200 ) {
coverageDates = determineDates();
subSQL = buildSubSQLStatement(eeSSN, coverageDates);
alSubDeps = getSubDeps(subSQL);
if ( sc200 ) {
XMLStreamWriter writer = outputFactory.createXMLStreamWriter(response.getOutputStream());
writer.writeStartDocument("1.0");
writer.writeStartElement("subscriber");
// CLIPPED //
writer.writeEndElement(); // subscriber
writer.writeEndDocument();
if ( sc200 ) {
response.setStatus(HttpServletResponse.SC_OK);
writer.flush();
} else {
response.sendError(httpReturnStatus, httpReturnMsg);
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error writing XML");
System.err.println(e);
}
}
#Override
public void destroy() {
}
private String getPlanDescription(String planID) {
String planDesc = null;
String sqlEE = "SELECT ...";
Connection connGPD = null;
Statement stGPD = null;
ResultSet rsGPD = null;
try {
connGPD = getDbConnectionEE();
try {
stGPD = connGPD.createStatement();
planDesc = "Statement error";
try {
rsGPD = stGPD.executeQuery(sqlEE);
if ( !rsGPD.isBeforeFirst() )
planDesc = "No data";
else {
rsGPD.next();
planDesc = rsGPD.getString("Plan_Description");
}
} catch (Exception rsErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error retrieving plan description.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(rsErr);
sc200 = false;
} finally {
if ( rsGPD != null ) {
try {
rsGPD.close();
} catch (Exception rsErr) {
System.err.println("getPlanDescription: Error closing result set.");
System.err.println(rsErr);
}
}
}
} catch (Exception stErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error creating plan description statement.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(stErr);
sc200 = false;
} finally {
if ( stGPD != null ) {
try {
stGPD.close();
} catch (Exception stErr) {
System.err.println("getPlanDescription: Error closing query statement.");
System.err.println(stErr);
}
}
}
} catch (Exception connErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error closing database.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(connErr);
sc200 = false;
} finally {
if ( connGPD != null ) {
try {
connGPD.close();
} catch (Exception connErr) {
System.err.println("getPlanDescription: Error closing connection.");
System.err.println(connErr);
}
}
}
return planDesc.trim();
}
I have "global" variables defined at the class level
You have instance variables declared at the class level. There are no globals in Java.
so that I may track the success of a method and determine if I need to send an error response.
Poor technique.
I do this because the method(s) already return a value.
You should use exceptions for this if the return values are already taken.
Are those global variables creating an unsafe thread environment
Those instance variables are creating an unsafe thread environment.
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe?
Via exceptions thrown by the methods, see above. If there is no exception, send an OK response, whatever form that takes, otherwise whatever error response is appropriate to the exception.
Though clipped for space, should I be doing all of the XML handling in the doGet method
Not if it's long or repetitive (used in other places too).
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Sure, why not?
Should each method that accesses the same database open a Connection or should the doGet() method create a Connection and pass it to each method
doGet() should open the connection, pass it to each method, and infallibly close it.
NB You don't need the ServletConfig variable, or the init() or getServletConfig() methods. If you remove all this you can get it from the base class any time you need it via the getServletConfig() method you have pointlessly overridden.
The variables you have defined are instance members. They are not global and are not class-level. They are variables scoped to one instance of your servlet class.
The servlet container typically creates one instance of your servlet and sends all requests to that one instance. So you will have concurrent requests overwriting these variables’ contents unpredictably.
It can be ok for a servlet to have static variables or instance member variables, but only if their contents are thread safe and they contain no state specific to a request. For instance it would be normal to have a (log4j or java.util.logging) Logger object as a static member, where the logger is specifically designed to be called concurrently without the threads interfering with each other.
For error handling use exceptions to fail fast once something goes wrong.
Servlets are painful to write and hard to test. Consider using a MVC web framework instead. Frameworks like spring or dropwizard provide built-in capabilities that make things like data access and error handling easier, but most importantly they encourage patterns where you write separate well-focused classes that each do one thing well (and can be reasoned about and tested independently). The servlet approach tends to lead people to cram disparate functions into one increasingly-unmanageable class file, which seems to be the road you’re headed down.
Related
I am trying to override some class of vertx web project, since I have to change some of the features. So the tricky part comes here.
#Override
public void reroute(HttpMethod method, String path) {
int split = path.indexOf('?');
if (split == -1) {
split = path.indexOf('#');
}
if (split != -1) {
log.warn("Non path segment is not considered: " + path.substring(split));
// reroute is path based so we trim out the non url path parts
path = path.substring(0, split);
}
/*((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);*/
((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);
request.params().clear();
// we need to reset the normalized path
normalisedPath = null;
// we also need to reset any previous status
statusCode = -1;
// we need to reset any response headers
response().headers().clear();
// special header case cookies are parsed and cached
if (cookies != null) {
cookies.clear();
}
// reset the end handlers
if (headersEndHandlers != null) {
headersEndHandlers.clear();
}
if (bodyEndHandlers != null) {
bodyEndHandlers.clear();
}
failure = null;
restart();
}
This code throws me a compilation error saying:
'HttpServerRequestWrapper cannot be accessed from outside package'
I know for a fact that we can use reflection to create objects of a class that cannot be accessed. Can reflection be used in this case? How can I fix such an issue.
Any help will be much appreciated.
In java 8 and/or without modules it is possible to just place class like that in same package as original one to get access to all package-default classes.
Otherwise you need to use reflections like in other response, but I would add that it is good idea to cache that Class and Method instance, as using Class.forName and clazz.getDeclaredMethod each time will slowdown code.
What about getting the Class object and then calling the methods on your specific (uncasted) object?
I assume request is a class attribute of type HttpServerRequestWrapper. Then, this is what I suggest:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
...
private final Method setMethod;
private final Method setPath;
public MyConstructor() {
Method tmp1 = null, tmp2 = null;
try {
final Class<?> clazz = Class.forName("io.vertx.ext.web.impl.HttpServerRequestWrapper");
tmp1 = clazz.getMethod("setMethod", HttpMethod.class);
tmp1.setAccessible(true);
tmp2 = clazz.getMethod("setPath", String.class);
tmp2.setAccessible(true);
} catch (ClassNotFoundException e) {
// do something
} catch (NoSuchMethodException e) {
// do something
} catch (SecurityException e) {
// do something
}
this.setMethod = tmp1;
this.setPath = tmp2;
}
...
#Override
public void reroute(HttpMethod method, String path) {
...
try {
this.setMethod.invoke(request, method);
this.setPath.invoke(request, path);
} catch (IllegalAccessException e) {
// do something
} catch (IllegalArgumentException e) {
// do something
} catch (InvocationTargetException e) {
// do something
}
...
}
EDIT: I updated this answer based on #GotoFinal's suggestion.
It looks like HttpServerRequestWrapper implements HttpServerRequest. So, you can change "HttpServerRequestWrapper" to "HttpServerRequest" in your code. But remember that by doing so, you'll only be able to call methods specified in the interface.
You can see those methods in https://vertx.io/docs/apidocs/io/vertx/rxjava/core/http/HttpServerRequest.html.
I am using IBM's ILOG 7.1 via Java (aka JRules). I need to debug some pre-existing functionality, where there is a set of rules being executed. Only some of the rules that should be executed are actually in the output of my trace. So I'm left wondering:
Are the rules not actually getting executed?
Are the rules simply not getting outputted as part of the trace?
I am not sure.
The Java code that I have to execute and log the traced rules is as follows:
public RulesDTO executeRules(RulesDTO rulesDTO) {
try {
String sessionId = rulesDTO.getSessionId();
String clientIp = rulesDTO.getClientIp();
LOG.info("Starting to execute rules.");
String ruleSetName = rulesDTO.getRuleSetName();
IlrSessionRequest request = this.rulesSessionProvider
.createRequest(ruleSetName);
Map<String, Object> parNameToObjectMap = new HashMap<String, Object>();
parNameToObjectMap.put("params", rulesDTO);
parNameToObjectMap.put("timeOut",
getEngineExecutionTimeout());
request.setInputParameters(parNameToObjectMap);
boolean isTraceEnabled = true;
// Enable trace to retrieve info on executed rules
request.setTraceEnabled(isTraceEnabled);
// get all traces
request.getTraceFilter().setInfoAllFilters(isTraceEnabled);
IlrSessionResponse response = null;
try {
// calls ILOG engine, itself, to execute rules
response = executeRulesRequest(request, ruleSetName);
}
catch (TimeoutException te) {
LOG.error(String.format("Rules %s timed out [timeout=%d]",
ruleSetName, getEngineExecutionTimeout()));
throw te;
}
logRuleExecutions(rulesDTO.getID(), sessionId, response);
return rulesDTO;
}
catch (Throwable t) {
LOG.error("Rule set execution failed. ilogVersion=7.1", t);
throw new RulesException(
"Rule set execution failed. ilogVersion=7.1", t);
}
}
private void logRuleExecutions(
long searchId, String sessionId, IlrSessionResponse response) {
IlrExecutionTrace executionTrace = response.getRulesetExecutionTrace();
List<IlrExecutionEvent> executionEvents = executionTrace.getExecutionEvents();
// ExecutedRule is a custom class I have wrapping IlrExecutionEvent
List<ExecutedRule> executedRules = getRuleExecutions(executionEvents);
int numRulesExecuted = executedRules.size();
LOG.info("sessionId={}, SearchId={}, numRules={}",
new Object[] {sessionId, searchId, numRulesExecuted});
// loop over executedRules list and just log each one
logFilteredRules(executedRules, searchId, sessionId);
}
private List<ExecutedRule> getRuleExecutions(List<IlrExecutionEvent> executionEvents) {
List<ExecutedRule> executedRules = new LinkedList<ExecutedRule>();
for (IlrExecutionEvent event : executionEvents) {
if (event instanceof IlrRuleEvent) {
IlrRuleEvent ruleEvent = (IlrRuleEvent) event;
allRuleExecutions.add(convertToRuleExecution(ruleEvent));
}
else {
List<IlrExecutionEvent> subEvents = ((IlrTaskEvent) event).getSubExecutionEvents();
LOG.debug("Size of subEvents={}", subEvents.size());
List<ExecutedRule> executedSubRules = getRuleExecutions(subEvents);
executedRules.addAll(executedSubRules);
}
}
}
In addition to the fact only some of the rules appear to be getting invoked, it appears that only earlier rules are invoked, and not rules that ought to be executed after the initial rules. Any help would be greatly appreciated. Thanks. :-)
java version "1.7.0_45"
Hello
I am initializing the class methods in the constructor. However, the new URL(uploadUrl) will throw an exception in the constructor. So if this happens the user shouldn't be able to continue. As the constructor cannot return anything, I am wondering that is the best way to handle this?
Many thanks for any suggestions,
public class MultipleFileTransfer {
private static final String TAG = MultipartUtility.class.getSimpleName();
private DataOutputStream dataOutputStream;
private FileInputStream fileInputStream;
private HttpURLConnection httpURLConnection;
private URL url;
public MultipleFileTransfer(final String uploadUrl) {
dataOutputStream = null;
fileInputStream = null;
httpURLConnection = null;
try {
url = new URL(uploadUrl);
} catch (MalformedURLException e) {
Log.wtf(TAG, e.getMessage()); /* <-- How to handle a failure */
}
}
/* Factory method that initializes the class methods and returns the class object */
public static MultipleFileTransfer getInstance(final String uploadUrl) {
/* Check that a valid url has been entered correctly */
if(!URLUtil.isValidUrl(uploadUrl)) {
Log.wtf(TAG, "Invalid url: " + uploadUrl);
return null;
}
return new MultipleFileTransfer(uploadUrl);
}
}
As the constructor cannot return anything, I am wondering that is the best way to handle this?
Typically, allow the exception to propagate to the caller, either directly or by wrapping it in your own higher-level abstraction exception. (In your case, just allowing it directly looks more appropriate.)
public MultipleFileTransfer(final String uploadUrl) throws MalformedURLException {
// -------------------------------------------------^
dataOutputStream = null;
fileInputStream = null;
httpURLConnection = null;
url = new URL(uploadUrl);
}
Since your instance isn't useful without the URL, it makes sense for construction to fail.
Or if you want to log it in the constructor (but if it's propagating, typically any logging if appropriate would be handled by the caller):
// Logging and re-throwing, but probably not recommended
public MultipleFileTransfer(final String uploadUrl) throws MalformedURLException {
// -------------------------------------------------^
dataOutputStream = null;
fileInputStream = null;
httpURLConnection = null;
try {
url = new URL(uploadUrl);
} catch (MalformedURLException e) {
Log.wtf(TAG, e.getMessage());
throw e; // <== Rethrowing
}
}
I can think of two decent ways to handle the situation:
(1) Let the constructor throw an exception. Either rethrow the same exception after logging, or throw a different exception. If the exception it throws isn't a RuntimeException (and MalformedURLException is not a RuntimeException), you'll need to add a throws clause to the constructor.
(2) Let the constructor create an object anyway, but mark it as an "invalid" object that cannot be used. I'd add an isValid() or isInvalid() method so that the caller can query whether it's valid. Other methods should throw IllegalStateException if they're called on an invalid object.
I don't think one is clearly better than the other. It depends on preference and perhaps on the design of the rest of the program.
I am trying to write a test case for checkRegistry method, which is a private method, I am using PowerMock-EasyMock-Juint to realize this.
Now to test this methods I want to suppress the calls to super calls methods
eg:
intigration = super.getParam("integritycheck");
I dont want the call to go to superClass method, but at the same time I want the variable integration to be set. How do I realize this?
The difficulty is
super.getParam("integritycheck"); and
sTmpOverride = super.getParam("RESPONSE_OVERRIDE"); will return different results.
Method that I am trying to write unit test.
private String checkRegistry()
{
String intigration = "";
String sresponse = "";
try
{
try
{
intigration = super.getParam("integritycheck");
sresponse = CustomImpl.getParam("responseWrite");
**Some Business Logic**
sTmpOverride = super.getParam("RESPONSE_OVERRIDE");
if (sTmpOverride == null) {
this._bRespOverride = true;
} else {
this._bRespOverride = sTmpOverride.trim().equalsIgnoreCase(
"true");
}
sTmpOverride = super.getParam("ERROR_OVERRIDE");
if (sTmpOverride == null) {
this._bErrOverride = true;
} else {
this._bErrOverride = sTmpOverride.trim().equalsIgnoreCase(
"true");
}
**Some Business Logic**
Logging.info("integritycheck : " + intigration );
Logging.info("responseWrite : " + sresponse );
super.track("Error Directory : " + sErrorPath);
}
}
catch (Exception ex)
{
_result= false;
}
return _result;
}
I am struck on using the below method
suppress(method(CustomRegisterChecker.class, "getParam"));
where CustomRegisterChecker.class is the super class.
UPDATE1:
//Here I am creating a mock for the super class
CustomRegisterChecker customRegisterMock=createMock(AbstractListener.class);
//I am supresssing the calls made to the super class and giving my own response
expect(abstractListenerMock.getParam("integritycheck ")).andReturn(null);
expect(abstractListenerMock.getParam("responseWrite")).andReturn(null);
But How do I invoke and test the method. I tried using Reflection API. BUT that does not work. it just simply run the program, supressing the super class methods does not happen here.
Sometimes when I use multiple Modeshape actions inside one function I get this error:
javax.jcr.RepositoryException: The session with an ID of '060742fc6' has been closed and can no longer be used.
I couldn't find any explanations of this on the web. Here is what I call:
myFunction( service.doSomething ( service.getStuff ( id, "en_EN" ).getPath() ) );
doSomething, getStuff:
#Interceptors({Foo.class, TraceInterceptor.class})
#Override
public Node doSomething(final String bar) throws RepositoryException {
return modeshape.execute(new JcrHandler<Node>() {
#Override
public Node execute(Session session) throws RepositoryException {
return session.getNode(bar);
}
});
}
#Interceptors(TraceInterceptor.class)
#Override
public ObjectExtended getStuff(final String query, final String language)
throws RepositoryException {
return modeshape.execute(new JcrHandler<ObjectExtended>() {
#Override
public ObjectExtendedexecute(Session session)
throws RepositoryException {
QueryManager queryManager = session.getWorkspace().getQueryManager();
ObjectExtendeditem = null;
String queryWrapped =
"select * from [th:this] as c where name(c)='lang_"
+ language + "' and c.[th:mylabel] "
+ "= '" + queryStr + "'";
LOGGER.debug("Query: " + queryWrapped);
Query query =
queryManager.createQuery(queryWrapped,Query.JCR_SQL2);
QueryResult result = query.execute();
NodeIterator iter = result.getNodes();
while (iter.hasNext()) {
Node node = iter.nextNode().getParent();
if (node.isNodeType("th:term")) {
item = new ObjectExtended();
item.setLabel(getLabel(language, node));
item.setPath(node.getPath());
}
}
return item;
}
});
}
Why is this happening please? What am I doing wrong?
That error message means one of two thing: either the repository is being shutdown, or the Session.logout() method is being called.
None of the above code shows how your sessions are being managed, and you don't say whether you are using a framework. But I suspect that somehow you are holding onto a Session too long (perhaps after your framework is closing the session), or the Session is leaking to multiple threads, and one thread is attempting to use it after the other has closed it.
The latter could be a real problem: while passing a single Session instance from one thread to another is okay (as long as the original thread no longer uses it), but per the JCR 2.0 specification Session instances are not threadsafe and should not be concurrently used by multiple threads.
If you're creating the Session in your code, it's often good to use a try-finally block:
Session session = null;
try {
session = ... // acquire the session
// use the session, including 0 or more calls to 'save()'
} catch ( RepositoryException e ) {
// handle it
} finally {
if ( session != null ) {
try {
session.logout();
} finally {
session = null;
}
}
}
Note that logout() does not throw a RepositoryException, so the above form usually works well. Of course, if you know you're not using session later on in the method, you don't need the inner try-finally to null the session reference:
Session session = null;
try {
session = ... // acquire the session
// use the session, including 0 or more calls to 'save()'
} catch ( RepositoryException e ) {
// handle it
} finally {
if ( session != null ) session.logout();
}
This kind of logic can easily be encapsulated.