Servlets + injection -multithreading issues - java

So for a school project we created a site where a user could submit a report on underwater life etc. We used simple dependency injection (javax.inject) and an error checking pattern as follows :
ReportService.java
public interface ReportService {
public static enum ReportServiceErrorsENUM {
DB_FAILURE, WRONG_COORD // etc
}
public Set<ReportServiceErrorsENUM> getLastErrors();
public int addNewReport(Report report);
}
ReportServiceImpl.java
public class ReportServiceImpl implements ReportService {
private Set<ReportServiceErrorsENUM> lastErrors;
private #Inject ReportDAO reportDAO;
#Override
public Set<ReportServiceErrorsENUM> getLastErrors() {
return this.lastErrors;
}
#Override
public int addNewReport(Report report) {
lastErrors= new HashSet<ReportServiceErrorsENUM>();//throw away previous errors
UserInput input = report.getUserInput();
if (input.getLatitude() == null) {
addError(ReportServiceErrorsENUM.WRONG_COORD);
}
// etc etc
if (reportDAO.insertReport(report) != 0) {
// failure inserting the report in the DB
addError(ReportServiceErrorsENUM.DB_ERROR);
}
if (lastErrors.isEmpty()) // if there were no errors
return EXIT_SUCCESS; // 0
return EXIT_FAILURE; // 1
}
}
SubmitReportController.java
#WebServlet("/submitreport")
public class SubmitReportController extends HttpServlet {
private static final long serialVersionUID = 1L;
private #Inject ReportService reportService;
#Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Report report = new Report();
// set the report's fields from the HttpServletRequest attributes
if(reportService.addNewReport(report) == ReportService.EXIT_FAILURE) {
for(ReportServiceErrorsENUM error : reportService.getLastErrors())
// display the errors etc
} else {
// display confirmation
}
}
}
The idea is that the Servlet controller calls the service (which is injected) then checks the services' return value and calls getLastErrors() on the service if there was an error - to inform the user what went wrong etc. Now I just came to realize that this is not thread safe - the #Inject'ed ReportService (reportService) will be shared by all threads using the servlet
Is it (crosses fingers) ?
How could one improve on this error mechanism ?
Thanks

typically for servlets you'd want to keep those variables (generally called "state") in some container-managed context.
i'd move these errors to the request scope - that way they're stored on the request object (conceptually) and any servlet/jsp/whatever working on that same request can see/edit them.
different requests mean different data storage.
example code for using the request scope from a servlet can be found here: http://www.exampledepot.com/egs/javax.servlet/State.html

Your design is neither thread-safe nor ready to server multiple users. It is not thread safe because several users (browsers) can hit the servlet at the same time and in turn access lastErrors set concurrently. (Yes, there is only one instance of servlet and your service).HashSet which you use is not thread safe.
Also if two different people try to use the same application, they will overwrite and have access to reports (errors) submitted by each other. In other words there is global state shared among all users while there should have been a state per user/session.
By fixing the second issue (I gave you a tip: use HTTPSession) you are unlikely to see the first issue. This is because it is rather rare to see concurrent access to the same session. But it's possible (concurrent AJAX requests, two browser tabs). Keep that in mind, but there are more important issues to solve now.

Related

Best practice to 'rollback' REST method calls inside method

