Null reference on Dagger 2 #Inject - firebase

I've created a gist highlighting the issue I'm running into. I'm using an Application Module to provide a Firebase dependency for me to inject elsewhere.
When I try to #Inject Firebase mFirebase in the data layer that dependency is never satisfied.
I'm trying to keep the Context out of my other layers, but the Firebase service depends on it. I'm interested in learning any other patterns to help keep Android classes out of my business logic.
FirebaseService.java
public class FirebaseService {
#Inject Firebase mFirebaseRef; //NEVER GET'S INJECTED!
#Override
public LoginResult signinWithEmail(final String email, final String password) {
mFirebaseRef.dostuff(); //THIS REFERENCE DOESN'T GET INJECTED!
}
}
ApplicationModule
#Provides
#Singleton
Firebase provideFirebase(#ApplicationContext Context context) {
Firebase.setAndroidContext(context);
return new Firebase(Util.FIREBASE_URL);
}
ApplicationComponent
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
#ApplicationContext Context context();
Application application();
Firebase firebase();
}
MyActivity
public class MyActivity extends AppCompatActivity {
private ActivityComponent mActivityComponent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public ActivityComponent getActivityComponent() {
if (mActivityComponent == null) {
mActivityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(MyApplication.get(this).getComponent())
.build();
}
return mActivityComponent;
}
The full code example is on github

Annotating a field with #Inject is not enough for the field injection to work. There's no magic involved, you just have to tell Dagger to do the injection.
First, add this method to your ApplicationComponent:
void inject(FirebaseService firebaseService);
Then, call this method from your FirebaseService (I guess it's an Android service, so add this to the onCreate method):
applicationComponent.inject(this);
This should do the trick. There's a great answer to a similar problem here.
EDIT
I've looked at your repository and I think you don't even need field injection in this case. You can just provide the Firebase dependency through a constructor. Here's your #Provides method:
#Provides
#Singleton
LoginService provideLoginService() {
return new FirebaseLoginService();
}
Add Firebase as a parameter to it and pass it to the FirebaseLoginService constructor:
#Provides
#Singleton
LoginService provideLoginService(Firebase firebase) {
return new FirebaseLoginService(firebase);
}
The constructor:
public FirebaseLoginService(Firebase firebase) {
this.mFirebaseRef = firebase;
}
Remove the #Inject annotation from your mFirebaseRef field since it's not needed anymore.
Here's the corresponding pull request.

Related

Valdiate pojo using #Valid in sping cloud streams

How can one enable validation using #Valid inside the following kafka consumer code ? I am using Spring Cloud Stream (Kafka Stream binder implementation), and there after my implemention is using functional model for example.
#Bean
public Consumer<KStream<String, #Valid Pojo>> process() {
return messages -> messages.foreach((k, v) -> process(v));
}
I tried the following but it didn't work....
#Bean
public DefaultMessageHandlerMethodFactory configureMessageHandlerMethodFactory(
DefaultMessageHandlerMethodFactory messageHandlerMethodFactory,
LocalValidatorFactoryBean validatorFactoryBean) {
messageHandlerMethodFactory.setValidator(validatorFactoryBean);
return messageHandlerMethodFactory;
}
This is simple in spring-kafka by implementing KafkaListenerConfigurer and setting LocalValidatorFactoryBean on KafkaListenerEndpointRegistrar
public class KafkaConfiguration implements KafkaListenerConfigurer {
#Override
public void configureKafkaListeners(KafkaListenerEndpointRegistrar registrar) {
registrar.setValidator(validatorFactoryBean);
}
.....
This is not supported in the functional model at the moment. Even for a non-functional scenario, this is non-trivial for types like KStream. The KafkaListenerConfigurer you mentioned above is for regular Kafka Support with a message channel binder. Your best options for Kafka Streams binder are either using some custom validation in the function itself before continuing with the processing or introducing a schema registry and then perform a schema validation before passing the record to the function.
You can follow the recommendation to create a bean that respects the functional interface of java, that is, it has only a public method, for example:
#Validated
#Component
public class Processor implements Consumer<KStream<String, Pojo>> {
#Override
public void accept(final #Valid #NotNull KStream<String, Pojo> stream) {
stream.foreach((k, v) -> process(v));
}
private void process(final Pojo v) {
}
}
So that generates an execution:
javax.validation.ConstraintDeclarationException: HV000151: A method
overriding another method must not reset the parameter constraint
configuration
It is not possible to overwrite the parameters of the accept method of the consumer functional interface so just remove the interface and leave the component like this:
#Validated
#Component
public class Processor {
public void accept(final #Valid #NotNull KStream<String, Pojo> stream) {
stream.foreach((k, v) -> process(v));
}
private void process(final Pojo v) {
}
}
The problem is that the spring cloud function will not recognize the bean for not extending one of the functional classes.
the workaround I got was:
#RequiredArgsConstructor
public abstract class ValidatedEventListener<T> implements Consumer<T> {
private final Validator validator;
#Override
public void accept(final T t) {
validate(t);
listen(t);
}
public abstract void listen(final T t);
public void validate(final Object event) {
var violations = validator.validate(event);
if (!violations.isEmpty()) throw new ConstraintViolationException(violations);
}
}

How to access token additionalInformation to validate expression-based access control

I succesfully added user_id additionnal information on the generated tokens on the authorization server side by implementing a TokenEnhancer. Here is a token generated:
{"access_token":"ccae1713-00d4-49c2-adbf-e699c525d53e","token_type":"bearer","expires_in":31512,"scope":"end-user","user_id":2}
Now, on the Resource server side, which is a completely separate spring project communicating through a RemoteTokenServices, i would like to use theses informations with method expression-based access control. For example i would like to use the added user_id data (it is Spring Data JPA repository for use with Spring Data Rest):
#PreAuthorize("#oauth2.hasScope('admin') or #id == authentication.principal.user_id")
#Override
UserAccount findOne (#P("id") Integer id);
The #oauth2.hasScope('admin') works as expected but the #id == authentication.principal.user_id" part obviously not.
how can i access to the additional data added to the token on expression-based access control ?
So i've found myself. The key interface is UserAuthenticationConverter.
Using the default provided DefaultUserAuthenticationConverter class, we can set a UserDetailsService which is used to set authentication.principal with the UserDetail object returned by the UserDetailsService. Without that, authentication.principal is only set with the token username as a String.
Here is an extract of my ResourceServerConfigAdapter:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration
extends ResourceServerConfigurerAdapter {
#Bean
UserDetailsService userDetailsService () {
return new UserDetailsServiceImpl();
}
#Bean
public UserAuthenticationConverter userAuthenticationConverter () {
DefaultUserAuthenticationConverter duac
= new DefaultUserAuthenticationConverter();
duac.setUserDetailsService(userDetailsService());
return duac;
}
#Bean
public AccessTokenConverter accessTokenConverter() {
DefaultAccessTokenConverter datc
= new DefaultAccessTokenConverter();
datc.setUserTokenConverter(userAuthenticationConverter());
return datc;
}
#Bean
RemoteTokenServices getRemoteTokenServices () {
RemoteTokenServices rts = new RemoteTokenServices();
rts.setCheckTokenEndpointUrl(
"http://localhost:15574/oauth/check_token");
rts.setAccessTokenConverter(accessTokenConverter());
rts.setClientId("client");
rts.setClientSecret("pass");
return rts;
}
...
}
Another method is to override the DefaultUserAuthenticationManager and provide a custom public Authentication extractAuthentication(Map<String, ?> map).
Once this is done, we can use the user data on expression-based access control like that:
#PreAuthorize("#oauth2.hasScope('admin') or #id == authentication.principal.userAccount.id")
#Override
UserAccount findOne (#P("id") Integer id);
Note that userAccount is my original DOMAIN user object. It could be everything the UserDetailsService returns.
EDIT:
To answer to Valentin Despa, here is my UserDetailsService implementation:
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserAccountRepository userAccountRepository;
public UserDetails loadUserByUsername (String username)
throws UsernameNotFoundException {
// Fetch user from repository
UserAccount ua = this.userAccountRepository
.findByEmail(username);
// If nothing throws Exception
if (ua == null) {
throw new UsernameNotFoundException(
"No user found having this username");
}
// Convert it to a UserDetails object
return new UserDetailsImpl(ua);
}
}

setPersistenceEnabled(true) crashes app

I’m creating my first Firebase App. One of its requirements is that it run when the network is not available. The Firebase guide states:
Enabling disk persistence allows our app to also keep all of its state even after an app restart. We can enable disk persistence with just one line of code.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
With disk persistence enabled, our synced data and writes will be persisted to disk across app restarts and our app should work seamlessly in offline situations.
Another requirement is to use Google Sign In. So in my MainActivity I check if the User is signed in, if not, I launch the SignInActivity. (The SignInActivity is from the Firebase examples.) The SignInActivity works, the user gets logged in, and MainActivity is launched for a second time. Now my app crashes on the code line FirebaseDatabase.getInstance().setPersistenceEnabled(true); with the following message:
Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
Now if I restart my app, the User is signed in, the SignInActivity is not launched, my app runs fine.
Any suggestions of how I avoid this crash after the User signs in?
As I was posting this question, I received a suggestion to relocate FirebaseDatabase.getInstance().setPersistenceEnabled(true);
to my “Application class”. I get exactly the same result … SignInActivity starts, completes, and I get a crash on the setPersistenceEnabled.
Below is my MainActivity onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
// Crash here upon returning from SignInActivity.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mFirebaseDbReference = FirebaseDatabase.getInstance().getReference();
// Initialize Firebase Auth
mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseUser = mFirebaseAuth.getCurrentUser();
if (mFirebaseUser == null) {
// Not signed in, launch the Sign In activity
Timber.tag("MainActivity").i("onCreate(): User not signed in, launching SignInActivity");
startActivity(new Intent(this, SignInActivity.class));
finish();
} else {
mUsername = mFirebaseUser.getDisplayName();
Timber.tag("MainActivity").i("onCreate(): User \"%s\" signed in.", mUsername);
if (mFirebaseUser.getPhotoUrl() != null) {
mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
}
}
A FirebaseApp is initialized by a ContentProvider so it is not initialized at the time onCreate() is called.
Get your FirebaseDatabase like this:
public class Utils {
private static FirebaseDatabase mDatabase;
public static FirebaseDatabase getDatabase() {
if (mDatabase == null) {
mDatabase = FirebaseDatabase.getInstance();
mDatabase.setPersistenceEnabled(true);
}
return mDatabase;
}
}
Then call Utils.getDatabase() from any activity you want.
Read more in this article
I fixed this exception by using setPersistenceEnabled(true) in my Application class.
public class MApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
}
In AndroidManifest.xml, set the application name as MApplication:
<application
android:name=".MApplication"
... />
I was facing a similar problem and using a static variable seemed to resolve the issue for me. So at first my code looked something like this
#Override
protected void onCreate(Bundle savedInstanceState) {
//..code
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase database = FirebaseDatabase.getInstance();
//..code
}
and now it looks more like
static boolean calledAlready = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
//..code
if (!calledAlready)
{
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
calledAlready = true;
}
FirebaseDatabase database = FirebaseDatabase.getInstance();
//..code
}
Hope it helps!
I bit late but today i got this issue, I solved by adding
static {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
to my Activity
To me, it is easier to be handled by creating a separate class for Firebase. This is because Firebase has its own instance and if you are using it in more than one activity, there's the possibility for it to crash if you call setPersistenceEnabled again in another activity.
Another good thing is that you can pass your context or parameters into the FirebaseHandler constructor if required. Or if you have fixed location in the database, they can be called easy without the .child("location") boilerplate.
Example:
public class FirebaseHandler {
// parameters
private Context context;
private String userKey;
private DatabaseReference databaseReference;
private static boolean isPersistenceEnabled = false;
private static String fixedLocationA = "locationA";
private static String fixedLocationB = "locationB";
public FirebaseHandler(Context context, String userKey) {
this.context = context; // context can be used to call PreferenceManager etc.
this.userKey = userKey;
if (!isPersistenceEnabled) {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
isPersistenceEnabled = true;
}
databaseReference = FirebaseDatabase.getInstance().getReference().child(userKey);
}
public DatabaseReference getRefA() {
return databaseReference.child(fixedLocationA);
}
public DatabaseReference getRefB() {
return databaseReference.child(fixedLocationB);
}
}
This can then be called in any Activity as below.
public class MyActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get instance
FirebaseHandler firebaseHandler = new FirebaseHander(this, "userKey");
// to set value
firebaseHandler.getRefA().setValue("value");
// to set listener
firebaseHandler.getRefB().addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// TODO here....
// also, can remove listener if required
if (certain condition) {
firebaseHandler.getRefB().removeEventListener(this);
}
}
}
}
}
I'm facing some problem too, but this is my temporary solution for my app.
Create BaseActivity extends AppcompatActivity and override onCreate, put setPersistenceEnabled there.
public class BaseActivity extends AppCompatActivity {
private static String TAG = "BaseActivity";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try{
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Log.d(TAG,FirebaseDatabase.getInstance().toString());
}catch (Exception e){
Log.w(TAG,"SetPresistenceEnabled:Fail"+FirebaseDatabase.getInstance().toString());
e.printStackTrace();
}
}
}
And change MainActivity to extend BaseActivity
public class MainActivity extends BaseActivity
EDIT: Follow #imakeApps answer
public class BaseActivity extends AppCompatActivity {
private static String TAG = "BaseActivity";
static boolean isInitialized = false;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try{
if(!isInitialized){
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
isInitialized = true;
}else {
Log.d(TAG,"Already Initialized");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Solved it by making the Firebase reference a static class field like this:
public class MainActivity extends AppCompatActivity
private static FirebaseDatabase fbDatabase;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(fbDatabase == null) {
fbDatabase = FirebaseDatabase.getInstance();
fbDatabase.setPersistenceEnabled(true);
}
It's no problem to create new Firebase references (without setPersistenceEnabled(true)) in other activities too.
If you don't like the static fields, this did the trick for me:
if (FirebaseApp.getApps(context).isEmpty()) {
FirebaseApp.initializeApp(context);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
This can be caused be more that one process initializing twice firebase or Multidex apps. For more info see this: https://github.com/firebase/quickstart-android/issues/15
I wouldn't recommend using Application to store the data because like its written in CodePath
There is always data and information that is needed in many places within your app. This might be a session token, the result of an expensive computation, etc. It might be tempting to use the application instance in order to avoid the overhead of passing objects between activities or keeping those in persistent storage.
However, you should never store mutable instance data inside the Application object because if you assume that your data will stay there, your application will inevitably crash at some point with a NullPointerException. The application object is not guaranteed to stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.
Thats the reason I would recommend using a Singleton like this:
public class DataBaseUtil {
private static FirebaseDatabase mDatabase;
public static FirebaseDatabase getDatabase() {
if (mDatabase == null) {
mDatabase = FirebaseDatabase.getInstance();
mDatabase.setPersistenceEnabled(true);
}
return mDatabase;
}}
just use it in your code then like
private FirebaseDatabase fdb = DataBaseUtil.getDatabase();
Create a class called Util.java
and add following code
public class Util {
private static FirebaseDatabase mData;
public static FirebaseDatabase getDatabase() {
if (mData == null) {
mData = FirebaseDatabase.getInstance();
mData.setPersistenceEnabled(true);
}
return mData;
}
}
Now replace FirebaseDatabase.getIntance() with Util.getDatabase() every time in each activity. Calling just once will get the error!
I was facing same issue. i changed code as below.
BEFORE(Causing Crash)
var rootRef = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
FIRDatabase.database().persistenceEnabled = true
}
AFTER (Resolved Crash)
var rootRef:FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
FIRDatabase.database().persistenceEnabled = true
rootRef = FIRDatabase.database().reference()
}
In Menifest
android:name=".AppName
Create java Class that extends Application
public class AppName extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
Make sure that .setpersistenceenabled(true) is not happening twice, while sign in by Google in your case, second care setPersistenceEnabled(true) is must be called before any instance of firebase called this resolve my issue.
For Kotlin Try this:
class DatabaseUtil {
companion object {
private val firebaseDatabase: FirebaseDatabase = FirebaseDatabase.getInstance()
init {
firebaseDatabase.setPersistenceEnabled(true)
}
fun getDatabase() : FirebaseDatabase {
return firebaseDatabase
}
}
}
Simply move you code in ExampleFragment.class from onCreateView method to onCreate method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
....
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
The error message describes the problem:
Calls to setPersistenceEnabled() must be made before any other usage
of FirebaseDatabase instance.
The fix to this problem is described into the documentation: As in SDK 2.x, persistence of the disk must be enabled before other calls to the database are made.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
https://firebase.google.com/support/guides/firebase-android
You can use:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
before using FirebaseDatabase.getInstance().getReference();

Cometd with Spring-MVC for personalized chatting

I am working in a Spring-MVC application and I would like to include personalized chat as a feature in it. After some research I found out Cometd to be a suitable option. After going through the documentation and forever repeating samples, I have a little bit of setup which I have done. I need some help to integrate a personalized chat service in the spring-mvc app, and enabling private chat when user pushes chat button.
So basically, I found out, "/service/chat" can be used for private chat, so I have a class for that, and to use private chat, I must have a mapping of userid<-->sessionId, but I cannot find examples anywhere how to do it. I am posting some of the code I have, kindly let me know what is remaining to do, and if possible, some resources, samples for that.
Controller code:
#Controller
#Singleton
public class MessageController {
private MessageService messageService;
#Autowired(required = true)
#Qualifier(value ="messageService")
public void setMessageService(MessageService messageService){this.messageService=messageService;}
#RequestMapping(value = "/startchatting", produces = "application/text")
#ResponseBody
public String startChattingService(){
return "OK";
}
#RequestMapping(value = "/stopchatting",produces = "application/text")
#ResponseBody
public String stopChatting(){
return "OK";
}
}
Private Message Service :
#Service
public class PrivateMessageService {
#Session
private ServerSession session;
#Listener("/service/private")
public void handlePrivateMessage(ServerSession sender, ServerMessage message){
String userId = (String) message.get("targetUserId");
//Mapping code necessary to map userids to session-id's.
//ServerSession recipient = findServerSessionFromUserId(userId);
//recipient.deliver(session,message.getChannel(),message.getData(),null);
}
}
CometConfigurer :
#Component
#Singleton
public class CometConfigurer {
private BayeuxServer bayeuxServer;
private ServerAnnotationProcessor processor;
#Inject
public void setBayeuxServer(BayeuxServer bayeuxServer){this.bayeuxServer = bayeuxServer;}
#PostConstruct
public void init() {this.processor= new ServerAnnotationProcessor(bayeuxServer);}
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
System.out.println("Configuring service " + name);
processor.processDependencies(bean);
processor.processConfigurations(bean);
processor.processCallbacks(bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
return bean;
}
public void postProcessBeforeDestruction(Object bean, String name) throws BeansException {
processor.deprocessCallbacks(bean);
}
#Bean(initMethod = "start", destroyMethod = "stop")
public BayeuxServer bayeuxServer() {
BayeuxServerImpl bean = new BayeuxServerImpl();
// bean.setOption(BayeuxServerImpl.LOG_LEVEL, "3");
return bean;
}
public void setServletContext(ServletContext servletContext) {
servletContext.setAttribute(BayeuxServer.ATTRIBUTE, bayeuxServer);
}
}
Cometd beans :
<beans:bean id="bayeuxServer" class="org.cometd.server.BayeuxServerImpl" init-method="start" destroy-method="stop"/>
I have directly included the JSP files which have cometd configuration and setup from https://github.com/fredang/cometd-spring-example, and modified them to serve my needs. Kindly let me know what else is remaining, all suggestions are welcome, I am unable to find any examples for same task on net, which are detailed, and have more code then explanation. Thank you.
Using Spring 4.x's new WebSocket feature would definitely work; moreover, this new module ships with lots of very interesting features for your use case:
STOMP protocol support
messaging abstractions
session management
pub/sub mechanisms
etc
You can check this nice chat application that demonstrates all those features.

CDI + EJB 3 + EJB Transaction

I need to audit invocations of ejb beans. Saying audit I mean write informations such as current logged user, method name, additional description to a database. I decided to do it by use of CDI decorator:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Override
public Account createAccount(Account account) {
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and the decorated class:
#Stateless
public class AccountServiceBean implements AccountService {
#Override
public Account createAccount(Account account) {
...
}
}
Now if I call AccountService from another ejb stateless bean, what will happen with transaction?:
#Stateless
public ApplicationFacadeBean implements ApplicationFacade {
#EJB
private AccountService accountService;
#Override
public Account createAccount(Account account) {
return accountService.createAccount(account);
}
}
I wanted to log transaction status in decorator (AccountServiceBeanDecorator) and decorated class (AccountServiceBean), so I injected TransactionSynchronizationRegistry as a resource in both classes:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and
#Stateless
public class AccountServiceBean implements AccountService {
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
...
}
}
I received strange behavior:
log from decorator
tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization#68fb15d0]]]
NullPointerException on second log (reg is null).
Can anybody explain it to me? Wheter AccountServiceBean class is called within the same transaction as ApplicationFacade?
Thank you
first: i would not mixing ejbs with cdi interceptors. ejbs has it on interceptor implementations.
second: interceptors are executed in the same transaction as the ejb where the interceptor is around.
possible solution:
create a correct ejb interceptor
put the interceptor around the method / class
create a second ejb (MyLoggerBean) with a method like this logToDatabase(String message) and annotate this method with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
inside the interceptor create a class member like this: #EJB private MyLoggerBean loggerBean
inside your #AroundInvoke annotated method you could call loggerBean. logToDatabase(...)
this would create a new transaction from inside the current transaction of the ejb where the interceptor is around
--> i know my english is not very good. but i hope that you understand what i think should work. if i have the time, i make e example on github...
Hmm... what container are you using? Generally I wouldn't suspect a CDI decorator to work on an EJB... I can't think of anything in the JEE spec that I've encountered that would give evidence either way.
Faced with your problem though, I did this with an interceptor, not a decorator. These are supported by the EJB spec... Anyway, here's my code, you would need to grab the variables from the context in your case:
import java.lang.reflect.Method;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
public class InvocationCountInterceptor {
#Inject
private InvocationCounter counter;
#AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Object returnValue = ctx.proceed();
Class<? extends Object> className = ctx.getTarget().getClass();
Method methodName = ctx.getMethod();
counter.incrementCounter(className, methodName);
return returnValue;
}
}
Then whatever EJB or EJB Method you want to audit, I just added this: #Interceptors(InvocationCountInterceptor.class)

Resources