Persisting in #PostConstruct: javax.persistence.TransactionRequiredException - java

My Entitymanager has no transaction when persisting an object in #PostConstruct.
I have no clue why and how to fix this, can anyone help me?
PS. If you need any other data, please ask
TestmachineManager
#Singleton
public class TestmachineManager {
#PersistenceContext
private EntityManager em;
private TimerTask handler = new TimerTask() {
#Override
public void run() {
DayPlanning planning = getPlanning();
Order order = planning.getNextInLine();
if(order instanceof Order) {
em.merge(planning);
List<String> tests = new ArrayList();
for(Test test : order.getTests()) {
tests.add(test.getName());
}
TestmachineSender.orderTests(order.getId(), order.getDomain(), tests);
timer.schedule(checker, safetycheckAt());
}
else {
timer.schedule(handler, postponeTo());
}
}
};
#PostConstruct
public void init() {
if(getPlanning().hasActiveTest()) {
handler.run();
}
}
private DayPlanning getPlanning() {
LocalDate today = new LocalDate(
Calendar.getInstance().getTime());
try {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DayPlanning> query = cb.createQuery(DayPlanning.class);
Root dayPlanning = query.from(DayPlanning.class);
Predicate predicateDate = cb.equal(dayPlanning.get("dateOfPlanning"), today.toDate());
query.select(dayPlanning).where(predicateDate);
return em.createQuery(query).getSingleResult();
} catch(NoResultException ex) {
DayPlanning newPlanning = new DayPlanning(today);
em.persist(newPlanning);
return newPlanning;
}
}
}
Stacktrace
Caused by: javax.persistence.TransactionRequiredException
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTxRequiredCheck(EntityManagerWrapper.java:163)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTransactionScopedTxCheck(EntityManagerWrapper.java:145)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:263)
at TestmachineManager.getPlanning(TestmachineManager.java:130)
at TestmachineManager.init(TestmachineManager.java:78)

You have a transaction in #PostConstruct for the Singleton bean. The moment you are loosing your transaction is when you launch a new timer using timer.schedule(checker, safetycheckAt()); or timer.schedule(handler, postponeTo());. I think that the timer object that you are using is actually java.util.Timer, looking at the signature.
You should try using the EJB TimerService resource. For example:
#Resource
private TimerService timerService;
#Timeout
public void run() {
DayPlanning planning = getPlanning();
Order order = planning.getNextInLine();
if(order instanceof Order) {
em.merge(planning);
List<String> tests = new ArrayList();
for(Test test : order.getTests()) {
tests.add(test.getName());
}
TestmachineSender.orderTests(order.getId(), order.getDomain(), tests);
// if the checker handler doesn't need to run transactionally you could leave it like it was before
timer.schedule(checker, safetycheckAt());
// otherwise you could create a checker EJB that uses #Timeout in the same manner or #Scheduled from EJB3.1
} else {
// postpone timer
timerService.createTimer(postponeTo(), "postponed timer information");
}
}
#PostConstruct
public void init() {
if(getPlanning().hasActiveTest()) {
timerService.createTimer(computeDelay(), "timer information");
// or you could use the other create methods of the timerService, those that fits your needs better
}
}
I didn't tested the code, it's just rough code. Hope it helps.

Related

Resilience4j context propagator not able to propagte thread local values

