Get all existing session beans from all users in Spring - spring

Is there a way to get all existing session beans managed by Spring at runtime? Getting them for the current user is easy.
Any suggestions?
Thanks,
XLR

I don't do Spring, but in normal JSF/JSP/Servlet you would grab HttpSessionBindingListener for this. Basically you need to give the session scoped bean a static List<Bean> property and implement the interface accordingly that it updates the static list in the valueBound() and valueUnbound() methods.
You can find a detailed code example in this answer.

Here is a solution I came up with that utilizes Spring:
I make a normal Spring singleton bean called SessionBeanHolder.
This bean holds a list of my session beans.
When a user logs in, I add the session bean to my SessionBeanHolder.
When referring to session beans in Spring, you are actually referring to proxies.
So the key thing to making this work was to fetch the underlying bean to add to the SessionBeanHolder.
Below is the sample code:
Note: My session bean is called SessionInfo.
#Scope(value="singleton")
#Component
public class SessionBeanHolder {
static Set<SessionInfo> beans;
public SessionBeanHolder() {
beans = new HashSet<SessionInfo>();
}
public Collection<SessionInfo> getBeans() {
return beans;
}
public void addBean(SessionInfo bean) {
try {
this.beans.add(removeProxyFromBean(bean));
} catch (Exception e) {
e.printStackTrace();
}
}
// Fetch the underlying bean that the proxy refers to
private SessionInfo removeProxyFromBean(SessionInfo proxiedBean) {
if (proxiedBean instanceof Advised) {
try {
return (SessionInfo) ((Advised) proxiedBean).getTargetSource().getTarget();
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
return proxiedBean;
}
}
}
Naturally, whenever you want to add session bean or fetch a list of all beans, just autowire the SessionBeanHolder and use its methods.
#Autowired
SessionBeanHolder sessionBeanHolder;

Related

How to create a conditional bean in Spring

I need to create a Conditional Bean in Spring. The use case is as following:
Class 1
In this class we are trying to create the Bean, which should be created for some clients who have the required permission, and for others it will return empty(). Thus the application should boot-up for all the clients without the BeanCreationException
#org.springframework.context.annotation.Configuration
public class SomeBeanConfiguration {
#Bean
public Optional<SomeBean> someBean() {
// whoAmI() ? returns IAmClient_1 - for whom this bean should be created
// whoAmI() ? returns IAmClient_2 - for whom this bean should not be created
final String somePermission = whoAmI();
try {
return Optional.of(SomeBean.builder()
.withPermission(new SomeCredentialsProvider(somePermission))
.build());
} catch (Exception ex) {
LOG.error("SomeBean creation exception : ", ex);
}
return Optional.empty();
}
}
Class 2
Where we are using this Bean in Constructor injection
#Bean
public SomeHelper someHelper(Optional<SomeBean> someBean) {
return new someHelper(someBean);
}
But the someHelper for client, who have permission are also getting an Optional.empty() in constructor.
What I am doing wrong here? Can anyone please help?
You need to change your method that's creating the bean. It should not be returning a bean of type Optional, it should be returning a bean of type SomeBean. Also, consider rewriting your logic to something more understandable, like dropping the catch block and creating the bean based on the output of whoAmI().
#Bean
public SomeBean someBean() {
// whoAmI() ? returns IAmClient_1 - for whom this bean should be created
// whoAmI() ? returns IAmClient_2 - for whom this bean should not be created
String somePermission = whoAmI();
if (somePermission.equals("IAmClient_1") {
return SomeBean.builder().withPermission(newSomeCredentialsProvider(somePermission)).build());
} else {
return null;
}
}
Now, when you autowire the Optional, the optional will contain the bean for IAmClient_1, and will be empty for all other cases.
In my opinion, it would be better to always construct SomeBean and just modify its behavior based on the value of the permission you're checking, but that's up to you.

what the difference between the two codes (Spring Boot)?

These two codes should do exactly the same thing, but the first one works and the second one doesnt work. Can anyone review the code and give the details about why the code failed during second approach.
The first code :
#Component
public class AdminSqlUtil implements SqlUtil {
#Autowired private ApplicationContext context;
DataSource dataSource =(DataSource) context.getBean("adminDataSource");
public void runSqlFile(String SQLFileName) {
Resource resource = context.getResource(SQLFileName);
EncodedResource encodedResource = new EncodedResource(resource, Charset.forName("UTF-8"));
try {
ScriptUtils.executeSqlScript(dataSource.getConnection(), encodedResource);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
The second code :
#Component
public class AdminSqlUtil implements SqlUtil {
#Autowired private ApplicationContext context;
public void runSqlFile(String SQLFileName) {
Resource resource = context.getResource(SQLFileName);
EncodedResource encodedResource = new EncodedResource(resource, Charset.forName("UTF-8"));
try {
ScriptUtils.executeSqlScript((DataSource)context.getBean("adminDataSource").getConnection(), encodedResource);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
The first one has a private scope and the framework can not access it. You could have add #inject before your private scope variable so the framework can initialize it. However the best practice is to define a public dependency setter for that to work.
The second one on the other hand initiates the value at the start, which is not a dependency injection by the way. I am not talking about good and bad practice. It is wrong. We don’t initialize a variable which is suppose to be initialized by the framework.
So lets go with the first one, Try to add a setter for it.
Take a look at this link.

Event not working in Message Driven Bean

I'm trying to generate and handle an event when my MDB receives a message. Here is what I'm doing:
public class MDBBooks implements MessageListener {
#Inject
private Event<Update> messageReceived;
public MDBLibri() {
}
#Override
public void onMessage(Message message) {
System.out.println("Message received");
try {
Update u = message.getBody(Update.class);
messageReceived.fire(u);
if(u != null){
... stuff
}
} catch (JMSException ex) {
System.out.println("JMSException: " + ex.getMessage());
}
}
public void eventHandler(#Observes Update up) {
System.out.println("There was an update");
}
}
But it just does not work, the string "There was an update" it's not printed in the glassfish console. I can't really tell what's the problem, my textbook does it the same way pretty much. I'm assuming the event fires fine, but the event handler isn't notified.
You are correct that the observer method does not get notified. In fact, CDI doesn't even know it exists. The reason is that in CDI, message-driven beans are non-contextual objects. To simplify, they are not considered CDI beans, but you can still inject into them and intercept them.
Now, for CDI to recognize an observer method, you have to place it in a managed bean or a session bean. Quoting the spec:
An observer method is a non-abstract method of a managed bean class or session bean class (or of an extension, as defined in Container lifecycle events).
So a solution for you would be to place your observer method in another class, which is either a managed bean or a session bean.

Spring Transaction Doesn't Rollback

We have a Spring Transaction rollback issues, where rollback doesn't seems to be working.
Within my service layer method which is annotated with #Transactional I call three different DAOImpl classes to insert 3 records. The middle insert do a get from a 4th table to populate a description field but this get failed. I expect the first insert to rollback but it doesn't seems to be happening.
Few Points:
The 'Get' method throws a Runtime Exception
We are using org.springframework.jdbc.datasource.DataSourceTransactionManager and MySQL datasource defined in applicationContext.xml. Beans are created in Beans.xml which is imported into ApplicationContext.xml
No #Transactional annotation in DAO layer
We have used <tx:annotation-driven transaction-manager="transactionManager"/> again in applicationContext.xml
We are using Spring 3.1
UPDATE:
Code snippets....
Service Class- This is somthing similar to what I have .... I tested with and without #Autowired. The transaction enable method is called within the service class.
public class CustomerService {
//#Autowired
CustomerOrderDAO customerOrderDAOImpl;
//#Autowired
CustomerItemDAO customerItemDAOImpl;
//#Autowired
CustomerPromotionDAO customerPromotionDAOImpl;
//#Autowired
PromotionDAO promotionDAOImpl;
//other variables
public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder) {
try {
saveOrderDetails(customerOrder);
.....
return customerOrder;
} catch (Exception e) //TO-DO catch proper exception
{
//Send error response
.......
return customerOrder;
}
}
#Transactional
public void saveOrderDetails(CustomerOrder customerOrder) throws Exception {
customerOrderDAOImpl.create(customerOrder);
....
while (promotionsIterator.hasNext()) {
customerPromotion.setPromotionName(promotionDAOImpl.getName(customerOrder.getPromotionId));
customerPromotionDAOImpl.create(customerPromotion);
}
......
while (customerItemIterator.hasNext()) {
customerItemDAOImpl.create(customerItem);
}
}
}
Any idea?
Thanks.
The default behaviour of #Transactional is that transactional behaviour is added with a proxy around the object (the CustomerService in your example). From the reference docs (scroll down):
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional.
In your example, an external call to the handlingIncomingOrders() passes through the proxy and hits the target object (an instance of the CustomerService). However, the subsequent call to saveOrderDetails() is a normal method call inside the target object, thus the transactional behaviour in the proxy is never invoked. However, if the saveOrderDetails() was called from another class, you will find that the transactional behaviour will work as expected.
The solution in your case would be calling saveOrderDetails(customerOrder); as proxyBean.saveOrderDetails(customerOrder); Where proxybean is the Object on whichhandleIncomingOrders` is being called.
If CustomerService is singleton (Defualt scope) it can be as simple as adding below code to the Service class. (adding a self reference as autowired)
//#Autowired
CustomerService customerService; // As this is injected its a proxy
and in the Method use it as
public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder) {
try {
customerService.saveOrderDetails(customerOrder);
.....
return customerOrder;
} catch (Exception e) //TO-DO catch proper exception
{
//Send error response
.......
return customerOrder;
}
}
If its scope is Prototype the one of possible simple solution will be as follows.
public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder, CustomerService customerService) {
try {
customerService.saveOrderDetails(customerOrder);
.....
return customerOrder;
} catch (Exception e) //TO-DO catch proper exception
{
//Send error response
.......
return customerOrder;
}
}
And where you are calling handleIncomingOrders use changes suggested in below code.
bean.handleIncomingOrders(customerOrder); //Suppose this is old code
Change it to
bean.handleIncomingOrders(customerOrder, bean);// THough it appears as we are sending reference to `THIS` as parameter whcihc can be unnecessary, in case of `Proxy`while inside your method `this` and `Passed reference` will point to different Obejects.

CMT rollback : how to roll back the transaction

The following code does not help in roll back even if I throw null pointer exception at update() method. Everytime it inserts values into the database if I run the code. Please help me how can I roll back the transaction if null pointer is thrown at update() method. Am I missing something in the code?
#TransactionManagement(value = TransactionManagementType.CONTAINER)
public class Bean implements RemoteIF {
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertIntoDb() {
insert();
update();
}
private Integer update() {
val=0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val1 = st.executeUpdate("INSERT INTO tab VALUES('ab')");
st.close();
throw new NullPointerException();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
private Integer insert() {
int val = 0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val = st.executeUpdate("INSERT INTO tab VALUES('bnm')");
st.close();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
}
Couple things that stick out at me as suspect.
No #Stateless, #Stateful or #Singleton annotation on the Bean class. Unless you've declared the bean in an ejb-jar.xml file, then this is not getting recognized as an EJB. Definitely double check that.
The DbConn.getConnection() looks suspiciously like you might be trying to manage database connections yourself. If you have any code that uses the DriverManager or does new FooDataSource(), then that is definitely the problem. If you want transaction management to work you have to get all resources from the container via either
Injection via a #Resource DataSource datasource field in the EJB class
JNDI lookup of java:comp/env/yourDataSource, where yourDataSource is the name of a datasource you configured in the ejb-jar.xml or declared on the bean class via using #Resource(type=DataSource.class, name="youDataSource") -- that annotation goes on the class itself rather than a method or field.
See also these answers for some insight as to how transaction management works:
How does UserTransaction propagate?
Programming BMT - UserTransaction

Resources