I'm working on updating some legacy code for a jboss application and I've run into a bit of a jam. I have two ejb beans, PersonBean and ClientBean. ClientBean is responsible for handling client specific services. ClientBean gets injected with an instance of PersonBean which it uses as a delegate to pass along requests to server side services. The problem I'm running into is that both beans also implement a LoginService interface which also needs to get injected. What I want is for an instance of PersonBean to get injected into ClientBean as the loginService but I'm ending up with ClientBean being injected into itself.
What do I need to do to correctly define this ejb mapping?
ClientServiceProviderBean:
#Stateless(name = "ClientServiceProvider")
#Local({ ClientServiceProvider.class, LoginService.class })
public class ClientServiceProviderBean implements ClientServiceProvider, LoginService
{
#EJB(name = "personService")
protected PersonService personService;
#EJB(name = "loginService")
protected LoginService loginService;
#Override
public LoginDTO getLoggedInUser()
{
LoginDTO loginDTO = loginService.getLoggedInUser();
return loginDTO;
}
#Override
public Long activateSession(String applicationName, String ipAddress)
{
return personService.activateSession(applicationName, ipAddress);
}
}
PersonServiceBean:
#Stateless(name = "PersonService")
#Local({ PersonService.class })
#Remote({ RemotePersonService.class })
public class PersonServiceBean implements PersonService, RemotePersonService, LoginService
{
#Override
#RolesAllowed({ "authenticated" })
public Long activateSession(String applicationName, String ipAddress)
{
Person p = getCallerAsPerson(entityManager, context.getCallerPrincipal());
SessionActivity sessionActivity = new SessionActivity(p.getId(), applicationName, true, ipAddress);
sessionActivity = save(entityManager, sessionActivity);
return sessionActivity.getId();
}
#Override
#PermitAll
public LoginDTO getLoggedInUser()
{
Principal p = context.getCallerPrincipal();
if (p != null && !"unauthenticated".equals(p.getName()))
{
try
{
Person person = getCallerAsPerson(entityManager, p);
if (person != null)
{
return createLoginDTO(person);
}
}
catch (javax.persistence.NoResultException e)
{
}
}
return null;
}
}
If you want to inject PersonServiceBean as a LoginService in ClientServiceProviderBean you should add the LoginService.class in the #Local anotation of PersonServiceBean and remove it from ClientServiceProviderBean.
Related
I have a quite simple quarkus extension which defines a ContainerRequestFilter to filter authentication and add data to a custom AuthenticationContext.
Here is my code:
runtime/AuthenticationContext.java
public interface AuthenticationContext {
User getCurrentUser();
}
runtime/AuthenticationContextImpl.java
#RequestScoped
public class AuthenticationContextImpl implements AuthenticationContext {
private User user;
#Override
public User getCurrentUser() {
return user;
}
public void setCurrentUser(User user) {
this.user = user;
}
}
runtime/MyFilter.java
#ApplicationScoped
public class MyFilter implements ContainerRequestFilter {
#Inject
AuthenticationContextImpl authCtx;
#Override
public void filter(ContainerRequestContext requestContext){
// doing some stuff like retrieving the user from the request Context
// ...
authCtx.setCurrentUser(retrievedUser)
}
}
deployment/MyProcessor.java:
class MyProcessor {
//... Some stuff
#BuildStep
AdditionalBeanBuildItem createContext() {
return new AdditionalBeanBuildItem(AuthenticationContextImpl.class);
}
}
I have a Null Pointer Exception in authCtx.setCurrentUser(retrievedUser) call (authCtx is never injected)
What am I missing here ?
Thanks
Indexing the runtime module of the extension fixes the problem.
There are multiple ways to do that as mentioned in https://stackoverflow.com/a/55513723/2504224
I have a Quarkus application in which I implemented the ContainerRequestFilter interface to save a header from incoming requests:
#PreMatching
public class SecurityFilter implements ContainerRequestFilter {
private static final String HEADER_EMAIL = "HD-Email";
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String email = requestContext.getHeaders().getFirst(HEADER_EMAIL);
if (email == null) {
throw new AuthenticationFailedException("Email header is required");
}
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return () -> email;
}
#Override
public boolean isUserInRole(String role) {
return false;
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return null;
}
});
}
}
In a class annotated with ApplicationScoped I injected the context as follows:
#ApplicationScoped
public class ProjectService {
#Context
SecurityContext context;
...
}
The problem is that the context attribute is actually never injected, as it is always null.
What am I doing wrong? What should I do to be able to retrieve the SecurityContext throughout the application's code?
I like to abstract this problem, so that the business logic does not depend on JAX-RS-specific constructs. So, I create a class to describe my user, say User, and another interface, the AuthenticationContext, that holds the current user and any other authentication-related information I need, e.g.:
public interface AuthenticationContext {
User getCurrentUser();
}
I create a RequestScoped implementation of this class, that also has the relevant setter(s):
#RequestScoped
public class AuthenticationContextImpl implements AuthenticationContext {
private User user;
#Override
public User getCurrentUser() {
return user;
}
public void setCurrentUser(User user) {
this.user = user;
}
}
Now, I inject this bean and the JAX-RS SecurityContext in a filter, that knows how to create the User and set it into my application-specific AuthenticationContext:
#PreMatching
public class SecurityFilter implements ContainerRequestFilter {
#Inject AuthenticationContextImpl authCtx; // Injecting the implementation,
// not the interface!!!
#Context SecurityContext securityCtx;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User user = ...// translate the securityCtx into a User
authCtx.setCurrentUser(user);
}
}
And then, any business bean that needs the user data, injects the environment-neutral, application-specific AuthenticationContext.
#Context can only be used in JAX-RS classes - i.e. classes annotated with #Path.
In your case, ProjectService is a CDI bean, not a JAX-RS class.
The canonical way to do what you want is to inject the SecurityContext into a JAX-RS resource and then pass that as a method parameter to your ProjectService
I have an interface and two implementations of that interface.
Now on the interface I am adding '#Component' annotation. One of the implementation has a '#primary' annotation which is only getting called up.
I want to call both the implementations when I call the interface's method from the autowired interface bean.
#Component
public interface CustomerPersister {
AbuserDetails setAbuserDetails(AbuserDetails customer);
}
#Primary
#Component
public class CustomerRedisPersisterImpl implements CustomerPersister{
#Autowired
private CustomerManager customerManager;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
if(customerManager.setAbuserDetails
(customer,ATSNamespaces.ABUSERDETAILS)){
return customer;
}else{
return new AbuserDetails();
}
}
#Component
public class MongoDbRepositoryImpl implements CustomerPersister{
#Autowired
MongoTemplate mongoTemplate;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
Query query = new Query(Criteria.where("login").is(customer.getLogin()));
System.out.println("query is:"+query);
Update update = new Update();
update.set("isReturnAbuser", customer.getIsReturnAbuser());
update.set("reasonReturnAbuser", customer.getReasonReturnAbuser());
update.set("isCODThrottled", customer.getIsCODThrottled());
update.set("reasonRTOAbuser", customer.getReasonRTOAbuser());
update.set("isFakeEmail", customer.getIsFakeEmail());
update.set("reasonFakeEmail", customer.getReasonFakeEmail());
update.set("amount",customer.getAmount());
WriteResult result = mongoTemplate.upsert(query, update, AbuserDetails.class);
System.out.println("This is the class that got returned:"+result.getClass());
System.out.println("New design result:"+result);
if(result!=null){
if(result.getN() != 0)
return customer;
else
return null;
}else
return null;
}
someOtherClass
#Autowired
private CustomerPersister customerPersister;
#Override
#RequestMapping(value = "/abuser/details/set", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public AbuserDetails setAbuserDetails(#RequestBody AbuserDetails customer){
return customerPersister.setAbuserDetails(customer);
}
You can tell Spring to autowire all implementations of an interface as a List and then call the method an all implementations.
class SomeClass {
List<CustomerPersister> customerPersisters;
#Autowired
SomeClass(List<CustomerPersister> customerPersisters) {
this.customerPersisters = customerPersisters;
}
public void setAbuserDetails(#RequestBody AbuserDetails customer) {
for (CustomerPersister customerPersister: customerPersisters) {
customerPersister.setAbuserDetails(customer);
}
}
}
Of course this will not allow you to return the result of customerPersister.setAbuserDetails(), because you can't return a single value from multiple persister calls. You either have to write some code in SomeClass that will determine which object should be returned or you could return a list of the results from all persisters. Or you have to redesign your interface to match the requirements.
I have following classes:
class ServiceA{
User getUser(){} // API call to userServiceA.com
Profile getProfile(){} // API call to profileServiceA.com
}
class ServiceB{
User getUser(){} // API call to userServiceB.com
Profile getProfile(){} // API call to profileServiceB.com
}
class GroupService(){
ServiceA serviceA;
ServiceB serviceB;
constructor(){
this.serviceA = new ServiceA();
this.serviceB = new ServiceB();
}
getUser(String type){
if(type.equals("A")){
serviceA.getUser();
}else if(type.equals("B")){
serviceB.getUser();
}
}
}
class Controller(){
get(RC routingContext){
String type = routingContext.getParam("type");
GroupService groupService = new GroupService();
groupService.getUser(type);
}
}
In this project ServiceC,D,E... will be kept adding, and that will turn the GroupService class into a chaos.
For this particular scenario, which is the correct design pattern I can apply? Factory pattern is one solution, but then if else or Map will still exist.
Update: Using Strategy Pattern
public interface ServiceAZ {
User getUser();
Profile getProfile();
}
class ServiceA implements ServiceAZ{
#Override
public User getUser() {
return // API call to userServiceA.com
}
#Override
public Profile getProfile() {
return // API call to profileServiceA.com
}
}
class ServiceB implements ServiceAZ{
#Override
public User getUser() {
return // API call to userServiceB.com
}
#Override
public Profile getProfile() {
return // API call to profileServiceB.com
}
}
class GroupService(){
private ServiceAZ service;
GroupService(ServiceAZ s){
this.service = s;
}
User getUser(){
service.getUser();
}
}
class Controller(){
get(routingContext){
String type = routingContext.getParam("type");
ServiceAZ service;
if(type.equals("A")){
service = new ServiceA();
}
if(type.equals("B")){
service = new ServiceB();
}
GroupService groupService = new GroupService(service);
groupService.getUser(type);
}
}
This looks much better but now in the controller, the if else is a problem
Let's say we have interface:
public interface IAuthentication { }
and two implementations:
public class LdapAuthentication implements IAuthentication {}
public class DbAuthentication implements IAuthentication {}
And finally we have a bean that is responsible for processing authentication. This bean should use one of the implementations shown above (based on configuration specified in for example db).
#Service
public class AuthenticationService {
public boolean authenticate(...) {
boolean useDb = ...; //got from db
//my problem here
//how to get right implementation: either LdapAuthentication or DbAuthentication?
IAuthentication auth = ...;
return auth.authenticate(...);
}
}
Question:
How to get the right implementation?
If parameter value does not change:
#Service
public class AuthenticationService {
private IAuthentication auth;
#PostConstruct
protected void init() {
boolean useDb = ...; //got from db
this.auth = ...; //choose correct one
}
public boolean authenticate(...) {
return auth.authenticate(...);
}
}
If parameter is dynamic
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}