I am trying to migrate my circuit breaker code from Hystrix to Resilience4j. The communication is between two applications out of which one is an artifact containing all the resilience 4j config in the java code itself and the second application which is a microservice uses it directly.
There's one RequestId which generates in the microservice and propagates to the artifact context where it gets printed in the logs. With Hystrix, it was working perfectly fine but ever since I moved to resilience, I am getting null for the request Id.
Below is my config for bulk head and context propagator :
ThreadPoolBulkheadConfig bulkheadConfig = ThreadPoolBulkheadConfig.custom()
.maxThreadPoolSize(maxThreadPoolSize)
.coreThreadPoolSize(coreThreadPoolSize)
.queueCapacity(queueCapacity)
.contextPropagator(new DummyContextPropagator())
.build();
// Bulk Head Registry
ThreadPoolBulkheadRegistry bulkheadRegistry = ThreadPoolBulkheadRegistry.of(bulkheadConfig);
// Create Bulk Head
ThreadPoolBulkhead bulkhead = bulkheadRegistry.bulkhead(name, bulkheadConfig);
Dummy Context Propagator :
public class DummyContextPropagator implements ContextPropagator {
private static final Logger log = LoggerFactory.getLogger( DummyContextPropagator.class);
#Override
public Supplier<Optional<Object>> retrieve() {
return () -> (Optional<Object>) get();
}
#Override
public Consumer<Optional<Object>> copy() {
return (t) -> t.ifPresent(e -> {
clear();
put(e);
});
}
#Override
public Consumer<Optional<Object>> clear() {
return (t) -> DummyContextHolder.clear();
}
public static class DummyContextHolder {
private static final ThreadLocal threadLocal = new ThreadLocal();
private DummyContextHolder() {
}
public static void put(Object context) {
if (threadLocal.get() != null) {
clear();
}
threadLocal.set(context);
}
public static void clear() {
if (threadLocal.get() != null) {
threadLocal.set(null);
threadLocal.remove();
}
}
public static Optional<Object> get() {
return Optional.ofNullable(threadLocal.get());
}
}
}
However, nothing seems to work so that I can get the RequestId.
Am I doing everything right or is there another way to do that ?
i think you want to get params from threadlocal from parent-thread when you in sub-thread, in hystrix it use command-model to decorate callabletask
in resilience4j i think u can fix it like this:
#Resource
DispatcherServlet dispatcherServlet;
#PostConstruct
public void changeThreadLocalModel() {
dispatcherServlet.setThreadContextInheritable(true);
}
i find my last answer may lead to some problems, when you use "dispatcherServlet.setThreadContextInheritable(true);"
it may pollute your custom thread-pool`s threadlocalmap;
so here is my final resolve, and it only works at resilience4j;
#Resource
Resilience4jBulkheadProvider resilience4jBulkheadProvider;
#PostConstruct
public void concurrentThreadContextStrategy() {
ThreadPoolBulkheadConfig threadPoolBulkheadConfig = ThreadPoolBulkheadConfig.custom().contextPropagator(new CustomInheritContextPropagator()).build();
resilience4jBulkheadProvider.configureDefault(id -> new Resilience4jBulkheadConfigurationBuilder()
.bulkheadConfig(BulkheadConfig.ofDefaults()).threadPoolBulkheadConfig(threadPoolBulkheadConfig)
.build());
}
private static class CustomInheritContextPropagator implements ContextPropagator<RequestAttributes> {
#Override
public Supplier<Optional<RequestAttributes>> retrieve() {
// give requestcontext to reference from threadlocal;
// this method call by web-container thread, such as tomcat, jetty,or undertow, depends on what you used;
return () -> Optional.ofNullable(RequestContextHolder.getRequestAttributes());
}
#Override
public Consumer<Optional<RequestAttributes>> copy() {
// load requestcontex into real-call thread
// this method call by resilience4j bulkhead thread;
return requestAttributes -> requestAttributes.ifPresent(context -> {
RequestContextHolder.resetRequestAttributes();
RequestContextHolder.setRequestAttributes(context);
});
}
#Override
public Consumer<Optional<RequestAttributes>> clear() {
// clean requestcontext finally ;
// this method call by resilience4j bulkhead thread;
return requestAttributes -> RequestContextHolder.resetRequestAttributes();
}
}
i got the same problem with springboot 2.5 et springboot cloud 2020.0.6
and I solved it with an implementation of ContextPropagator
public class SleuthPropagator implements ContextPropagator<TraceContext> {
ThreadLocal<ScopedSpan> scopedSpanThreadLocal = new ThreadLocal<>();
#Override
public Supplier<Optional<TraceContext>> retrieve() {
return this::getCurrentcontext;
}
#Override
public Consumer<Optional<TraceContext>> copy() {
return c -> {
if (!c.isPresent()) {
return;
}
TraceContext traceContext = c.get();
ScopedSpan resilience4jSpan = getTracer()
.map(t -> t.startScopedSpanWithParent("Resilience4j", traceContext))
.orElse(null);
scopedSpanThreadLocal.set(resilience4jSpan);
};
}
#Override
public Consumer<Optional<TraceContext>> clear() {
return t -> {
try {
ScopedSpan resilience4jSpan = scopedSpanThreadLocal.get();
if (resilience4jSpan != null) {
resilience4jSpan.finish();
}
} finally {
scopedSpanThreadLocal.remove();
}
};
}
private static Optional<Tracer> getTracer() {
return Optional.ofNullable(Tracing.current())
.map(Tracing::tracer);
}
private Optional<TraceContext> getCurrentcontext() {
return getTracer()
.map(Tracer::currentSpan)
.map(Span::context);
}
}
And use the propagator in adding this to your application.properties
resilience4j.thread-pool-bulkhead.instances.YOUR_BULKHEAD_CONFIG.context-propagators=com.your.package.SleuthPropagator

Java/Mockito - A unit test for the thread to sleep exponentially before retry

I am trying to create a unit test class to test the class I created. This class tries to send a message and if it fails to send, it will wait exponentially (1s, 2s, 4s, 8s etc) before it retries to send the message. I want to test if this exponential wait behaviour works correctly. But I am new to unit testing and not really sure how to go about testing it using JUnit and Mockito. Any help would be greatly appreciated!
#Slf4j
#Setter
#RequiredArgsConstructor
#AllArgsConstructor(access = AccessLevel.PACKAGE)
public class RetriableProcessorExponentialDecorator implements
AbsMessageProcessorDecorator {
private final AbsMessageProcessor messageProcessor;
#Autowired
private AbsMessageActiveMQConfiguration configuration;
#Override
public void onMessage(AbsMessage message) throws Exception {
int executionCounter = 0;
long delay = 1000;
final int maxRetries = this.configuration.getExceptionRetry() + 1;
do {
executionCounter++;
try {
this.messageProcessor.onMessage(message);
} catch (RetriableException e) {
log.info("Failed to process message. Retry #{}", executionCounter);
delay = (long) (delay * (Math.pow(this.configuration.getMultiplier(), executionCounter)));
Thread.sleep(delay);
} catch (Exception e) {
// We don't retry on this, only RetriableException.
throw e;
}
} while (executionCounter < maxRetries && delay < Long.MAX_VALUE);
}
}
P.S Based on #Andy Turner's suggestion, I added a line private final DefaultSleeper defaultSleeper; in my RetriableProcessorExponentialDecorator class, and then replace Thread.sleep(delay) with defaultSleeper.sleep(delay).
Then in my unit test class, I mocked DefaultSleeper by doing #Mock
private DefaultSleeper sleeper; and passed the mock object through the constructor of RetriableProcessorExponentialDecorator like the following:
#Before
public void setUp() {
this.decorator = new
RetriableProcessorExponentialDecorator(sleeper, processor,
configuration);
}
Am I on the right track?
Create an interface something like this:
interface Sleeper {
void sleep(long delay) throws InterruptedException;
}
Then you can create an implementation of this:
class DefaultSleeper implements Sleeper {
#Override public void sleep(long delay) throws InterruptedException {
Thread.sleep(delay);
}
}
and mock it: Sleeper sleeper = mock(Sleeper.class);.
Now you can inject an instance of this into your RetriableProcessorExponentialDecorator, and test for interactions with the mock in your test.

How to restart scheduled task on runtime with EnableScheduling annotation in spring?

I have been investigating how to change the frequency of a job on runtime with Java 8 and spring. This question was very useful but it did not totally solve my issue.
I can now configure the date when to job should be executed next. But If set the delay to 1 year, then I need to wait 1 year before the new configuration in taken into account.
My idea would be to stop the scheduled task if the configuration value is changed (so from another class). Then recalculate the next time the task should be executed. Perhaps there is an easier way of doing this.
Here is the code I have so far.
#Configuration
#EnableScheduling
public class RequestSchedulerConfig implements SchedulingConfigurer {
#Autowired
SchedulerConfigService schedulerConfigService;
#Bean
public RequestScheduler myBean() {
return new RequestScheduler();
}
#Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
#Override public void run() {
myBean().startReplenishmentComputation();
}
},
new Trigger() {
#Override public Date nextExecutionTime(TriggerContext triggerContext) {
Duration d = schedulerConfigService.getIntervalFromDB();
return DateTime.now().plus(d).toDate();
}
}
);
}
}
This would be what I would like to do.
#RestController
#RequestMapping("/api/config/scheduler")
public class RequestSchedulerController {
#Autowired
ApplicationConfigWrapper applicationConfigWrapper;
#RequestMapping("/set/")
#ResponseBody
public String setRequestSchedulerConfig(#RequestParam(value = "frequency", defaultValue = "") final String frequencyInSeconds){
changeValueInDb(frequencyInSeconds);
myJob.restart();
return "Yeah";
}
}
Create a singleton bean that gets an injected TaskScheduler. This will hold as state variables all ScheduledFutures, like private ScheduledFuture job1;
On deployment, load from databases all schedule data and start the jobs, filling in all state variables like job1.
On change of scheduling data, cancel the corresponding Future (e.g job1) and then start it again with the new scheduling data.
The key idea here is to get control on the Futures as they are created, so to save them in some state variables, so that when something in scheduling data changes, you can cancel them.
Here is the working code:
applicationContext.xml
<task:annotation-driven />
<task:scheduler id="infScheduler" pool-size="10"/>
The singleton bean, that holds the Futures
#Component
public class SchedulerServiceImpl implements SchedulerService {
private static final Logger logger = LoggerFactory.getLogger(SchedulerServiceImpl.class);
#Autowired
#Qualifier(value="infScheduler")
private TaskScheduler taskScheduler;
#Autowired
private MyService myService;
private ScheduledFuture job1;//for other jobs you can add new private state variables
//Call this on deployment from the ScheduleDataRepository and everytime when schedule data changes.
#Override
public synchronized void scheduleJob(int jobNr, long newRate) {//you are free to change/add new scheduling data, but suppose for now you only want to change the rate
if (jobNr == 1) {//instead of if/else you could use a map with all job data
if (job1 != null) {//job was already scheduled, we have to cancel it
job1.cancel(true);
}
//reschedule the same method with a new rate
job1 = taskScheduler.scheduleAtFixedRate(new ScheduledMethodRunnable(myService, "methodInMyServiceToReschedule"), newRate);
}
}
}
What about using Set<ScheduledTask> ScheduledTaskRegistrar.getScheduledTasks() to get all schedules tasks and calling ScheduledTask::cancel() ?
or maybe executing ThreadPoolTaskScheduler::shutdown()
and recreating ThreadPoolTaskScheduler and setting it again in ScheduledTaskRegistrar ?
The following, an improved version of this code, seems a working POC based on Spring Boot. You can start and stop the scheduled tasks any number of times based on a table configuration. But you can't start a stopped job from where it was stopped.
1) In the main class, make sure scheduling is enabled, and perhaps configure a ThreadPoolTaskScheduler with size more than one so scheduled tasks may run in parallel.
#SpringBootApplication
#EnableScheduling
#Bean
public TaskScheduler poolScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
scheduler.setPoolSize(10);
scheduler.initialize();
return scheduler;
}
2) an object that contains the schedule configuration, e.g. a cron like configuration in this case:
public class ScheduleConfigVo {
//some constructors, getter/setters
private String taskName;
private String configValue; // like */10 * * * * * for cron
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ScheduleConfigVo that = (ScheduleConfigVo) o;
return taskName.equals(that.taskName) &&
configValue.equals(that.configValue) ;
}
#Override
public int hashCode() {
return Objects.hash(taskName, configValue);
}
}
equals and hashCode are needed since object comparison will be conducted.
3) I use mybatis, so the sheduled selection is something like:
#Mapper
public interface ScheduleConfigMapper {
List<ScheduleConfigVo> getAllConfigure();
}
and
public class ScheduleConfigMapperImpl implements ScheduleConfigMapper {
#Override
public List<ScheduleConfigVo>getAllConfigure() {
return getAllConfigure();
}
}
with a simple companion mybatis xml configuration (not shown here but can find it anywhere in the internet).
4) create a table and populate it with a record
CREATE TABLE "SCHEDULER"
( "CLASS_NAME" VARCHAR2(100), --PK
"VALUE" VARCHAR2(20 BYTE) --not null
)
and populated it with a record class_name=Task1, value=*/10 * * * * * etc. => run like a cron every ten seconds
5) the scheduler part:
#Service
public class DynamicScheduler implements SchedulingConfigurer {
#Autowired
private ScheduleConfigMapper repo;
#Autowired
private Runnable [] tsks;
#Autowired
private TaskScheduler tsch;
private ScheduledTaskRegistrar scheduledTaskRegistrar;
private ScheduledFuture future;
private Map<String, ScheduledFuture> futureMap = new ConcurrentHashMap<>(); // for the moment it has only class name
List<ScheduleConfigVo> oldList = new ArrayList<>();
List<ScheduleConfigVo> newList;
List<ScheduleConfigVo> addList = new ArrayList<>();
List<ScheduleConfigVo> removeList = new ArrayList<>();
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
if (scheduledTaskRegistrar == null) {
scheduledTaskRegistrar = taskRegistrar;
}
if (taskRegistrar.getScheduler() == null) {
taskRegistrar.setScheduler(tsch);
}
updateJobList();
}
#Scheduled(fixedDelay = 5000)
public void updateJobList() {
newList = repo.getConfigure()== null ? new ArrayList<>() : repo.getConfigure();
addList.clear();
removeList.clear();
if (!newList.isEmpty()) {
//compare new List with oldList
if (!oldList.isEmpty()) {
addList = newList.stream().filter(e -> !oldList.contains(e)).collect(Collectors.toList());
removeList = oldList.stream().filter(e -> !newList.contains(e)).collect(Collectors.toList());
} else {
addList = new ArrayList<>(newList); // nothing to remove
}
} else { // nothing to add
if (!oldList.isEmpty()) {
removeList = new ArrayList<>(oldList);
} // else removeList = 0
}
log.info("addList="+ addList.toString());
log.info("removeList="+ removeList.toString());
//re-schedule here
for ( ScheduleConfigVo conf : removeList ) {
if ( !futureMap.isEmpty()){
future = futureMap.get(conf.getTaskName());
if (future != null) {
log.info("cancelling task "+conf.getTaskName() +" ...");
future.cancel(true);
log.info(conf.getTaskName() + " isCancelled = " + future.isCancelled());
futureMap.remove(conf.getTaskName());
}
}
}
for ( ScheduleConfigVo conf : addList ) {
for (Runnable o: tsks) {
if (o.getClass().getName().contains(conf.getTaskName())) { // o has fqn whereas conf has class name only
log.info("find " + o.getClass().getName() + " to add to scheduler");
future = scheduledTaskRegistrar.getScheduler().schedule(o, (TriggerContext a) -> {
CronTrigger crontrigger = new CronTrigger(conf.getConfigValue());
return crontrigger.nextExecutionTime(a);
});
futureMap.put(o.getClass().getName().substring(o.getClass().getName().lastIndexOf('.')+1), future);
}
}
}
oldList.clear();
oldList= newList;
}
6) one or more Runnable tasks that actually does the cron work, for instance:
#Slf4j
#Service
public class Task1 implements Runnable {
#Override
public void run() {
log.info("Task1 is running...");
}
}
Once the application is started, the cron job will run. The running interval changes as the value in the table changes, and the job stops as the table entry is removed.
Note that if the job runs longer than the cron interval, the next run is after the previous job finishes. You can simulate this situation by adding, for instance, sleep 15 seconds in Task1 above to test it. Sometimes after being cancelled, a job maybe still run till it's done.
***Just edit to add that if folks like lambda to save some lines, the above removeList and addList can be modified as:
removeList.stream().filter(conf -> {
future = futureMap.get(conf.getTaskName());
return future != null;
}).forEach((conf) -> {
log.info("cancelling task " + conf.getTaskName() + " ...");
future.cancel(true);
log.info(conf.getTaskName() + " isCancelled = " + future.isCancelled());
});
and
Arrays.stream(tsks).forEach(task -> {
addList.stream().filter(conf -> task.getClass().getName().contains(conf.getTaskName())).forEach(conf -> {
log.info("find " + task.getClass().getName() + " to add to scheduler");
future = scheduledTaskRegistrar.getScheduler().schedule(task, (TriggerContext a) -> {
CronTrigger crontrigger = new CronTrigger(conf.getConfigValue());
return crontrigger.nextExecutionTime(a);
});
futureMap.put(task.getClass().getName().substring(task.getClass().getName().lastIndexOf('.') + 1), future);
});
});
One simple approach is to only ever add new tasks, not to try and cancel or restart the scheduler.
Each time the configuration changes, just add a new task with its new configuration.
Then, whenever a task runs, it must first check some state (by querying database, or lookup up in a concurrent map, or whatever) to decide if it is the latest version. If it is, then it should proceed. Otherwise, it should end immediately.
The only downside is that if you are changing job configuration frequently compared to how often they run, then of course the list of scheduled tasks will keep growing in memory.

Java SwingWorker load data from database to List

I have a problem with my MVC application that displays data in a JTable. Everything worked fine, but I decided to add a SwingWorker to retrieve data from the database.
My controller calls the model with data from the database. It looks like this.
Model.java
public class Model {
private List<Category> people = new Vector<Category>();
public List<Category> getPeople() {
return new ArrayList<Category>(people);
}
public void load() throws Exception {
people.clear();
DAOFactory factory = DAOFactory.getFactory(DAOFactory.MYSQL);
CategoryDAO personDAO = factory.getCategoryDAO();
people.addAll(personDAO.getCategory());
}
}
I add SwingWorker to getCategory class
MySQLCategodyDAO.java
public class MySQLCategoryDAO extends SwingWorker<Void, Vector<Object>> implements CategoryDAO{
private Job job;
private List<Category> cat;
public MySQLCategoryDAO(Job job){
this.job = job;
}
#Override
protected Void doInBackground() throws Exception {
// TODO Auto-generated method stub
if(job == Job.SELECT){
getCategory();
System.out.println("Table selected");
}
return null;
}
#Override()
public void done(){
}
public List<Category> getCategory() throws SQLException
{
cat = new ArrayList<Category>();
Connection conn = Database.getInstance().getConnection();
System.out.println(conn);
String sql = "select id, name from kategorie";
Statement selectStatement = conn.createStatement();
ResultSet results = selectStatement.executeQuery(sql);
while(results.next())
{
int id = results.getInt("id");
String name = results.getString("name");
Category category = new Category(id, name);
cat.add(category);
}
results.close();
selectStatement.close();
return cat;
}
}
View just retrieves the data from the model:
people = model.getPeople();
for (Category person : people) {
tablemodel
.addRow(new Object[] { person.getId(), person.getName() });
}
The problem comes when you call SwingWorker in class Model.java
public void load() throws Exception {
people.clear();
DAOFactory factory = DAOFactory.getFactory(DAOFactory.MYSQL);
CategoryDAO personDAO = factory.getCategoryDAO();
people.addAll(new MySQLCategoryDAO(Job.SELECT).execute()); - ERROR
}
Error:-
The method addAll(Collection<? extends Category>) in the type List<Category> is not applicable for the
arguments (void)
I know SwingWorker returns nothing, because there is an error. I should write the code in the method done(), but I have no idea how to solve it.
execute does not have a return value so it can't be used in the way you are trying to use it. The idea of SwingWorker is that the task should be executed asynchronously so you need to rework your design.
The SwingWorker bears a result (the List<Category>) and you either need to:
put the result somewhere from inside the SwingWorker (such as with the publish mechanism)
or call get from the outside to wait for the task to finish and return.
Here is the tutorial for review: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Quick example:
class MySQLCategoryDAO extends SwingWorker<Void, Category> {
// ...
private List<Category> list; // do not modify inside doInBackground
MySQLCategoryDAO(Job job, List<Category> list) {
this.list = list;
// ...
}
#Override
protected Void doInBackground() {
// ...
while(results.next()) {
int id = results.getInt("id");
String name = results.getString("name");
publish(new Category(id, name)); // publish results to the EDT
}
// ...
return null;
}
#Override
protected void process(List<Category> chunks) {
list.addAll(chunks); // add results to the list on the EDT
// add to the JTable (?)
}
}
public void load() throws Exception {
people.clear();
DAOFactory factory = DAOFactory.getFactory(DAOFactory.MYSQL);
CategoryDAO personDAO = factory.getCategoryDAO();
// just execute
new MySQLCategoryDAO(Job.SELECT, people).execute();
}
If you want to populate the entire table at once then you can also publish a List after the loop instead of one Category at a time. process would receive a List<List<Category>> with a singular element.
Sorry my mistake.
From the view gets to model.getPeople (), but nothing is returned. I did a test:
But nothing is returned
public class Model {
private List<Category> people = new Vector<Category>();
public List<Category> getPeople() {
for (Category person : people) {
System.out.println(person.getName()); //no data
}
return new ArrayList<Category>(people);
}
public void load() throws Exception {
people.clear();
DAOFactory factory = DAOFactory.getFactory(DAOFactory.MYSQL);
new MySQLCategoryDAO(Job.SELECT,people).execute();
}
}

Java EE Future NullPointer

I'm new to java.util.concurrent package and i came to a problem with Future object.
This is a conversationScoped bean.
#Inject SomeBean stateFull;
Boolean comp = false, comp1 = false;
public void doSomething(){
stateFull.runProcess();
try {
comp = stateFull.getFuture().get();
System.out.println("Future "+syncEJB.getFuture().get());
updateView();
comp1 = stateFull.getFuture1().get();
System.out.println("Future "+syncEJB.getFuture().get());
updateView();
} catch (InterruptedException ex) {
Logger.getLogger(SynchronizationMB.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(SynchronizationMB.class.getName()).log(Level.SEVERE, null, ex);
}
}
SomeBean looks like this.
#Stateful
public class SomeBean{
#Inject AnotherBean stateLess;
private volatile Future<Boolean> future, future1;
#Asynchronous
public void runProcess(){
future = stateLess.compute();
future1 = stateLess.compute();
}
public Future<Boolean> getFuture() {
return future;
}
public Future<Boolean> getFuture1() {
return future1;
}
}
And AnotherBean:
#Stateless
public class AnotherBean{
#Asynchronous
public Future<Boolean> compute() {
boolean result;
System.out.println("--------------");
System.out.println("completed sync");
System.out.println("--------------");
result = true;
return new AsyncResult<Boolean>(result);
}
}
And now to my problem. I call doSomething() method and i think that according to documentation of Future.get() it should call runProcess() and than wait at
comp = stateFull.getFuture().get();
until future in SomeBean is completed from AnotherBean, but it just keep throwing NullPointerException. Anyone knows why it can be be happening?
-------------------EDIT-----------------------
NullPointer has been corrected. Now i have another problem. Let's say that i set more Future objects in Somebean by calling more methods in runProcess(). And then i want to update my page everytime the Future object get a result to see the progress. How do i do that? Right now i use
private void updateView(){
RequestContext ctx = RequestContext.getCurrentInstance();
ctx.update("mainFrm:info");
}
under every Boolean in doSomething() method, but it doesnt do what i want. All booleans just appear all at once.
NPE happens because starting new thread is a heavy operation, and when you call stateFull.getFuture().get(); new thread is not started yet(so feature is null).
Here the right way of using #Async with Future.

Categories

Resources