Clustered Singleton using Wildfly? - java

I'm trying to create a simple clustered Singleton on Wildfly 8.2. I've configured 2 Wildfly instances, running in a standalone clustered configuration. My app is deployed to both, and I'm able to access it with no problem.
My clustered EJB looks like this:
#Named
#Clustered
#Singleton
public class PeekPokeEJB implements PeekPoke {
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(PeekPokeEJB.class);
private static final long serialVersionUID = 2332663907180293111L;
private int value = -1;
#Override
public void poke() {
if (logger.isDebugEnabled()) {
logger.debug("poke() - start"); //$NON-NLS-1$
}
Random rand = new SecureRandom();
int newValue = rand.nextInt();
if (logger.isDebugEnabled()) {
logger.debug("poke() - int newValue=" + newValue); //$NON-NLS-1$
}
this.value = newValue;
if (logger.isDebugEnabled()) {
logger.debug("poke() - end"); //$NON-NLS-1$
}
}
#Override
public void peek() {
if (logger.isDebugEnabled()) {
logger.debug("peek() - start"); //$NON-NLS-1$
}
if (logger.isDebugEnabled()) {
logger.debug("peek() - value=" + value); //$NON-NLS-1$
}
if (logger.isDebugEnabled()) {
logger.debug("peek() - end"); //$NON-NLS-1$
}
}
}
...and I've written a very simple RESTful service to let me call these methods through the browser...
#Path("/test")
#Named
public class TestRS extends AbstractRestService {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestRS.class);
#Inject
private PeekPoke ejb = null;
#GET
#Path("/poke")
public void poke() {
if (logger.isDebugEnabled()) {
logger.debug("poke() - start"); //$NON-NLS-1$
}
this.ejb.poke();
if (logger.isDebugEnabled()) {
logger.debug("poke() - end"); //$NON-NLS-1$
}
}
#GET
#Path("/peek")
public void peek() {
if (logger.isDebugEnabled()) {
logger.debug("peek() - start"); //$NON-NLS-1$
}
this.ejb.peek();
if (logger.isDebugEnabled()) {
logger.debug("peek() - end"); //$NON-NLS-1$
}
}
}
I'm able to call both the peek and poke methods from a single Wildfly instance and get the expected value. However, if I attempt to call poke from one instance, and peek from another I see that the values are not being replicated across the EJBs.
I was under the impression that a clustered singleton would replicate the value of 'value' across both application servers, providing the same value regardless of which host I made the peek call from. Is this not correct? Is there something I'm missing that still needs to be added to this code?
I'd appreciate any help you can give me! Thanks!

Singleton session beans provide a formal programming construct that guarantees a session bean will be instantiated once per application in a particular Java Virtual Machine (JVM).
The JSR 318: Enterprise JavaBeans TM ,Version 3.1 says:
A Singleton session bean is a session bean component that is
instantiated once per application. In cases where the container is
distributed over many virtual machines, each application will have one
bean instance of the Singleton for each JVM
Hence, in a clustered application, each cluster member will have its own instance of singleton session beans and data is not shared across JVM instances (in Wildfly implementation).
In Wildfly if you need only one instance of singleton in a cluster scope you can use the SingletonService implementation. Using a SingletonService, the target service is installed on every node in the cluster but is only started on one node at any given time.
See:
Implement an HA Singleton
cluster-ha-singleton: A SingletonService deployed in a JAR started
by SingletonStartup and accessed by an EJB
UPDATE:
WildFly 10 adds the ability to deploy a given application as a
"singleton deployment". This is a new implementation of a feature that
existed in AS 6.0 and earlier. When deployed to a group of clustered
servers, a singleton deployment will only deploy on a single node at
any given time. If the node on which the deployment is active stops or
fails, the deployment will automatically start on another node.
See: WildFly 10 Final is now available!

Related

Associatation between : SpringIocContainer | ApplicationContext | WebApplicationContext

