Если мы рассмотрим общие сценарии, в которых может быть выбрано это исключение, доступ к свойствам с объектом вверху.
Пример:
string postalcode=Customer.Address.PostalCode;
//if customer or address is null , this will through exeption
здесь, если адрес имеет значение null, то вы получите NullReferenceException.
Итак, в качестве практики мы всегда должны использовать проверку нуля, прежде чем обращаться к свойствам в таких объектах (особенно в общих)
string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
Истеризация потоков из управляемого управляемого сеанса компонента не обязательно является хаком, если она выполняет задание, которое вы хотите. Но нерестовые потоки при необходимости должны выполняться с особой осторожностью. Код не должен записываться таким образом, чтобы один пользователь мог, например, создать неограниченное количество потоков за сеанс и / или продолжить работу потоков даже после того, как сеанс будет уничтожен. Это рано или поздно взорвет ваше приложение.
Код должен быть написан таким образом, чтобы вы могли убедиться, что пользователь может, например, никогда не создавать более одного фонового потока за сеанс и гарантировать поток чтобы прерваться всякий раз, когда сессия будет уничтожена. Для выполнения нескольких задач в сеансе вам необходимо поставить в очередь задачи.
Кроме того, все эти потоки должны предпочтительно обслуживаться общим пулом потоков, чтобы вы могли наложить ограничение на общее количество порожденных потоков на уровне приложения. Средний сервер приложений Java EE предлагает пул потоков, управляемый контейнером, который вы можете использовать, среди прочих, EJB @Asynchronous
и @Schedule
. Чтобы быть независимым от контейнера, вы также можете использовать Util Concurrent ExecutorService
и ScheduledExecutorService
для Java 1.5.
Ниже приведены примеры Java EE 6+ с EJB.
@Named
@RequestScoped // Or @ViewScoped
public class Bean {
@EJB
private SomeService someService;
public void submit() {
someService.asyncTask();
// ... (this code will immediately continue without waiting)
}
}
@Stateless
public class SomeService {
@Asynchronous
public void asyncTask() {
// ...
}
}
@Named
@RequestScoped // Or @ViewScoped
public class Bean {
private Future<List<Entity>> asyncEntities;
@EJB
private EntityService entityService;
@PostConstruct
public void init() {
asyncEntities = entityService.asyncList();
// ... (this code will immediately continue without waiting)
}
public List<Entity> getEntities() {
try {
return asyncEntities.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new FacesException(e);
} catch (ExecutionException e) {
throw new FacesException(e);
}
}
}
@Stateless
public class EntityService {
@PersistenceContext
private EntityManager entityManager;
@Asynchronous
public Future<List<Entity>> asyncList() {
List<Entity> entities = entityManager
.createQuery("SELECT e FROM Entity e", Entity.class)
.getResultList();
return new AsyncResult<>(entities);
}
}
Если вы используете библиотечную утилиту JSF OmniFaces , это можно сделать еще быстрее, если вы аннотируете управляемый компонент с помощью @Eager
.
@Singleton
public class BackgroundJobManager {
@Schedule(hour="0", minute="0", second="0", persistent=false)
public void someDailyJob() {
// ... (runs every start of day)
}
@Schedule(hour="*/1", minute="0", second="0", persistent=false)
public void someHourlyJob() {
// ... (runs every hour of day)
}
@Schedule(hour="*", minute="*/15", second="0", persistent=false)
public void someQuarterlyJob() {
// ... (runs every 15th minute of hour)
}
@Schedule(hour="*", minute="*", second="*/30", persistent=false)
public void someHalfminutelyJob() {
// ... (runs every 30th second of minute)
}
}
@Named
@RequestScoped // Or @ViewScoped
public class Bean {
@EJB
private SomeTop100Manager someTop100Manager;
public List<Some> getSomeTop100() {
return someTop100Manager.list();
}
}
@Singleton
@ConcurrencyManagement(BEAN)
public class SomeTop100Manager {
@PersistenceContext
private EntityManager entityManager;
private List<Some> top100;
@PostConstruct
@Schedule(hour="*", minute="*/1", second="0", persistent=false)
public void load() {
top100 = entityManager
.createNamedQuery("Some.top100", Some.class)
.getResultList();
}
public List<Some> list() {
return top100;
}
}
Проверьте EJB 3.1 @Asynchronous methods
. Это именно то, для чего они предназначены.
Небольшой пример использования OpenEJB 4.0.0-SNAPSHOT. Здесь у нас есть @Singleton
компонент с одним методом, отмеченным @Asynchronous
. Каждый раз, когда этот метод вызывается кем-либо, в этом случае ваш управляемый bean-компонент JSF, он немедленно возвращается независимо от того, сколько времени действительно принимает метод.
@Singleton
public class JobProcessor {
@Asynchronous
@Lock(READ)
@AccessTimeout(-1)
public Future<String> addJob(String jobName) {
// Pretend this job takes a while
doSomeHeavyLifting();
// Return our result
return new AsyncResult<String>(jobName);
}
private void doSomeHeavyLifting() {
try {
Thread.sleep(SECONDS.toMillis(10));
} catch (InterruptedException e) {
Thread.interrupted();
throw new IllegalStateException(e);
}
}
}
Вот небольшой тестовый файл, который вызывает @Asynchronous
метод несколько раз подряд.
Каждый вызов возвращает объект Future , который по существу запускает empty и позже будет иметь свое значение, заполненное контейнером, когда вызов соответствующего метода фактически завершается
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class JobProcessorTest extends TestCase {
public void test() throws Exception {
final Context context = EJBContainer.createEJBContainer().getContext();
final JobProcessor processor = (JobProcessor) context.lookup("java:global/async-methods/JobProcessor");
final long start = System.nanoTime();
// Queue up a bunch of work
final Future<String> red = processor.addJob("red");
final Future<String> orange = processor.addJob("orange");
final Future<String> yellow = processor.addJob("yellow");
final Future<String> green = processor.addJob("green");
final Future<String> blue = processor.addJob("blue");
final Future<String> violet = processor.addJob("violet");
// Wait for the result -- 1 minute worth of work
assertEquals("blue", blue.get());
assertEquals("orange", orange.get());
assertEquals("green", green.get());
assertEquals("red", red.get());
assertEquals("yellow", yellow.get());
assertEquals("violet", violet.get());
// How long did it take?
final long total = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
// Execution should be around 9 - 21 seconds
assertTrue("" + total, total > 9);
assertTrue("" + total, total < 21);
}
}
Под обложками, что делает эту работу, является:
JobProcessor
, вызывающий не видит на самом деле экземпляр JobProcessor
. Скорее это подкласс или прокси-сервер, у которого все методы переопределены. Методы, которые должны быть асинхронными, обрабатываются по-разному. Runnable
, которое обертывает метод и параметры, которые вы указали. Эта runnable предоставляется Executor , которая является просто рабочей очередью, прикрепленной к пулу потоков. Future
, который связан с Runnable
, который теперь ожидает в очереди. Runnable
, наконец, выполняет метод на экземпляре real JobProcessor
, он примет возвращаемое значение и установит его в Future
, сделав его доступным для вызывающего. Важно отметить, что объект AsyncResult
, возвращаемый JobProcessor
, не является тот же объект Future
, который удерживает вызывающий объект. Было бы здорово, если бы реальный JobProcessor
мог просто вернуться String
, а версия JobProcessor
вызывающего абонента могла вернуться Future<String>
, но мы не видели никакого способа сделать это, не добавляя дополнительной сложности. Таким образом, AsyncResult
представляет собой простой объект-оболочку. Контейнер вытащит String
, выбросит AsyncResult
, а затем String
в real Future
, который удерживает вызывающий абонент.
To получите прогресс на этом пути, просто передайте потокобезопасный объект, например AtomicInteger , к методу @Asynchronous
и периодически обновляйте его компонентом с кодом процента.
Я пробовал это и отлично работал с моим управляемым bean-файлом JSF
ExecutorService executor = Executors.newFixedThreadPool(1);
@EJB
private IMaterialSvc materialSvc;
private void updateMaterial(Material material, String status, Location position) {
executor.execute(new Runnable() {
public void run() {
synchronized (position) {
// TODO update material in audit? do we need materials in audit?
int index = position.getMaterials().indexOf(material);
Material m = materialSvc.getById(material.getId());
m.setStatus(status);
m = materialSvc.update(m);
if (index != -1) {
position.getMaterials().set(index, m);
}
}
}
});
}
@PreDestroy
public void destory() {
executor.shutdown();
}