Is it possible do make an EJB service which accepts callbacks and call it on the
client which invokes the service? The use case is: Uploading a large byte array
to the service which will parse it and transform the result into Objects and
persist them. I want to notify the client which of these steps are done.
#Local
public interface MyService {
Status upload(byte[] content, Callable<Void> onReceived, Calable<Void> onPersisting);
}
#Stateless(name = "MyService")
public class MyServiceImpl extends MyService {
Status upload(byte[] content, Callable<Void> onReceived, Calable<Void> onPersisting) {
// Invoke this because all date is transfered to server.
onReceived.call();
// Do the parsing stuff ...
onPersisting.call();
// Do the persisting stuff ...
return new Status(...); // Done or failed or such.
}
}
On the client I pass in the callables:
Context ctx = ...
MyService service = ctx.get(...);
ctx.upload(bytes, new Callable<void() {
#Override
public Void call() {
// Do something
return null;
}
}, new Callable<Void>() {
#Override
public Void call() {
// Do something
return null;
}
});
Is something like that possible in EJB?
I'm new to the JEE world: I know that the client get some stubs of the EJB
interface and the calls are transfered by "background magic" to the servers
real EJB implementation.
Case 1: Using a local business interface (or no-interface view)
Yes it's possible as long as your service is only accessed by an local business interface. Why? A local business interface can be only accessed by local client.
A local client has these characteristics [LocalClients].
It must run in the same application as the enterprise bean it accesses.
It can be a web component or another enterprise bean.
To the local client, the location of the enterprise bean it accesses is not transparent.
To summarize the important characteristics. It run in the same application respectively in the same JVM, it's a web or EJB component and that the location of the accessed bean is not transparent for the local client. Please take a look at LocalClients for more details.
Below a simple Hello World example. My example uses a no-interface view this is equivalent to a local business interface.
Edit: Example expanded by JNDI lookup.
/** Service class */
import javax.ejb.Stateless;
#Stateless
public class Service {
public void upload(final Callback callback) {
callback.call();
}
}
/** Callback class */
public class Callback {
public void call() {
System.out.println(this + " called.");
}
}
/** Trigger class */
import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
#Singleton
public class Trigger {
#EJB
Service service;
#Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void triggerService() {
System.out.println("Trigger Service call");
service.upload(new Callback());
//or by JNDI lookup and method overriding
try {
Service serviceByLookup = (Service) InitialContext.doLookup("java:module/Service");
serviceByLookup.upload(new Callback() {
#Override
public void call() {
System.out.println("Overriden: " + super.toString());
}
});
} catch (final NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
It's also possible to implement the Callback class as StatelessBean and inject it in the Service class.
/** Service class */
#Stateless
public class Service {
#EJB
Callback callback;
public void upload() {
callback.call();
}
}
Case 2: Using a remote business interface
If you are using a remote interface it's not possible to pass a callback object to your EJB. To get status information back to your client you have to use JMS.
Below a short kick-off example.
#Remote
public interface IService {
void upload();
}
#Stateless
public class Service implements IService {
#EJB
private AsyncUploadStateSender uploadStateSender;
#Override
public void upload() {
for (int i = 0; i <= 100; i += 10) {
uploadStateSender.sendState(i);
try {
Thread.sleep(1000L);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
}
#Stateless
public class AsyncUploadStateSender {
#Resource(lookup = "jms/myQueue")
private Queue queue;
#Inject
private JMSContext jmsContext;
#Asynchronous
public void sendState(final int state) {
final JMSProducer producer = jmsContext.createProducer();
final TextMessage msg = jmsContext.createTextMessage("STATE CHANGED " + state + "%");
producer.send(queue, msg);
}
}
public class Client {
public static void main(final String args[]) throws NamingException, InterruptedException, JMSException {
final InitialContext ctx = ... // create the InitialContext;
final IService service = (IService) ctx.lookup("<JNDI NAME OF IService>");
final ConnectionFactory factory = (ConnectionFactory) ctx.lookup("jms/__defaultConnectionFactory");
final Queue queue = (Queue) ctx.lookup("jms/myQueue");
// set consumer
final Connection connection = factory.createConnection();
final MessageConsumer consumer = connection.createSession().createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(final Message msg) {
try {
System.out.println(((TextMessage) msg).getText());
} catch (final JMSException e) {
e.printStackTrace();
}
}
});
connection.start();
// start upload
service.upload();
Thread.sleep(1000L);
}
}
Note: you have to create the queue jms/myQueue and the connection factory jms/__defaultConnectionFactory in your application server to make the example work.
Related
I am trying to develop a spring boot app. I have written all core implementations in core java without spring framework. I am using that jar in this spring boot app. I would like to manage the concurrency of my rest controller. So, configured ThreadPoolTaskExecutor accordingly in the main class. Ideally, I want only 2 concurrent requests to get into the execute() method, which I annotated Async. I was testing for 2 concurrent requests at a time but I see in the log that my requests are entering execute() all at once. All the tasks are memory intensive. So those are failing with heap memory issues. I am trying to figure out the ideal concurrency number. I would like to know if my configuration is correct or am I missing something? Thank you.
Here's my main class:
#SpringBootApplication
#EnableAsync
public class RestapiApplication implements AsyncConfigurer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(RestapiApplication.class, args);
System.out.println("Rightdata Middleware ready to accept requests:");
}
#Bean(name = "executor1")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(2);
taskExecutor.setCorePoolSize(2);
taskExecutor.setThreadNamePrefix("LULExecutor-");
taskExecutor.setQueueCapacity(100);
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
And here's my REST controller:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
/**
* The log.
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
try {
execute(jsonObjectPOJO);
} catch (Exception e) {
e.getMessage();
}
}});
executor.shutdown();
return jsonObjectPOJO;
}
#Async("executor1")
private void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> futureTarget;
Future<?> futureSource;
futureSource = processSource(executorService);
futureTarget = processTarget(executorService);
manageSourceProcessingResults(futureSource);
manageTargetProcessingResults(futureTarget);
executorService.shutdown();
//Do rest of the tasks.
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processSource(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureSource = executorService.submit(coreActionClass);
return futureSource;
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processTarget(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
return futureTarget;
}
private void manageSourceProcessingResults(Future<?> futureSource) {
try{
futureSource.get();
} catch(Exception e){
e.printStackTrace();
}
}
private void manageTargetProcessingResults(Future<?> futureTarget) {
try{
futureTarget.get();
} catch(Exception e){
e.printStackTrace();
}
}
}
UPDATE- 1:
I have now changed the code to following:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
/**
* The log.
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
try {
execute(jsonObjectPOJO);
} catch (Exception e) {
e.getMessage();
}
}});
executor.shutdown();
return jsonObjectPOJO;
}
}
And AsyncService class:
public class AsyncService {
#Async("executor1")
public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> futureTarget;
Future<?> futureSource;
futureSource = processSource(executorService);
futureTarget = processTarget(executorService);
manageSourceProcessingResults(futureSource);
manageTargetProcessingResults(futureTarget);
executorService.shutdown();
//Do rest of the tasks.
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processSource(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureSource = executorService.submit(coreActionClass);
return futureSource;
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processTarget(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
return futureTarget;
}
private void manageSourceProcessingResults(Future<?> futureSource) {
try{
futureSource.get();
} catch(Exception e){
e.printStackTrace();
}
}
private void manageTargetProcessingResults(Future<?> futureTarget) {
try{
futureTarget.get();
} catch(Exception e){
e.printStackTrace();
}
}
}
My understanding is that when I configure maxpoolsize(2) no more
than 2 requests would be in the execute() method at one time. For a
new request to enter, one of the earlier requests has to complete
its execution. Is my understanding correct? Would the async apply
to the inner executor service?
I am of the view that at one time only 2 requests are handled and
each of those requests can spawn 2 different threads and complete
its task. Please clarify.
I see two problems.
1) In your process method you are creating a new ExecutorService. This is not needed. Instead just call the execute method after the jsonObjectPOJO is retrieved.
2) You cannot use #Async int he same class that it is implemented. You'll need to create a new class, lets called MyAsyncService to contain the #Async method. This is because of the aspect orientated programming that is going on under the covers.
Check out this link for more info. Below is a quote from the link.
First – let’s go over the rules – #Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won’t work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn’t work because it bypasses the proxy and calls the underlying method directly.
EDIT 1:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
#AutoWired
AsyncService asyncService;
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
asyncService.execute(jsonObjectPOJO);
return jsonObjectPOJO;
}
public class AsyncService {
#Async("executor1")
public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
//No Futures, no ExecutorServices, just process that request.
}
}
By creating and configuring the ThreadPoolTaskExecutor to use only 2 threads, you have accomplished your goal.
EDIT2: Spring #Async limit number of threads
I have a situation I cannot explain.
I am trying to create a RMI server that will call Spring services, but I cannot bind the beans to the rmi registry because they all are nulls.
The code is like this:
public class RMIServer {
private static final Logger LOG = LoggerFactory.getLogger(RMIServer.class);
public static void main(final String[] args) throws RemoteException, AlreadyBoundException {
final Registry registry = LocateRegistry.createRegistry(RmiConstants.RMI_PORT);
final ApplicationContext ctx = new AnnotationConfigApplicationContext(RmiConfiguration.class);
for (final String key : ctx.getBeansOfType(BaseRmi.class).keySet()) {
LOG.info("Registering {}...", key);
registry.bind(key, (BaseRmi) ctx.getBean(key));
}
LOG.info("RMI server was started...");
}
}
The Spring configuration class is:
#Configuration
#ImportResource({ "classpath*:app-context.xml" })
public class RmiConfiguration {
#Bean
AccountRmi accountRmi() {
try {
return new AccountRmiImpl();
} catch (final RemoteException e) {
return null;
}
}
}
The bean that I want to instantiate is this:
public class AccountRmiImpl extends BaseRmi implements AccountRmi {
private static final long serialVersionUID = 5798106327227442204L;
private final static Logger LOG = LoggerFactory.getLogger(AccountRmiImpl.class.getName());
#Autowired
private AccountService accountService;
public AccountRmiImpl() throws RemoteException {
super();
}
#Override
public List<PersonType> getPersonTypes() throws AppException {
return accountService.getPersonTypes();
}
}
The BaseRmi is:
public abstract class BaseRmi extends UnicastRemoteObject {
protected BaseRmi() throws RemoteException {
super();
}
private static final long serialVersionUID = 9115163829282749718L;
}
The interface for this bean is this:
public interface AccountRmi extends AccountFacade, java.rmi.Remote {
}
where the AccountFacade contains the business logic.
What I saw is that if I remove the java.rmi.Remote interface on the AccountRmi interfaces declaration the bean gets instantiated but I need that interface for the remote lookup. No errors are getting displayed in the logs. Does spring has a limitation for multiple interfaces on a bean declaration or it's just because of the java.rmi.Remote interfaces ?
I can provide further details if requested.
Many thanks,
Daniel
I'm trying to setup a simple UDP server using Netty following the example here but using Spring for wiring dependencies.
My Spring config class:
#Configuration
#ComponentScan("com.example.netty")
public class SpringConfig {
#Value("${netty.nThreads}")
private int nThreads;
#Autowired
private MyHandlerA myHandlerA;
#Autowired
private MyHandlerB myHandlerB;
#Bean(name = "bootstrap")
public Bootstrap bootstrap() {
Bootstrap b = new Bootstrap();
b.group(group())
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
#Override
protected void initChannel(DatagramChannel ch) throws Exception {
ch.pipeline().addLast(myHandlerA, myHandlerB);
}
});
return b;
}
#Bean(name = "group", destroyMethod = "shutdownGracefully")
public NioEventLoopGroup group() {
return new NioEventLoopGroup(nThreads);
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
My server class:
#Component
public class MyUDPServer {
#Autowired
private Bootstrap bootstrap;
#Value("${host}")
private String host;
#Value("${port}")
private int port;
#PostConstruct
public void start() throws Exception {
bootstrap.bind(host, port).sync().channel().closeFuture().await();
/* Never reached since the main thread blocks due to the call to await() */
}
}
During the blocking call to await(), I don't see my application listening on the specified interface. I've tried to run the sample (setting up the server directly from the main function) and it works. I didn't find examples for setting up a UDP server using Netty and Spring.
Thanks, Mickael
EDIT:
In order to avoid blocking the Main thread (which is used for Spring configuration), I've created a new thread as follows:
#Component
public class MyUDPServer extends Thread {
#Autowired
private Bootstrap bootstrap;
#Value("${host}")
private String host;
#Value("${port}")
private int port;
public MyUDPServer() {
setName("UDP Server");
}
#PostConstruct
#Override
public synchronized void start() {
super.start();
}
#Override
public void run() {
try {
bootstrap.bind(host, port).sync().channel().closeFuture().await();
} catch (InterruptedException e) {
} finally {
bootstrap.group().shutdownGracefully();
}
}
#PreDestroy
#Override
public void interrupt() {
super.interrupt();
}
}
I can see the new thread is blocked waiting for Channel close (as in the example). The Main thread can continue Spring configuration. However, it still doesn't work.
There is no need to wait for termination of the channel in #PostConstruct. Try to remove await().
I am developing a REST API using Jersey 2 and I need some of my classes to be instantiated on start up and not just when some resource request triggers it.
So what I am asking is: how do I achieve that an instance of SomethingImpl defined below here is created on server start up and not just when someone hits the something resource? In Guice I would use .asEagerSingleton().
Application:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new AbstractBinder() {
#Override
protected void configure() {
bind(" else").to(String.class);
bind(SomethingImpl.class).to(Something.class).in(Singleton.class);
}
});
register(SomeResource.class);
}
}
Something:
public interface Something {
String something();
}
public class SomethingImpl implements Something {
#Inject
public SomethingImpl(final String something) {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
System.out.println(something() + something);
try {
Thread.sleep(4000);
} catch (final InterruptedException e) {
break;
}
}
}
}).start();
}
#Override
public String something() {
return "Something";
}
}
Some resource:
#Path("/")
public class SomeResource {
private final Something something;
#Inject
public SomeResource(final Something something) {
this.something = something;
}
#GET
#Path("something")
public String something() {
return something.something();
}
}
In a later release of hk2 than is integrated with Jersey (but which will be integrated soon) you can have services that are marked #Immediate. These basically get started as soon as they are added to hk2. However, in order to make it work you will have to add the Immediate context to the system (e.g. https://hk2.java.net/2.2.0-b27/apidocs/org/glassfish/hk2/utilities/ServiceLocatorUtilities.html#enableImmediateScope%28org.glassfish.hk2.api.ServiceLocator%29)
It would be a good idea to lobby with the Jersey team to have this scope/context pair enabled by default (they already enable things like PerThread scope)
I have created this issue: https://java.net/jira/browse/JERSEY-2291 to request that Jersey enable #Immediate services by default
Write an implementation of a javax.servlet.ServletContextListener and add that listener to your web xml.
http://www.mkyong.com/servlet/what-is-listener-servletcontextlistener-example/
Besides what msknapp said, you can also use #WebListener for the servlet context listener so that you don't have to add the listener to web.xml. Then your listener will look like
#WebListener
public class SomethingListener implements ServletContextListener {
#Inject
private final Something something;
#Override
public void contextInitialized(ServletContextEvent sce) {
//put code about something here
}
}
I have created an EJB timer with a local interface and I am not able to do JNDI lookup for it from a ServletContextListener.
Here is part of the EJB code:
#Stateless
#LocalBinding(jndiBinding = "TimedFileDeletion")
public class TimedFileDeletionBean implements TimedFileDeletionBeanLocal {
#Resource
TimerService timerService;
private String timerInfo = "FileDeletionTimer";
public void startTimer() {
....
}
public boolean isItRunning() {
....
}
#Timeout
public void timeout(Timer timer) {
....
}
}
Here is the local interface:
public interface TimedFileDeletionBeanLocal {
public void startTimer();
public boolean isItRunning();
}
And here is the ServletContextListener:
public class StartupEventHandler implements ServletContextListener {
TimedFileDeletionBeanLocal timedFileDeletionBeanLocal;
public StartupEventHandler() {
try {
InitialContext ic = new InitialContext();
timedFileDeletionBeanLocal = (TimedFileDeletionBeanLocal) ic.lookup("java:comp/env/ejb/TimedFileDeletion");
} catch (NamingException e) {
e.printStackTrace();
}
}
public void contextInitialized(ServletContextEvent arg0) {
if(!timedFileDeletionBeanLocal.isItRunning()) {
timedFileDeletionBeanLocal.startTimer();
}
}
public void contextDestroyed(ServletContextEvent arg0) {
}
}
For the lookup I also used the following Strings but none of the worked:
- java:comp/env/TimedFileDeletion
- java:comp/TimedFileDeletion
- java:TimedFileDeletion
- TimedFileDeletion
In all cases I was getting a javax.naming.NameNotFoundException.
Any advice would be appreciated.
While starting JBoss it logs all the local/remote interfaces & their jndi configuration.
JBoss startup log :
15:26:47,394 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
hrms/AccountSummarySessionBean/local - EJB3.x Default Local Business Interface
hrms/AccountSummarySessionBean/local-com.cc.hrms.bl.accounts.generalaccount.session.AccountSummarySessionBeanLocal - EJB3.x Local Business Interface
Lookup :
initialCtx.lookup("hrms/AccountSummarySessionBean/local-com.cc.hrms.bl.accounts.generalaccount.session.AccountSummarySessionBeanLocal");
I am using JBoss-5 & have generalized method for lookup, just giving interface name.
You can modify it accordingly.