BackGround
After reading from 1 2 3 4 5 6 Links I reached the following conclusion-
As Spring mvc designed over standered servlets,and facilitate same functionality of servlet context and application context.In spring there is two type of context ApplicationContext and WebApplicationContext-
ApplicationContext initialise by ContextLoaderListener,single instanse per application.
WebApplicationContext loaded by per DispatcherServlet.
We can understand above like this ApplicationContext extends by WebApplicationContext so what ever stuff associated with ApplicationContext at the end this is part of WebApplicationContext.
Doubts
ApplicationContextAware offers which context object.
public class SomeThing implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeanException {
//this context object is `ApplicationContext` or `WebApplicationContext`?
}
}
context and container seems synonyms to most of us,I want to
give an example.Let say we have two dispatcher servlet one for
rest and other for mvc.
First Dispatcher-
public class RestInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[] { "/rest/*" };
}
}
Second Dispatcher-
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[] {
"/mvc/*"
};
}
}
than here there is two instance of WebApplicationContext,those
common part is loaded by ContextLoaderListner as define in
rootContext.
I am not sure, but there must not be 2 IocContainer in a single SpringApplication.
BeanFactory ie SpringIocContainer is,where all the bean object
lives,what ever objects we associates with WebApplicationContext is
part of Spring container,how does this container initialised by
WebApplicationContext?I want to want to know how does they both
associated with each other?
And whenever we did ctx.getBean()- this returns object from spring
container,how does this communication between context and container
happens?
There is a similar answer that denies the both are same,it says
Spring comes with several container implementations,Both load bean definitions, wire beans together, and dispense beans upon request,but an ApplicationContext offers much more.
So my point is why Both load bean definitions, wire beans together,this is kind of rework?
One more thing even though web-app is spring driven or not, there must be a context which standard servlet provides and used in Http communication......
Spring follows this or spring handles this in some other manner.And in spring context means a just IOC container, of which some part is loaded by DispacherServlet and some part is loaded by ContextLoaderListner and can facilitate much more such as I18N,access to static resource etc..
Basically, in a spring MVC application the spring contexts are registered in the servlet context of the web application. You can do that in the web.xml file setting the spring ContextLoaderListener or with java configuration. In the comments I pointed out this link where it explains how this is done via java configuration classes:
spring: where does `#autowired` look for beans?
There you can see how the 'connection' is done. Then, you asked in the comments what this achieves:
WebApplicationContextUtils.getWebApplicationContext(myServle‌​t.getServletContext(‌​))
If you check the code of that class you can see it gets the WebApplicationContext from the attributes of the ServletContext. These attributes are set in the initialization of the web application. If you notice, in the ContextLoader class (parent of ContextLoaderListener), in the initWebApplicationContext method it sets these attributes to the servlet context:
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{#link #CONTEXT_CLASS_PARAM contextClass}" and
* "{#link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* #param servletContext current servlet context
* #return the new WebApplicationContext
* #see #ContextLoader(WebApplicationContext)
* #see #CONTEXT_CLASS_PARAM
* #see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
That is done in this line:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
As you can see, it is stored in the same place where you are trying to get it with the WebApplicationContextUtils.getWebApplicationContext(myServle‌​t.getServletContext(‌​)) :
/**
* Find the root {#code WebApplicationContext} for this web app, typically
* loaded via {#link org.springframework.web.context.ContextLoaderListener}.
* <p>Will rethrow an exception that happened on root context startup,
* to differentiate between a failed context startup and no context at all.
* #param sc ServletContext to find the web application context for
* #return the root WebApplicationContext for this web app, or {#code null} if none
* #see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
*/
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
So as you can see all the answers to your doubts are in the code that spring executes during the startup of the web application.
Hope I answered your question.
For Your Doubt 1
In an spring application there is a single instance of context Which is WebAplicationCntext per DispatcherServlet.Which can be refer by a super Interface ApplicationContext-
public class SomeThing implements ApplicationContextAware{
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeanException{
//this context object is `WebApplicationContext` which is refer by `ApplicationContext`.
}
}
In spring , context means a just IOC container, of which some part is loaded by DispacherServlet and some part is loaded by ContextLoaderListner and can facilitate much more such as I18N,access to static resource etc
Your above understanding is almost correct.In Spring All the WebApplicationContext object shares some common reference which is rootContext.
This answer desn't include answer of doubt2, doubt3 ,and why all context perform same task.

spring bean startup/shutdown order configuration (start h2 db as server)

I'd like to create configuration/bean to automatically start H2DB in my development profile. I'd like to have it running as a tcp server. It's needed to be started before any DataSource configuration. Can someone tell me how to achieve this?
Wha have I done is
#Profile("h2")
#Component
public class H2DbServerConfiguration implements SmartLifecycle {
private static final Logger logger = LoggerFactory.getLogger(H2DbServerConfiguration.class);
private Server server;
#Override
public boolean isAutoStartup() {
return true;
}
#Override
public void stop(Runnable callback) {
stop();
new Thread(callback).start();
}
#Override
public void start() {
logger.debug("############################################");
logger.debug("############################################");
logger.debug("STARTING SERVER");
logger.debug("############################################");
logger.debug("############################################");
try {
server = Server.createTcpServer("-web", "-webAllowOthers", "-webPort", "8082").start();
} catch (SQLException e) {
throw new RuntimeException("Unable to start H2 server", e);
}
}
#Override
public void stop() {
logger.debug("############################################");
logger.debug("############################################");
logger.debug("STOPPING SERVER");
logger.debug("############################################");
logger.debug("############################################");
if (server != null)
if (server.isRunning(true))
server.stop();
}
#Override
public boolean isRunning() {
return server != null ? server.isRunning(true) : false;
}
#Override
public int getPhase() {
return 0;
}
}
but this isn't an option for me because component is created after datasource (I have liquibase setup so it's too late) and Phase is still the same that means FIFO order and I'd like to be FILO.
Mix #Profile and #Component seams to me a bad idea. Profiles are designed to work with Configuration (documentation)
Do you really need profile? In my opinion it makes sense if you have several possible configurations, one based on H2, and if you want be able to switch between these configurations (typically at start time by setting a properties...)
Manage the H2 server with a bean (documentation) seams correct to me (as suggested by Stefen). Maybe you will prefer annotations... If you want a spring profile, then you will need a Configuration object too. It will simply load the H2 server bean (in my opinion it's better to manage the H2 server lifecycle with a bean than with a context/config).
Create your server as a bean :
#Bean(initMethod = "start", destroyMethod = "stop")
Server h2Server() throws Exception {
return Server.createTcpServer("-tcp","-tcpAllowOthers","-tcpPort","9192");
}
Now you can configure spring to create other beans (e.g the datasource)
after the bean h2Server using #DependsOn
#DependsOn("h2Server")
#Bean
DataSource dataSource(){
...
}
Hi, what about using spring boot? It has automatically configured datasource so I don't want to reconfigure it.
You are right, to use the above approach you have to create your own datasource in order to annotate it with #DependsOn .
But it looks like this is not really necessary.
In one of my projects I am creating the h2Server as a bean as described.
I use the datasource created by spring, so without any #DependsOn.
It works perfectly. Just give it a try.
Your solution with SmartLifecycle does not work, because it creates the server on ApplicationContext refresh, which happens after all beans (including the datasource ) were created.

Updating Dropwizard config at runtime

Is it possible to have my app update the config settings at runtime? I can easily expose the settings I want in my UI but is there a way to allow the user to update settings and make them permanent ie save them to the config.yaml file? The only way I can see it to update the file by hand then restart the server which seems a bit limiting.
Yes. It is possible to reload the service classes at runtime.
Dropwizard by itself does not have the way to reload the app, but jersey has.
Jersey uses a container object internally to maintain the running application. Dropwizard uses the ServletContainer class of Jersey to run the application.
How to reload the app without restarting it -
Get a handle to the container used internally by jersey
You can do this by registering a AbstractContainerLifeCycleListener in Dropwizard Environment before starting the app. and implement its onStartup method as below -
In your main method where you start the app -
//getting the container instance
environment.jersey().register(new AbstractContainerLifecycleListener() {
#Override
public void onStartup(Container container) {
//initializing container - which will be used to reload the app
_container = container;
}
});
Add a method to your app to reload the app. It will take in the list of string which are the names of the service classes you want to reload. This method will call the reload method of the container with the new custom DropWizardConfiguration instance.
In your Application class
public static synchronized void reloadApp(List<String> reloadClasses) {
DropwizardResourceConfig dropwizardResourceConfig = new DropwizardResourceConfig();
for (String className : reloadClasses) {
try {
Class<?> serviceClass = Class.forName(className);
dropwizardResourceConfig.registerClasses(serviceClass);
System.out.printf(" + loaded class %s.\n", className);
} catch (ClassNotFoundException ex) {
System.out.printf(" ! class %s not found.\n", className);
}
}
_container.reload(dropwizardResourceConfig);
}
For more details see the example documentation of jersey - jersey example for reload
Consider going through the code and documentation of following files in Dropwizard/Jersey for a better understanding -
Container.java
ContainerLifeCycleListener.java
ServletContainer.java
AbstractContainerLifeCycleListener.java
DropWizardResourceConfig.java
ResourceConfig.java
No.
Yaml file is parsed at startup and given to the application as Configuration object once and for all. I believe you can change the file after that but it wouldn't affect your application until you restart it.
Possible follow up question: Can one restart the service programmatically?
AFAIK, no. I've researched and read the code somewhat for that but couldn't find a way to do that yet. If there is, I'd love to hear that :).
I made a task that reloads the main yaml file (it would be useful if something in the file changes). However, it is not reloading the environment. After researching this, Dropwizard uses a lot of final variables and it's quite hard to reload these on the go, without restarting the app.
class ReloadYAMLTask extends Task {
private String yamlFileName;
ReloadYAMLTask(String yamlFileName) {
super("reloadYaml");
this.yamlFileName = yamlFileName;
}
#Override
public void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
if (yamlFileName != null) {
ConfigurationFactoryFactory configurationFactoryFactory = new DefaultConfigurationFactoryFactory<ReportingServiceConfiguration>();
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = Jackson.newObjectMapper();
final ConfigurationFactory<ServiceConfiguration> configurationFactory = configurationFactoryFactory.create(ServiceConfiguration.class, validator, objectMapper, "dw");
File confFile = new File(yamlFileName);
configurationFactory.build(new File(confFile.toURI()));
}
}
}
You can change the configuration in the YAML and read it while your application is running. This will not however restart the server or change any server configurations. You will be able to read any changed custom configurations and use them. For example, you can change the logging level at runtime or reload other custom settings.
My solution -
Define a custom server command. You should use this command to start your application instead of the "server" command.
ArgsServerCommand.java
public class ArgsServerCommand<WC extends WebConfiguration> extends EnvironmentCommand<WC> {
private static final Logger LOGGER = LoggerFactory.getLogger(ArgsServerCommand.class);
private final Class<WC> configurationClass;
private Namespace _namespace;
public static String COMMAND_NAME = "args-server";
public ArgsServerCommand(Application<WC> application) {
super(application, "args-server", "Runs the Dropwizard application as an HTTP server specific to my settings");
this.configurationClass = application.getConfigurationClass();
}
/*
* Since we don't subclass ServerCommand, we need a concrete reference to the configuration
* class.
*/
#Override
protected Class<WC> getConfigurationClass() {
return configurationClass;
}
public Namespace getNamespace() {
return _namespace;
}
#Override
protected void run(Environment environment, Namespace namespace, WC configuration) throws Exception {
_namespace = namespace;
final Server server = configuration.getServerFactory().build(environment);
try {
server.addLifeCycleListener(new LifeCycleListener());
cleanupAsynchronously();
server.start();
} catch (Exception e) {
LOGGER.error("Unable to start server, shutting down", e);
server.stop();
cleanup();
throw e;
}
}
private class LifeCycleListener extends AbstractLifeCycle.AbstractLifeCycleListener {
#Override
public void lifeCycleStopped(LifeCycle event) {
cleanup();
}
}
}
Method to reload in your Application -
_ymlFilePath = null; //class variable
public static boolean reloadConfiguration() throws IOException, ConfigurationException {
boolean reloaded = false;
if (_ymlFilePath == null) {
List<Command> commands = _configurationBootstrap.getCommands();
for (Command command : commands) {
String commandName = command.getName();
if (commandName.equals(ArgsServerCommand.COMMAND_NAME)) {
Namespace namespace = ((ArgsServerCommand) command).getNamespace();
if (namespace != null) {
_ymlFilePath = namespace.getString("file");
}
}
}
}
ConfigurationFactoryFactory configurationFactoryFactory = _configurationBootstrap.getConfigurationFactoryFactory();
ValidatorFactory validatorFactory = _configurationBootstrap.getValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = _configurationBootstrap.getObjectMapper();
ConfigurationSourceProvider provider = _configurationBootstrap.getConfigurationSourceProvider();
final ConfigurationFactory<CustomWebConfiguration> configurationFactory = configurationFactoryFactory.create(CustomWebConfiguration.class, validator, objectMapper, "dw");
if (_ymlFilePath != null) {
// Refresh logging level.
CustomWebConfiguration webConfiguration = configurationFactory.build(provider, _ymlFilePath);
LoggingFactory loggingFactory = webConfiguration.getLoggingFactory();
loggingFactory.configure(_configurationBootstrap.getMetricRegistry(), _configurationBootstrap.getApplication().getName());
// Get my defined custom settings
CustomSettings customSettings = webConfiguration.getCustomSettings();
reloaded = true;
}
return reloaded;
}
Although this feature isn't supported out of the box by dropwizard, you're able to accomplish this fairly easy with the tools they give you.
Before I get started, note that this isn't a complete solution for the question asked as it doesn't persist the updated config values to the config.yml. However, this would be easy enough to implement yourself simply by writing to the config file from the application. If anyone would like to write this implementation feel free to open a PR on the example project I've linked below.
Code
Start off with a minimal config:
config.yml
myConfigValue: "hello"
And it's corresponding configuration file:
ExampleConfiguration.java
public class ExampleConfiguration extends Configuration {
private String myConfigValue;
public String getMyConfigValue() {
return myConfigValue;
}
public void setMyConfigValue(String value) {
myConfigValue = value;
}
}
Then create a task which updates the config:
UpdateConfigTask.java
public class UpdateConfigTask extends Task {
ExampleConfiguration config;
public UpdateConfigTask(ExampleConfiguration config) {
super("updateconfig");
this.config = config;
}
#Override
public void execute(Map<String, List<String>> parameters, PrintWriter output) {
config.setMyConfigValue("goodbye");
}
}
Also for demonstration purposes, create a resource which allows you to get the config value:
ConfigResource.java
#Path("/config")
public class ConfigResource {
private final ExampleConfiguration config;
public ConfigResource(ExampleConfiguration config) {
this.config = config;
}
#GET
public Response handleGet() {
return Response.ok().entity(config.getMyConfigValue()).build();
}
}
Finally wire everything up in your application:
ExampleApplication.java (exerpt)
environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));
Usage
Start up the application then run:
$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye
How it works
This works simply by passing the same reference to the constructor of ConfigResource.java and UpdateConfigTask.java. If you aren't familiar with the concept see here:
Is Java "pass-by-reference" or "pass-by-value"?
The linked classes above are to a project I've created which demonstrates this as a complete solution. Here's a link to the project:
scottg489/dropwizard-runtime-config-example
Footnote: I haven't verified this works with the built in configuration. However, the dropwizard Configuration class which you need to extend for your own configuration does have various "setters" for internal configuration, but it may not be safe to update those outside of run().
Disclaimer: The project I've linked here was created by me.