The title might be incorrect, but I will try to explain my issue. My project is a Spring Boot project. I have services which do calls to external REST endpoints.
I have a service method which contains several method calls to other services I have. Every individual method call can be successful or not. Every method call is done to a REST endpoint and there can be issues that for example the webservice is not available or that it throws an unknown exception in rare cases. What ever happens, I need to be able to track which method calls were successful and if any one of them fails, I want to rollback to the original state as if nothing happened, see it a bit as #Transactional annotation. All REST calls are different endpoints and need to be called separately and are from an external party which I don't have influence on. Example:
public MyServiceImpl implements MyService {
#Autowired
private Process1Service;
#Autowired
private Process2Service;
#Autowired
private Process3Service;
#Autowired
private Process4Service;
public void bundledProcess() {
process1Service.createFileRESTcall();
process2Service.addFilePermissionsRESTcall();
process3Service.addFileMetadataRESTcall(); <-- might fail for example
process4Service.addFileTimestampRESTcall();
}
}
If for example process3Service.addFileMetadataRESTcall fails I want to do something like undo (in reverse order) for every step before process3:
process2Service.removeFilePermissionsRESTcall();
process1Service.deleteFileRESTcall();
I read about the Command pattern, but that seems to be used for Undo actions inside an application as a sort of history of actions performed, not inside a Spring web application. Is this correct for my use case too or should I track per method/webservice call if it was successful? Is there a best practice for doing this?
I guess however I track it, I need to know which method call failed and from there on perform my 'undo' method REST calls. Although in theory even these calls might also fail of course.
My main goal is to not have files being created (in my example) which any further processes have not been performed on. It should either be all successful or nothing. A sort of transactional.
Update1: improved pseudo implementation based on comments:
public Process1ServiceImpl implements Process1Service {
public void createFileRESTcall() throws MyException {
// Call an external REST api, pseudo code:
if (REST-call fails) {
throw new MyException("External REST api failed");
}
}
}
public class BundledProcessEvent {
private boolean createFileSuccess;
private boolean addFilePermissionsSuccess;
private boolean addFileMetadataSuccess;
private boolean addFileTimestampSuccess;
// Getters and setters
}
public MyServiceImpl implements MyService {
#Autowired
private Process1Service;
#Autowired
private Process2Service;
#Autowired
private Process3Service;
#Autowired
private Process4Service;
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Transactional(rollbackOn = MyException.class)
public void bundledProcess() {
BundleProcessEvent bundleProcessEvent = new BundleProcessEvent();
this.applicationEventPublisher.publishEvent(bundleProcessEvent);
bundleProcessEvent.setCreateFileSuccess = bundprocess1Service.createFileRESTcall();
bundleProcessEvent.setAddFilePermissionsSuccess = process2Service.addFilePermissionsRESTcall();
bundleProcessEvent.setAddFileMetadataSuccess = process3Service.addFileMetadataRESTcall();
bundleProcessEvent.setAddFileTimestampSuccess = process4Service.addFileTimestampRESTcall();
}
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollback(BundleProcessEvent bundleProcessEvent) {
// If the last process event is successful, we should not
// be in this rollback method even
//if (bundleProcessEvent.isAddFileTimestampSuccess()) {
// remove timestamp
//}
if (bundleProcessEvent.isAddFileMetadataSuccess()) {
// remove metadata
}
if (bundleProcessEvent.isAddFilePermissionsSuccess()) {
// remove file permissions
}
if (bundleProcessEvent.isCreateFileSuccess()) {
// remove file
}
}
Your operation looks like a transaction, so you can use #Transactional annotation. From your code I can't really tell how you are managing HTTP response calls for each of those operations, but you should consider having your service methods to return them, and then do a rollback depending on response calls. You can create an array of methods like so, but how exactly you want your logic to be is up to you.
private Process[] restCalls = new Process[] {
new Process() { public void call() { process1Service.createFileRESTcall(); } },
new Process() { public void call() { process2Service.addFilePermissionsRESTcall(); } },
new Process() { public void call() { process3Service.addFileMetadataRESTcall(); } },
new Process() { public void call() { process4Service.addFileTimestampRESTcall(); } },
};
interface Process {
void call();
}
#Transactional(rollbackOn = Exception.class)
public void bundledProcess() {
restCalls[0].call();
... // say, see which process returned wrong response code
}
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollback() {
// handle rollback according to failed method index
}
Check this article. Might come in handy.
The answer to this question is quite broad. There are various ways to do distributed transactions to go through them all here. However, since you are using Java and Spring, your best bet is to use something like JTA (Java Transaction API), which enables a distributed transactions across multiple services/instances/etc.. Fortunately, Spring Boot supports JTA using either Atomikos or Bitronix. You can read the doc here.
One approach to enable distributed transactions is through a message broker such as JMS, RabbitMQ, Kafka, ActiveMQ, etc. and use a protocol like XA transactions (two-phase commit). In the case of external services that do not support distributed, one approach is to write a wrapper service that understands XA transactions to that external service.

Wicket: Define Application wide variable

I wanted to make a little "log" on what the user is doing. I have different panels and all of these have Ajax functions such as "onclick", "onevent" and "onchange". What I planned was to define an Application wide ArrayList of Strings to log all the things.
I wrote following into WicketApplication.java
public class WicketApplication extends WebApplication {
private List<String> log = new ArrayList<String>();
#Override
public Class<? extends WebPage> getHomePage() {
//code
}
#Override
public void init() {
//code
}
public List<String> getLog() {
return log;
}
public void setLog(List<String> log) {
this.log = log;
}}
Then in one of my panels:
public class Foo extends Panel{
private static final long serialVersionUID = 1L;
private WicketApplication wapp = (WicketApplication) Application.get();
public Foo(String id){
super(id);
}
public void bar(){
List<String> protocol = wapp.getLog();
protocol.add(foo.getBarName() + " has been added to " + selectedKontakt.getObject().getName());
wapp.setLog(protocol);
}
}
In a next panel I tried to create a new reference to WicketApplication. But it seems not to be the same.
Now I have these questions:
Isn't WicketApplication unique and therefore usable for this kind of manipulation?
Do I have to take a session for this?
Can I even parse Applcation to WebApplication? Because I have this error in the console
wapp <----- field that is causing the problem
Is there any other way to create an Application wide variable?
I think you are doing it wrong (on multiple levels).
Firstly: if you want to log, use a Logging framework. E.g. LogBack, preferably accessed through SLF4J
Secondly: if you don't want to use a log framework, create a log service (a dedicated object, not the Wicket Application), use Dependency Injection to inject the log service into all components where you need it. Wicket Supports both Spring and Guice
Third: Static access to the WebApplication as suggested by the accepted answer sounds like a very bad idea (but it is apparently suggested by Wicket, to be fair).
Normal way of use is (static) method. Its typical, don't be afraid.
MyApllication m = MyApllication.get();
So is genrally easy in every wicket object.
Usually "statically overrided" to return correct type, (and can give additional control).
public static MyApllication getMyApplication() {
return (MyApllication) get();
}
or
public static MyApllication get() {
return (MyApllication ) WebApplication.get();
}
When this static method returns correct type, Your problem is resolved.
Analyse how is build AuthenticatedWebApplication over WebApplication (or WebApplication over Application) , its from Wicket team and seems be canonical
BTW I You will have idea access / execute actions dependent on user / session similar idea exist : WebSession override in MySession

RPC call - static methods is not working

I am trying to define a static method in the service interface to make an rpc call. But it doesn't allow me to do so. here I am pasting my code
Client class
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics) {
String url = GWT.getModuleBaseURL() + "domesticservice";
domesticServiceAsync = (DomesticServiceAsync) GWT.create(DomesticService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) domesticServiceAsync;
endpoint.setServiceEntryPoint(url);
domesticServiceAsync.sendDomesticData(product,dma,yrmnths,dist,metrics,new Domestichandler<Void>() );
}
public class Domestichandler<Void> implements AsyncCallback<Void> {
#Override
public void onFailure(Throwable caught) {
String error = caught.getMessage();
System.out.println(error);
}
public void onSuccess(Void result) {
System.out.println("perfect");
}
}
Service
public interface DomesticService extends RemoteService {
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics);
}
public interface DomesticServiceAsync {
void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics,AsyncCallback<Void> callback);
}
Server side -
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics) {
System.out.println(product);
}
Basically I am trying to send the values from the front interface to the server side and I don't want any return value. But the values passed to the server side should be stored globally in the server class so i can access those values in different method. I tried changing all the senddomestic values to static but it won't allow me to do so? why?
Because RemoteServiceServlet needs to invoke your service methods somehow and the implementation expects instance methods. But this shouldn't prevent you from assigning the method data to static fields. Just be aware of multi threading.
GWT always uses instance methods for RPC calls, static methods are not possible in this case.
What is important to understand about GWT is that any RemoteServiceServlet instances are created and maintained by the servlet container (e.g. Tomcat). The servlet container might create a number of servlet instances on startup (Tomcat creates 6 RemoteServiceServlet instances by default) and then uses load balancing to determine which servlet handles an RPC request at a particular point in time. Depending on settings of course, you have little control over which RemoteServiceServlet instance exactly will handle a specific RPC request.
Therefore, if you want to store information on the server side globally using RPC calls, the idea proposed by YuPPie to use static fields of your RemoteServiceServlet implementation is a BAD idea. You will have no idea which of the RemoteServiceServlet instances maintained by the server contains your static data, and any subsequent calls to retrieve the data will give erratic results.
You have a few options, though. Storing the information in a database (or something similar) is the most straightforward option, but from your post I'm guessing you want something simpler. A singleton class which holds your data is probably the way to go. A thread-safe example:
public class DataContainer
{
private static DataContainer _singleton;
private String _dataField1;
public static synchronized DataContainer getInstance()
{
if (_singleton == null)
_singleton = new DataContainer();
return _singleton;
}
public synchronized String getDataField1()
{
return _dataField1;
}
public synchronized void setDataField1(String dataField1)
{
_dataField1 = dataField1;
}
}
Then in the server side implementation of your RPC call you could do something like:
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics)
{
DataContainer.getInstance().setDataField1(product);
}
This way, if there are multiple servlet instances they will all share the singleton instance of DataContainer, thus giving you a place to store your data globally. I hope this will help you.

