A have a new feature that forces me to pass some new data structure to my EJBs.
This is a user session context that has some information about the current user.
How can I pass this data to my EJBs without refactoring my method signature and calls?
Use ThreadLocal? But if it is not on the same JVM?
There's some code bellow. Can you have a tip to me?
/** created at login time, when user put it's credential */
public class UserSessionContext {
private User current;
private Company company;
// getters e setters
}
/** web page controller */
#ManagedBean // at web tier
public class ListCustomersController {
#EJB
CustomersRepository customers; // some remote interface
// getters / setters
private List<Filters> filters; // from the UI
/** used in a datatable component */
public List<Customer> getAll(){
return customers.getAllWith(filters); // I have no UserSessionContext parameter
}
}
/** used by web controller */
#Stateless
public CustomersEJB implements CustomersRepository {
#Inject
UserSessionContext currentContext; // injected before call this object
public List<Customer> getAllWith(List<Filter> filters){
TypeQuery<Customer> customersQuery = ... //
customersQuery.setParameter("company",currentContext.getCompany());
return customersQuery.getResultList();
}
}
There is no standard mechanism for doing this (JSR-149). WebSphere Application Server has a Work Area Service programming model extension that can be used to propagate contexts in this manner.
Related
I'm fairly new to both Java EE and MVC. I have to develope a web application using Servlet, JSP, JB and EJB. As most applications mine too needs to interact with a RDBMS.
A friend of mine sent me a wep App he developed in which he has a
Serializable DbManager class
in which a
private transient Connection
exists as a member variable.
In all his servlets in which he needs DB access he has a DbManager variable. It is instantiate in the init method of the servlet and it is retrived like this :
this.manager = (DbManager)super.getServletContext().getAttribute("dbmanager");
All the queries are implemented as public methods of the DbManager Class.
I was wondering if this is a good way to implement such needs or if there is a better way to handle Db access and queries execution. I tought of implementing business logic and thus DB access as public methods in my EJBs.
Thanks for any help!
Homemade DbManager style classes are redundant when you are living in a JavaEE environment. You can make use of JPA for performing all your database queries from a stateless session bean that forms the "controller" part of your MVC architecture:
#Stateless
public class OrderController {
#PersistenceContext
private EntityManager em;
public void addNewOrder(Order order) {
em.persist(order)
}
public List<Order> findAllOrders() {
TypedQuery<Order> findAllOrdersQuery = em.createQuery("select o from Order o", Order.class);
return findAllOrdersQuery.list();
// In practice you would add pagination to this.
// It's not practical to return a million orders to your view.
}
...
}
This stateless EJB manages all transactions on your behalf, so you don't normally need to be concerned with beginning, committing and/or rolling back transactions.
The Order class is a component of your "model":
#Entity
public class Order {
#Id
private long id;
#Column
private String orderNumber;
#Column
private String description;
// other attributes
...
Order() { }
public Order(String orderNumber, String description) {
this.orderNumber = orderNumber;
this.description = description;
}
// setters and getters
...
// you must also override equals() and hashCode()
}
You will see many examples where developers introduce a so called DAO layer into their controller, but this is considered redundant as the EntityManager essentially satisfies that contract.
I've decided to use a request scoped container managed EntityManager and I've created a producer for that reason:
#RequestScoped
public class EntityManagerProducer {
#PersistenceContext(unitName = "PU", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#Produces
public EntityManager getEntityManager() {
return entityManager;
}
}
I've got two EJB's that expose a remote client view:
#Remote
public interface EJB1 {
void createPerson(int id, String firstName, String lastName);
}
#Remote
public interface EJB2 {
void containsEntity(Person person);
}
#Stateless
public class EJB1Impl implements EJB1 {
#Inject
private EntityManager entityManager;
#EJB
private EJB2 ejb2;
#Override
public void createPerson(final int id, final String firstName, final String lastName) {
Person person = new Person();
person.setId(id);
person.setFirstName(firstName);
person.setLastName(lastName);
entityManager.persist(person);
System.out.println("EJB1Impl: persistence context contains entity: " + entityManager.contains(person));
ejb2.containsEntity(person);
}
}
#Stateless
public class EJB2Impl implements EJB2 {
#Inject
private EntityManager entityManager;
#Override
public void containsEntity(final Person person) {
System.out.println("EJB2Impl: PersistenceContext contains entity: " + entityManager.contains(entity));
person.setLastName("new name");
//entityManager.merge(person);
}
}
The EJB's are deployed on WildFly 10. I access them by a remote client using this tutorial. The current version throws this exception: org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped. If I remove the #RequestScoped annotation from the producer, I don't get an exception but the EntityManager injected into the second EJB returns false when asked if it contains the entity and if I want the changes made to the entity (the change of the last name) to have any effect, I have to call entityManager.merge(person), which apparently means that the entity is detached. I'm sure that the second EJB executes withing the same transaction because if I inject an EJBContext and call setRollbackOnly() the transaction started in EJB1 is rolled back and a new person is not inserted into the data base.
The documentation of javax.enterprise.context.RequestScoped says that the request scope is active during any remote method invocation of any EJB, so what gives? How can I share an EntityManager during a request across multiple EJB's
I'm using Unity.MVC for DI in my ASP.NET MVC 4.6 app. I have a service interface passed into the controller and that's working great. Now I want to pass in an interface to the EF context to the service but I'm not sure how to do this. I've read EF has this IObjectContextAdapter that I could pass into my service ctor and that works, but I need to then query the actual tables on inside my service from this context but because it's an IObjectContextAdapter it doesn't know my tables. How do I do this?
public class ContactService : IContactService
{
//private ContactsEntities context;
private IObjectContextAdapter context;
// test ctor
public ContactService(IObjectContextAdapter ctx)
{
context = ctx;
}
// prod ctor
public ContactService()
{
context = new ContactsEntities();
}
List<Contact> GetAllContacts()
{
return (from c in context.ObjectContext.?? // I need to query the Contacts table that would be attached to the actual context I pass in but still keep the decoupling from using an Interface passed into the ctor
}
}
The IObjectContextAdapter is the type of ObjectContext property of DbContext.
You should subclass DbContext e.g. ContactsDatabaseContext
public class ContactsDatabaseContext : DbContext, IContactsDatabaseContext
{
// ...
}
And then just register your ContactsDatabaseContext with your IoC container. Something like this:
container.RegisterType<IContactsDatabaseContext, ContactsDatabaseContext>();
Your ContactsDatabaseContext class and IContactsDatabaseContext interface should have properties of type DbSet<T> that refer to your tables e.g.:
IDbSet<BrandDb> Users { get; set; }
UPDATE:
Since you are using a generated file, then do this:
public partial class ContactsDatabaseContext : IContactsDatabaseContext
{
// Expose the DbSets you want to use in your services
}
I have 3 services which should override the default services only if the user has a specific role.
Or even better. Inject the current user/security in the new services.
The service then performs the check for the user role and calls the original service.
I tried to inject security.context into it. But then $security->getToken() returns null.
In the controllers it works fine. How can i get the current user in my service? This is what i want to do:
class AlwaysVisibleNavigationQueryBuilder extends NavigationQueryBuilder
{
public function __construct(\Sulu\Component\Content\Compat\StructureManagerInterface $structureManager, $languageNamespace, SecurityContext $security)
{
if (in_array('ROLE_SULU_ADMINISTRATOR', $security->getToken()->getRoles())) {
// Show unpublished content, too
$this->published = false;
}
parent::__construct($structureManager, $languageNamespace);
}
}
At the moment of creation of the service, the securityContext was not aware of the current user. The Security is filles when the application runs and not on dependency-resolution.
The following Code works.
class AlwaysVisibleNavigationQueryBuilder extends NavigationQueryBuilder
{
protected $security;
public function __construct(\Sulu\Component\Content\Compat\StructureManagerInterface $structureManager, $languageNamespace, SecurityContext $security)
{
$this->security = $security;
parent::__construct($structureManager, $languageNamespace);
}
public function build($webspaceKey, $locales)
{
$roles = $this->security->getToken()->getRoles();
if (in_array('ROLE_SULU_ADMINISTRATOR', $roles)) {
// Show unpublished content, too
$this->published = false;
}
return parent::build($webspaceKey, $locales);
}
}
Thanks to Matteo!
I have a question for the article, which probably many of us have read: Dependency Injection in ASP.NET Web API 2.
Let's assume, that the ProductRepository at some later point in time needs to delegate to some other service. How should ProductRepository request the concrete instance from the dependency injector at that later time as it is a bad practice to inject the dependency injector itself into the ProductRepository?
You can inject the new service inside the ProductRepository just like you injected the IProductRepository into ProductsController.
public class ProductRepository : IDisposable
{
private readonly IOtherService m_OtherService;
public ProductRepository(IOtherService other_service)
{
m_OtherService = other_service;
}
...
}
If you register IOtherService successfully in the container, the container would be able to create ProductRepository and ProductsController successfully.
If it is a problem for you to have the OtherService created everytime (maybe you will not use it all the time), you can use the factory pattern. For example:
public interface IOtherServiceFactory
{
IOtherService Create();
}
public class ProductRepository : IDisposable
{
private readonly IOtherServiceFactory m_OtherServiceFactory;
public ProductRepository(IOtherServiceFactory other_service_factory)
{
m_OtherServiceFactory = other_service_factory;
}
...
}
Now, you can create an instance of OtherService only when you need it.
You have to create an implementation of IOtherServiceFactory and register it with the container.