How to get an existing websocket instance

I'm working on an application that uses Websockets (Java EE 7) to send messages to all the connected clients asynchronously. The server (Websocket endpoint) should send these messages whenever a new article (an engagement modal in my app) is created.
Everytime a connection is established to the websocket endpoint, I'm adding the corresponding session to a list, which I could be able to access outside.
But the problem I had is, when I'm accessing this created websocket endpoint to which all the clients connected from outside (any other business class), I've get the existing instance (like a singleton).
So, can you please suggest me a way I can get an existing instance of the websocket endpoint, as I can't create it as new MyWebsocketEndPoint() coz it'll be created by the websocket internal mechanism whenever the request from a client is received.
For a ref:
private static WebSocketEndPoint INSTANCE = null;
public static WebSocketEndPoint getInstance() {
if(INSTANCE == null) {
// Instead of creating a new instance, I need an existing one
INSTANCE = new WebSocketEndPoint ();
}
return INSTANCE;
}
Thanks in advance.
The container creates a separate instance of the endpoint for every client connection, so you can't do what you're trying to do. But I think what you're trying to do is send a message to all the active client connections when an event occurs, which is fairly straightforward.
The javax.websocket.Session class has the getBasicRemote method to retrieve a RemoteEndpoint.Basic instance that represents the endpoint associated with that session.
You can retrieve all the open sessions by calling Session.getOpenSessions(), then iterate through them. The loop will send each client connection a message. Here's a simple example:
#ServerEndpoint("/myendpoint")
public class MyEndpoint {
#OnMessage
public void onMessage(Session session, String message) {
try {
for (Session s : session.getOpenSessions()) {
if (s.isOpen()) {
s.getBasicRemote().sendText(message);
}
} catch (IOException ex) { ... }
}
}
But in your case, you probably want to use CDI events to trigger the update to all the clients. In that case, you'd create a CDI event that a method in your Websocket endpoint class observes:
#ServerEndpoint("/myendpoint")
public class MyEndpoint {
// EJB that fires an event when a new article appears
#EJB
ArticleBean articleBean;
// a collection containing all the sessions
private static final Set<Session> sessions =
Collections.synchronizedSet(new HashSet<Session>());
#OnOpen
public void onOpen(final Session session) {
// add the new session to the set
sessions.add(session);
...
}
#OnClose
public void onClose(final Session session) {
// remove the session from the set
sessions.remove(session);
}
public void broadcastArticle(#Observes #NewArticleEvent ArticleEvent articleEvent) {
synchronized(sessions) {
for (Session s : sessions) {
if (s.isOpen()) {
try {
// send the article summary to all the connected clients
s.getBasicRemote().sendText("New article up:" + articleEvent.getArticle().getSummary());
} catch (IOException ex) { ... }
}
}
}
}
}
The EJB in the above example would do something like:
...
#Inject
Event<ArticleEvent> newArticleEvent;
public void publishArticle(Article article) {
...
newArticleEvent.fire(new ArticleEvent(article));
...
}
See the Java EE 7 Tutorial chapters on WebSockets and CDI Events.
Edit: Modified the #Observer method to use an event as a parameter.
Edit 2: wrapped the loop in broadcastArticle in synchronized, per #gcvt.
Edit 3: Updated links to Java EE 7 Tutorial. Nice job, Oracle. Sheesh.
Actually, WebSocket API provides a way how you can control endpoint instantiation. See https://tyrus.java.net/apidocs/1.2.1/javax/websocket/server/ServerEndpointConfig.Configurator.html
simple sample (taken from Tyrus - WebSocket RI test):
public static class MyServerConfigurator extends ServerEndpointConfig.Configurator {
public static final MyEndpointAnnotated testEndpoint1 = new MyEndpointAnnotated();
public static final MyEndpointProgrammatic testEndpoint2 = new MyEndpointProgrammatic();
#Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
if (endpointClass.equals(MyEndpointAnnotated.class)) {
return (T) testEndpoint1;
} else if (endpointClass.equals(MyEndpointProgrammatic.class)) {
return (T) testEndpoint2;
}
throw new InstantiationException();
}
}
You need to register this to an endpoint:
#ServerEndpoint(value = "/echoAnnotated", configurator = MyServerConfigurator.class)
public static class MyEndpointAnnotated {
#OnMessage
public String onMessage(String message) {
assertEquals(MyServerConfigurator.testEndpoint1, this);
return message;
}
}
or you can use it with programmatic endpoints as well:
public static class MyApplication implements ServerApplicationConfig {
#Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
return new HashSet<ServerEndpointConfig>
(Arrays.asList(ServerEndpointConfig.Builder
.create(MyEndpointProgrammatic.class, "/echoProgrammatic")
.configurator(new MyServerConfigurator())
.build()));
}
#Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
return new HashSet<Class<?>>(Arrays.asList(MyEndpointAnnotated.class));
}
Of course it is up to you if you will have one configurator used for all endpoints (ugly ifs as in presented snippet) or if you'll create separate configurator for each endpoint.
Please do not copy presented code as it is - this is only part of Tyrus tests and it does violate some of the basic OOM paradigms.
See https://github.com/tyrus-project/tyrus/blob/1.2.1/tests/e2e/src/test/java/org/glassfish/tyrus/test/e2e/GetEndpointInstanceTest.java for complete test.