Spring Singleton Thread Safety

If I have a Java class defined below that is injected in my web application via dependency injection:
public AccountDao
{
private NamedParameterJdbcTemplate njt;
private List<Account> accounts;
public AccountDao(Datasource ds)
{
this.njt = new NamedParameterJdbcTemplate(ds);
refreshAccounts();
}
/*called at creation, and then via API calls to inform service new users have
been added to the database by a separate program*/
public void refreshAccounts()
{
this.accounts = /*call to database to get list of accounts*/
}
//called by every request to web service
public boolean isActiveAccount(String accountId)
{
Account a = map.get(accountId);
return a == null ? false : a.isActive();
}
}
I am concerned about thread safety. Does the Spring framework not handle cases where one request is reading from the list and it is currently being updated by another? I have used read/write locks before in other applications, but I have never thought about a case such as above before.
I was planning on using the bean as a singleton so I could reduce database load.
By the way, this is a follow up of the below question:
Java Memory Storage to Reduce Database Load - Safe?
EDIT:
So would code like this solve this problem:
/*called at creation, and then via API calls to inform service new users have
been added to the database by a separate program*/
public void refreshAccounts()
{
//java.util.concurrent.locks.Lock
final Lock w = lock.writeLock();
w.lock();
try{
this.accounts = /*call to database to get list of accounts*/
}
finally{
w.unlock();
}
}
//called by every request to web service
public boolean isActiveAccount(String accountId)
{
final Lock r = lock.readLock();
r.lock();
try{
Account a = map.get(accountId);
}
finally{
r.unlock();
}
return a == null ? false : a.isActive();
}
Spring framework does not do anything under the hood concerning the multithreaded behavior of a singleton bean. It is the developer's responsibility to deal with concurrency issue and thread safety of the singleton bean.
I would suggest reading the below article: Spring Singleton, Request, Session Beans and Thread Safety
You could have asked for clarification on my initial answer. Spring does not synchronize access to a bean. If you have a bean in the default scope (singleton), there will only be a single object for that bean, and all concurrent requests will access that object, requiring that object to the thread safe.
Most spring beans have no mutable state, and as such are trivially thread safe. Your bean has mutable state, so you need to ensure no thread sees a list of accounts the other thread is currently assembling.
The easiest way to do that is to make the accounts field volatile. That assumes that you assign the new list to the field after having filled it (as you appear to be doing).
private volatile List<Accounts> accounts;
As a singleton and non-synchronized, Spring will allow any number of threads to concurrently invoke isActiveAccount and refreshAccounts. So, no this class is not going to be thread-safe and will not reduce the database load.
we have many such meta data and have some 11 nodes running. on each app node we have static maps for such data so its one instance only, init from db at start up once in off peak hour every day or when support person triggers it. have an interal simple http post based API to send updates from 1 node to others for some of the data which we need updates in real time.
public AccountDao
{
private static List<Account> accounts;
private static List<String> activeAccounts;
private NamedParameterJdbcTemplate njt;
static {
try{
refreshAccounts();
}catch(Exception e){
//log but do not throw. any uncaught exceptions in static means your class is un-usable
}
}
public AccountDao(Datasource ds)
{
this.njt = new NamedParameterJdbcTemplate(ds);
//refreshAccounts();
}
/*called at creation, and then via API calls to inform service new users have
been added to the database by a separate program*/
public void refreshAccounts()
{
this.accounts = /*call to database to get list of accounts*/
}
public void addAccount(Account acEditedOrAdded)
{
//add or reove from map onr row
//can be called from this node or other node
//meaning if you have 2 nodes, keep IP port of each or use a internal web service or the like to tell
//node B when a account id added or changed in node A ...
}
//called by every request to web service
public static boolean isActiveAccount(String accountId)
{
Account a = map.get(accountId);
return a == null ? false : a.isActive();
}
}

How should I better handle loading of Wicket Models when using Wicket forms?

I am in the middle of writing a form using Apache Wicket. The same page/class is used to add a new item to the database AND to edit an existing record in the database. Of course, since the page constructor is called only once, the model is always set to whatever record is initially loaded on the page, or a new record if we're not editing an existing one.
I have found a number of ways to dynamically load data, but they seem verbose and a little clunky. I suspect there is a best practice for handling a scenario like this.
For reference, here is some edited code:
public class JobManagement extends WebPage {
private static final long serialVersionUID = 1L;
private long jobId = 0;
protected void setJobId(long id) {
this.jobId = id;
}
protected long getJobId() {
return jobId;
}
public JobManagement() {
LoadableDetachableModel<Job> jobModel = new LoadableDetachableModel<Job>() {
private static final long serialVersionUID = 1L;
#Override
protected Job load() {
Job job = (Job) EntityFactory.getInstance().getBean("job");
// if we're editing an existing job, load the object
if (jobId >= 1) {
job.load(jobId);
}
return job;
}
};
add(new FeedbackPanel("feedbackPanel"));
Form<Job> jobForm = new Form<Job>("jobForm") {
private static final long serialVersionUID = 1L;
#Override
public void onSubmit() {
// Handles the form submit...
}
};
add(jobForm);
jobForm.setModel(new CompoundPropertyModel<Job>(jobModel));
// SNIP ... All my form fields go here!
jobForm.add(new Button("submit"));
}
}
I'm using a LoadableDetachableModel, but it's not entirely clear to me how to best handle loading it dynamically whenever the page is rendered. I've attempted to load a new instance of a Model, override the getObject() class which returns my LoadableDetachableModel, but there's something that feels very wrong about that. Any input would be appreciated. I've been trying to feel my way through this framework via online documentation exclusively, so forgive my evident lack of familiarity.
To answer my own question, the problem I was experiencing was that the model that was bound to the form appeared to persist every time I returned to the page. This led me to believe the problem was how I was managing models, but the problems was actually related to how I was linking to pages.
The page above was being linked to as follows:
Link<String> link = new BookmarkablePageLink<String>("addLink", MyAddClass.class);
While that is an acceptable approach in some cases, it is not the correct approach in this particular case. What should have happened was this:
Link<String> link = new Link<String>("addLink") {
public void onClick() {
setResponsePage(new MyAddClass());
}
}
When links are handled dynamically in this way, my application behaves as intended. Thanks to everyone who pitched in to help open my eyes to this fundamental problem with my links.
While the link method (previous answer) could pose a problem and often does depending on how you implement a link, the actual problem in this case was with the "EntityFactory" that was being used to load the instance of the domain object. In this case, it was providing cached data instead of instantiating a new instance.
To sum up, things to evaluate in cases like these where the form's model object doesn't appear to reset when you browse to the form a second time:
1 - The link strategy you're using to get to the page
2 - The method you're using to load and bind the domain object to a model on the form

Categories

Resources