Custom Annotation JSF

I wanted to make a custom annotation to check security on some functions for my JSF web application. For security I use Tomcat security with JaaS, so I have no application managed security to my disposal.
What actually want to do is make an annotation for my methods in the Backing Beans like Spring Security (#Secured("role")). My security system is implemented so that every function is a role and you can dynamically make "user roles" these are stored in the DB and when somebody logs in all the (function)roles in that "user role" will be set in tomcat security as roles.
So now I have this piece of code to check if my user can access the function:
public static void checkSecurity(final String function) {
final FacesContext facesContext = FacesContext.getCurrentInstance();
try {
if (facesContext.getExternalContext().getRemoteUser() == null) {
facesContext.getExternalContext().redirect("login.xhtml");
return;
}
if (!facesContext.getExternalContext().isUserInRole(function)) {
facesContext.getExternalContext().redirect("restricted.xhtml");
return;
}
} catch (final Exception ex /* Mandatory "IOException e" will be caught + all other exceptions. */) {
facesContext.getExternalContext().setResponseStatus(403); // HTTP Status 403: Forbidden. Can also throw 401.
facesContext.responseComplete();
}
}
Now I have to call this SecurityUtil.checkSecurity("name_of_function"); in every method.
But I want to have an annotation like this #CustomSecurity("function_name_role").
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomSecurity {
// Single Element called value.
String value();
}
And when a method has this annotation the checkSecurity function automatically has to be performed. So I have to scan for this annotation at a point, or make some kind of actionlistener. JSF should have some options for this but all the forums I found on this don't really help.
Does somebody has some ideas?
EDIT:
I tried this blog it works but only on an action of a component (and components don't render when you don't have the role). So how secure is this when people try to hack into the JSF structure. And I rather have it running on every method.
public class SecurityActionListener extends ActionListenerImpl implements ActionListener {
private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
#SuppressWarnings("unused")
#Override
public void processAction(final ActionEvent event) {
final FacesContext context = FacesContext.getCurrentInstance();
final Application application = context.getApplication();
final ConfigurableNavigationHandler navHandler = (ConfigurableNavigationHandler) application.getNavigationHandler();
// Action stuff
final UIComponent source = event.getComponent();
final ActionSource actionSource = (ActionSource) source;
MethodBinding binding;
binding = actionSource.getAction();
final String expr = binding.getExpressionString();
if (!expr.startsWith("#")) {
super.processAction(event);
return;
}
final int idx = expr.indexOf('.');
final String target = expr.substring(0, idx).substring(2);
final String t = expr.substring(idx + 1);
final String method = t.substring(0, (t.length() - 1));
final MethodExpression expression = new MethodExpressionMethodBindingAdapter(binding);
final ELContext elContext = context.getELContext();
final ExpressionFactory factory = context.getApplication().getExpressionFactory();
final ValueExpression ve = factory.createValueExpression(elContext, "#{" + target + '}', Object.class);
final Object result = ve.getValue(elContext);
// Check if the target method is a secured method
// and check security accordingly
final Method[] methods = result.getClass().getMethods();
for (final Method meth : methods) {
if (meth.getName().equals(method)) {
if (meth.isAnnotationPresent(CustomSecurity.class)) {
final CustomSecurity securityAnnotation = meth.getAnnotation(CustomSecurity.class);
System.out.println("Function to check security on: " + securityAnnotation.value()); // TODO TO LOG
SecurityUtil.checkSecurity(securityAnnotation.value());
} else {
super.processAction(event);
}
break;
}
}
}
}
And this in the faces-config.xml:
<action-listener>
com.nielsr.randompackagebecauseofnda.SecurityActionListener
</action-listener>
This blog could also be an answer, but I don't know how it will work with my JaaS Tomcat security because the security is in a separate project deployed as a standalone JAR in the tomcat lib folder.
But I actually don't know that I have to secure my Beans. Because I have configured all the functions (aka roles see above) that are on 1 page in the Web.xml as security constraints. And I render the components on the page only if you have to rights or "function_role" on that component. So is this secured enough? Or if somebody has a right to a function on a page can he render the components himself and so hack my site?
I'm not that familiar to JSF to know this, what is going on in that extra JSF abstraction layer between Controller and View? (I'm more of a Spring MVC developer, but because of requirements I have to use JSF but it's nice to broaden my knowledge.)
You can "scan for your Annotations" using
http://code.google.com/p/reflections/
Regards

Categories

Resources