Hibernate is saving a completely new entity automatically(full stack of calls) - java

I have already asked this question two times, but I'm new to stackoverflow and it seems that I don't know the rules for formatting my example code in here. Now I've decided to give the full stack of the calls and I hope I can explain the situation because everything is so strange and I can't find the words to describe it. First I will give you the source of the classes that have something to do with the problem. My actual question is in the end of the page. The large piece of code is just in case, because I don't know what could be the explanation of my problem.
Here is a service facade that gets calls from my flex application.
public class ServiceFacade implements IAuthenticationService, IProfileService, ICampaignService {
#Autowired
private IAuthenticationService authenticationService;
#Autowired
private IProfileService profileService;
#Autowired
private ICampaignService campaignService;
public void login(User user) throws AuthenticationException{
authenticationService.login(user);
}
#Override
public void logout() throws AuthenticationException {
authenticationService.logout();
}
#Override
public void sendForgottenPassword(String email) {
authenticationService.sendForgottenPassword(email);
}
#Override
public Profile getProfile(Long userId) {
return profileService.getProfile(userId);
}
#Override
public Profile updateProfile(Profile profile) {
return profileService.updateProfile(profile);
}
#Override
public Collection<String> getSocialConnectionsTypes(Long userId) {
return profileService.getSocialConnectionsTypes(userId);
}
#Override
public List<Email> findDuplicateEmails(Long profileId, List<Email> emails) {
return profileService.findDuplicateEmails(profileId, emails);
}
#Override
public Campaign getCampaign(Long campaignId) {
return campaignService.getCampaign(campaignId);
}
#Override
public Campaign updateCampaign(Campaign campaign) {
return campaignService.updateCampaign(campaign);
}
#Override
public void removeCampaign(Long campaignId) {
campaignService.removeCampaign(campaignId);
}
#Override
public void setPools(Long campaignId, Collection<Pool> pools) {
campaignService.setPools(campaignId, pools);
}
#Override
public void addPool(Long campaignId, Pool pool) {
campaignService.addPool(campaignId, pool);
}
#Override
public void removePool(Long campaignId, Pool pool) {
campaignService.removePool(campaignId, pool);
}
#Override
public List<Campaign> getCampaigns() {
return campaignService.getCampaigns();
}
#Override
public void updatePool(Long campaignId, Pool pool) {
campaignService.updatePool(campaignId, pool);
}
}
The method which is important for my question is the findDuplicateEmails method.
The profileService is implemented in the following class:
public class ProfileService implements IProfileService {
#Autowired
private IProfileManager profileManager;
#Override
public Profile getProfile(Long userId) {
return profileManager.getProfile(userId);
}
#Override
public Profile updateProfile(Profile profile){
profileManager.updateProfile(profile);
return profile;
}
#Override
public Collection<String> getSocialConnectionsTypes(Long userId) {
return profileManager.getSocialConnectionsTypes(userId);
}
#Override
public List<Email> findDuplicateEmails(Long profileId, List<Email> emails) {
return profileManager.findDuplicateEmails(profileId, emails);
}
}
Again the important method is findDuplicateEmails
The implementation of the profileManager is the following class:
public class ProfileManager implements IProfileManager {
#Autowired
private IProfileDao profileDao;
#Autowired
private ISectionManager autoCompleteManager;
#Autowired
private IUserSecurityService userSecurityService;
#Transactional
public Profile getProfile(Long userId) {
return profileDao.getProfileByUser(userId);
}
#Transactional
public void updateProfile(final Profile profile) {
List<Major> notApprovedMajors = extractNotApprovedMajors(profile);
List<Degree> notApprovedDegrees = extractNotApprovedDegrees(profile);
List<School> notApprovedSchools = extractNotApprovedSchools(profile);
List<Language> notApprovedLanguages = extractNotApprovedLanguages(profile);
List<Position> notApprovedPositions = extractNotApprovedPositions(profile);
List<Company> notApprovedCompanies = extractNotApprovedCompanies(profile);
List<Country> notApprovedCountries = extractNotApprovedCountries(profile);
List<City> notApprovedCities = extractNotApprovedCities(profile);
List<Certificate> notApprovedCertificates = extractNotApprovedCertificates(profile);
autoCompleteManager.updateAll(notApprovedMajors);
autoCompleteManager.updateAll(notApprovedDegrees);
autoCompleteManager.updateAll(notApprovedSchools);
autoCompleteManager.updateAll(notApprovedLanguages);
autoCompleteManager.updateAll(notApprovedPositions);
autoCompleteManager.updateAll(notApprovedCompanies);
autoCompleteManager.updateAll(notApprovedCountries);
autoCompleteManager.updateAll(notApprovedCities);
autoCompleteManager.updateAll(notApprovedCertificates);
profileDao.updateProfile(profile);
}
#Override
public List<Email> findDuplicateEmails(Long profileId, List<Email> emails) {
Profile persistedProfile = profileDao.findById(profileId);
if (persistedProfile.getContact() == null)
{
persistedProfile.setContact(new Contact());
}
List<Email> resultEmails = new ArrayList<Email>();
for (int i = 0; i < emails.size(); i++) {
if ((!userSecurityService.guaranteeUniquePrincipal(emails.get(i)) &&
!isPersistedInThePersistentCollection(emails.get(i), persistedProfile.getContact().getEmails())) ||
isDuplicateInTheCurrentCollection(emails.get(i), emails, i + 1)) {
resultEmails.add(emails.get(i));
}
}
return resultEmails;
}
private boolean isDuplicateInTheCurrentCollection(Email emailToCheck, List<Email> emails, int index)
{
for (int i = index ; i < emails.size(); i ++) {
if (emails.get(i).getEmailAddress().equals(emailToCheck.getEmailAddress())) {
return true;
}
}
return false;
}
private boolean isPersistedInThePersistentCollection(Email emailToCheck, Collection<Email> emails)
{
if (emails == null) {
return false;
}
for (Email persistedEmail : emails) {
if (persistedEmail.getEmailAddress().equalsIgnoreCase(emailToCheck.getEmailAddress())) {
return true;
}
}
return false;
}
}
Again the important method is the method findDuplicateEmails
Now, after this short background, here is my problem:
I am using Hibernate with spring's HibernateTemplate. I found out that in the method findDuplicateEmails, some completely new entities which come form the flex application gets saved automatically. This was very strange and during the debbugging I found out that even if I change the method findDuplicateEmails in the ProfileManager so it looks like:
#Override
public List<Email> findDuplicateEmails(Long profileId, List<Email> emails) {
Email email = new Email();
return null;
}
the entity email gets saved automatically. I also found out that if the identifier of the entity is not "email", but something else, like "newEmail", or "email1", or something, there is no problem and the entity gets persisted if and only if I make it persistent. This problem exists only in this class and finally, this problem shows up only for the Email. I mean that if I have Phone phone = new Phone(); the entity phone gets persisted only when I wish.
The flex application first checks that the entered from the user emails are unique, and then after some user interaction calls the method updateProfile() if the entered data is valid.

I would download Hibernate sources and start debugging, you will either find a bug in Hibernate (happens) or in your code, as this is one weird behavior.
This is an advice I got once, and was the fastest, most educating way to get to the root.

Related

Room query that returns List<Object> freezes application

I've tried using the below code to load some data from my database and display them in an activity:
PhoneNumberViewModel phoneNumberViewModel =
new ViewModelProvider(WorksideContactCard.this).get(PhoneNumberViewModel.class);
phoneNumberViewModel
.getPhoneNumbersById(contactID)
.observe(this,
numbers -> {
phoneNumberList = numbers;
});
To be precise, I needn't observe the data changing, as the data will be refreshed if the activity is resumed. But the above code freezes my application, although I am accessing the DB using an AsyncTask in my other class.
Why could this be so?
EDIT:
PhoneNumberViewModel.class:
public class PhoneNumberViewModel extends AndroidViewModel {
private PhoneNumberRepository phoneNumberRepository;
private LiveData<List<PhoneNumber>> allPhoneNumbers;
public PhoneNumberViewModel(#NonNull Application application) {
super(application);
phoneNumberRepository = new PhoneNumberRepository(application);
allPhoneNumbers = phoneNumberRepository.getAllPhoneNumbersLive();
}
public void insert(PhoneNumber phoneNumber) {
System.out.println("[PhoneNumberRepository] Adding new phoneNumber");
phoneNumberRepository.insert(phoneNumber);
}
public void update(PhoneNumber phoneNumber) {
phoneNumberRepository.update(phoneNumber);
}
public void delete(PhoneNumber phoneNumber) {
phoneNumberRepository.delete(phoneNumber);
}
public LiveData<List<PhoneNumber>> getAllPhoneNumbers() {
return allPhoneNumbers;
}
public LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId)
throws ExecutionException, InterruptedException {
return phoneNumberRepository.getPhoneNumbersByContactId(contactId);
}
PhoneNumberRepository.class
public class PhoneNumberRepository {
private PhoneNumberDao phoneNumberDao;
private LiveData<List<PhoneNumber>> allPhoneNumbers;
private LiveData<List<PhoneNumber>> phoneNumbersByIdList;
public PhoneNumberRepository(Application application) {
WorksideDatabase database = WorksideDatabase.getInstance(application);
phoneNumberDao = database.phoneNumberDao();
allPhoneNumbers = phoneNumberDao.getAllPhoneNumbers();
}
...
public LiveData<List<PhoneNumber>> getAllPhoneNumbersLive() {
return allPhoneNumbers;
}
public void deleteAllPhoneNumbers() {
new DeleteAllPhoneNumbersAsyncTask(phoneNumberDao).execute();
}
public LiveData<List<PhoneNumber>> getPhoneNumbersByContactId(long id)
throws ExecutionException, InterruptedException {
return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
}
private static class SelectPhoneNumberByIdAsyncTask
extends AsyncTask<Long, Void, LiveData<List<PhoneNumber>>> {
private PhoneNumberDao phoneNumberDao;
private Long ID;
private SelectPhoneNumberByIdAsyncTask(PhoneNumberDao phoneNumberDao, Long contactId) {
this.phoneNumberDao = phoneNumberDao;
ID = contactId;
}
#Override
protected LiveData<List<PhoneNumber>> doInBackground(Long... contactId) {
ID = contactId[0];
return phoneNumberDao.getPhoneNumbersById(ID);
}
}
...
}
PhoneNumberDao.class:
#Dao
public interface PhoneNumberDao {
#Insert
void insert(PhoneNumber phoneNumber);
#Update
void update(PhoneNumber phoneNumber);
#Delete
void delete(PhoneNumber phoneNumber);
// Probably uneeded method
#Query("DELETE FROM phone_numbers_table")
void deleteAllPhoneNumbers();
// Delete entry/entries by ID
#Query("DELETE FROM phone_numbers_table WHERE id = :phoneNumberId")
void deleteByPhoneNumberId(long phoneNumberId);
// Retrieve entry/entries by contact ID
// #Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
// List<PhoneNumber> getPhoneNumbersById(long contactId);
// Retrieve all saved phone numbers in LiveData format
#Query("SELECT * FROM phone_numbers_table")
LiveData<List<PhoneNumber>> getAllPhoneNumbers();
#Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId);
#Query("SELECT * FROM phone_numbers_table")
List<PhoneNumber> getAll();
}
return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
this here is your problem ,.get() call blocks the main thread
I would suggest switching to Kotlin and using Coroutines or here you can handle this using callbacks and not using AsyncTask.get() which blocks the main thread.Also BTW Async Task is going to be deprecated last I heard ,So keep that in mind too.
This is one solution the problem
public class Repository implements
SelectPhoneNumberByIdAsyncTask.OnPhoneNumberFound {
#Override
public void onPhoneNumberFound(LiveData<List<Object>> list) {
//you can get the data you want here
}
}
class SelectPhoneNumberByIdAsyncTask extends AsyncTask<Long, Void,
LiveData<List<PhoneNumber//Correct this>>> {
interface OnPhoneNumberFound{
void onPhoneNumberFound(LiveData<List<PhoneNumberDao>> list);
}
OnPhoneNumberFound mListener;
private PhoneNumberDao phoneNumberDao;
private Long ID;
private SelectPhoneNumberByIdAsyncTask(Object phoneNumberDao, Long contactId) {
this.phoneNumberDao = phoneNumberDao;
ID = contactId;
}
#Override
protected LiveData<List<Object>> doInBackground(Long... contactId) {
ID = contactId[0];
mListener.onPhoneNumberFound(phoneNumberDao.getPhoneNumbersById(ID));
return null;
}
}
use .execute() call now instead of .get()

Implementing a design pattern in a Database project

I am at my last year at the university and working on my final project with a group of friends.
I am responsible on implementing the database (using google firestore in java) and i am trying to implement it using a design pattern.
I found the adapter quiet useful, as I can create an interface called:
GenericDB, which contains all the methods the database needs to use.
A concrete class, let's call her FirestoreDB which implements it,
and an Adapter, which also implements the GenericDB, and holds an Instance Of GenericDB as a variable, so I can choose at run time which db I will want to use (maybe in the future the db would change)
Here is some basic code:
public interface GenericDB {
boolean add(String... args);
boolean delete(String... args);
boolean get(String... args);
boolean changePassword(String... args);
}
public class FirestoreDB implements GenericDB {
private final Firestore db;
public FirestoreDB() {
FirestoreOptions firestoreOptions =
FirestoreOptions.getDefaultInstance().toBuilder()
.setProjectId(Constants.PROJECT_ID)
.build();
this.db = firestoreOptions.getService();
}
public boolean add(String... args) {
return true;
}
public boolean delete(String... args) {
return false;
}
public boolean get(String... args) {
return false;
}
public boolean changePassword(String... args) {
return false;
}
}
public class Adapter implements GenericDB {
private GenericDB db;
public Adapter(GenericDB db){
this.db = db;
}
public boolean add(String... args) {
return this.db.add(args);
}
public boolean delete(String... args) {
return db.delete(args);
}
public boolean get(String... args) {
return db.get(args);
}
public boolean changePassword(String... args) {
return db.changePassword(args);
}
}
public class DatabaseCreator {
public GenericDB getDB(DATABASE database) {
switch (database) {
case FIRESTORE:
return new FirestoreDB();
default:
return null;
}
}
DatabaseCreator database = new DatabaseCreator();
GenericDB db = database.getDB(EXTRA.DATABASE.FIRESTORE);
Adapter ad = new Adapter(db);
System.out.println(ad.add("1"));
Is this a good use of the adapter pattern?
Is this a good use of the adapter pattern?
What you call Adapter, is not an Adapter. Purpose of Adapter is to convert interface of the class to another interface expected by the client. But your 'adapter' implements the same GenericDB interface as the adaptee it wraps - no conversion happens here.
The closest one to Adapter is your FirestoreDB class. It is intended to convert the interface of Firestore to the interface GenericDB required by your code. Of course there is still small chance that Google will change Firestore to implement your GenericDB interface. Until then you should use an adapter.
To make FirestoreDB a proper adapter, you should pass adaptee (Firestore object) to the constructor of adapter, and later call it in the GenericDB methods implementation:
public class FirestoreAdapter implements GenericDB {
private final Firestore db;
public FirestoreAdapter(Firestore db) { // pass adaptee to adapter
this.db = db;
}
public boolean add(...) {
// DocumentReference docRef = db.collection(colName).document(docId);
// etc
}
// etc
}
This adapter could be passed to the code which expects GenericDB interface and knows nothing about Firestore interface:
FirestoreOptions firestoreOptions =
FirestoreOptions.getDefaultInstance().toBuilder()
.setProjectId(Constants.PROJECT_ID)
.build();
Firestore firestore = firestoreOptions.getService(); // but your code requires GenericDB
GenericDB db = new FirestoreAdapter(firestore); // adapt Firestore interface
// use db here as if Firestore was implementing your GenericDB interface
You can write adapters for another type of database in the same way. But... usually, you don't use some abstract database interface, because databases are very different with different sets of features. Trying to find some intersection of features supported by all databases might be not a great idea (unless you writing CosmosDB). Usually, you will work with higher-level abstractions, like Repositories.
You should use Command Design Pattern it more flexible than Adapter in your case
Example:
import java.util.HashMap;
import java.util.Map;
// demo for firestore
class Firestore {
private Map<Object, Object> map = new HashMap<>();
public void add(Object id, Object object) {
map.put(id, object);
}
public Object get(Object id) {
return map.get(id);
}
}
interface FirestoreAware {
void setFirestore(Firestore firestore);
}
enum CommandType {
ADD,
DELETE,
GET,
CHANGE_PASSWORD,
GET_USER
}
interface Command {
CommandType getType();
}
class GetCommand implements Command {
private int id;
public GetCommand id(int id) {
this.id = id;
return this;
}
public int getId() {
return id;
}
#Override
public CommandType getType() {
return CommandType.GET;
}
}
class AddCommand implements Command {
private int id;
private String jsonData;
public AddCommand id(int id) {
this.id = id;
return this;
}
public AddCommand jsonData(String jsonData) {
this.jsonData = jsonData;
return this;
}
public int getId() {
return id;
}
public String getJsonData() {
return jsonData;
}
#Override
public CommandType getType() {
return CommandType.ADD;
}
}
interface CommandHandler<C> {
Object handle(C cmd);
}
abstract class CommandFirestoreHandler<C>
implements CommandHandler<C>, FirestoreAware {
protected Firestore firestore;
#Override
public void setFirestore(Firestore firestore) {
this.firestore = firestore;
}
}
class AddCommandHandler extends CommandFirestoreHandler<AddCommand> {
#Override
public Object handle(AddCommand cmd) {
firestore.add(cmd.getId(), cmd.getJsonData());
return Boolean.TRUE;
}
}
class GetCommandHandler extends CommandFirestoreHandler<GetCommand> {
#Override
public Object handle(GetCommand cmd) {
return firestore.get(cmd.getId());
}
}
interface GenericDB {
<T> T execute(Command cmd);
}
class FirestoreDB implements GenericDB {
private final Firestore firestore;
private final Map<CommandType, CommandHandler> handlers;
public FirestoreDB() {
this(new Firestore());
}
public FirestoreDB(Firestore firestore) {
this.firestore = firestore;
this.handlers = new HashMap<>();
// demo add default command handlers
this.addHandler(CommandType.ADD, new AddCommandHandler());
this.addHandler(CommandType.GET, new GetCommandHandler());
}
public void addHandler(CommandType commandType, CommandHandler handler) {
if(handler instanceof FirestoreAware)
((FirestoreAware)handler).setFirestore(firestore);
this.handlers.put(commandType, handler);
}
#Override
public <T> T execute(Command cmd) {
CommandHandler handler = handlers.get(cmd.getType());
return (T)handler.handle(cmd);
}
}
class DatabaseCreator {
public GenericDB getDB(String database) {
switch (database) {
case "FIRESTORE":
return new FirestoreDB();
default:
return null;
}
}
}
public class GenericDBDemo {
public static void main(String[] args) {
DatabaseCreator database = new DatabaseCreator();
GenericDB db = database.getDB("FIRESTORE");
db.execute(new AddCommand().id(1).jsonData("{'_id': 1, 'name' : 'hello world'}"));
System.out.println(db.execute(new GetCommand().id(1)).toString());
}
}

DAO pattern with realm

How can DAO be used with realm? Because when in my activity I try to set members of my model class I get an exception :
java.lang.IllegalStateException: Changing Realm data can only be done from inside a transaction.
I know that using realm.executeTransaction fixes the issue, but the code in my activity is no more database-agnostic because it will countain code that is specific to low level database communication. So later if I want to change database, the refactoring will cost a lot of time and work... Besides, I will have to handle in all my activities a reference to Realm.getDefaultInstance();
Here is sample of code of my activity
protected void onCreate(Bundle savedInstanceState)
{
mBook = mBookDaoImpl.getBookById(bookId);
}
// Later in the code
private void saveBook(String name)
{
mBook.setName(name);
}
Here is my model class
public class Book extends RealmObject
{
#Required
#PrimaryKey
private String id;
private String name;
public Book() {
}
public Book(String id, String name) {
this.id = id;
this.name = name;
}
// getter setter methods
}
Here is my DAO interface :
public interface BookDao
{
List<Book> getAllBooks();
Book getBookByIsbn(int isbn);
void saveBook(Book book);
void deleteBook(Book book);
}
And finally is my implementation :
public class BookDaoImpl implements BookDao
{
private static BookDaoImpl INSTANCE = null;
private Realm mRealm;
private BookDaoImpl()
{
mRealm = Realm.getDefaultInstance();
}
public static BookDaoImpl getInstance()
{
if (INSTANCE == null)
INSTANCE = new BookDaoImpl();
return INSTANCE;
}
#Override
public List<Book> getAllBooks()
{
return mRealm.where(Book.class).findAll();
}
#Override
public Book getBookById(String id)
{
return mRealm.where(Book.class).equalTo("id", id).findFirst();
}
#Override
public void saveBook(final Book book)
{
mRealm.executeTransaction(new Realm.Transaction()
{
#Override
public void execute(Realm realm)
{
if (book.getId() == null)
book.setId(UUID.randomUUID().toString());
realm.copyToRealmOrUpdate(book);
}
});
}
#Override
public void deleteBook(final Book book)
{
mRealm.executeTransaction(new Realm.Transaction()
{
#Override
public void execute(Realm realm)
{
mRealm.where(Counter.class).equalTo("id", book.getId())
.findFirst()
.deleteFromRealm();
}
});
}
}
Realm's getInstance() method returns a thread-local, reference counted instance which must be paired with a close() call, so your DAO implementation probably won't suit what you expect.
If you use my library Realm-Monarchy which I created specifically for making it easier to "abstract Realm away", then you can implement your DAO like this:
public class BookDaoImpl implements BookDao
{
private static BookDaoImpl INSTANCE = null;
private Monarchy monarchy;
private BookDaoImpl(Monarchy monarchy)
{
this.monarchy = monarchy;
}
public static BookDaoImpl getInstance(Monarchy monarchy)
{
if (INSTANCE == null) {
synchronized(BookDaoImpl.class) {
if(INSTANCE == null) {
INSTANCE = new BookDaoImpl(monarchy);
}
}
}
return INSTANCE;
}
#Override
public List<Book> getAllBooks()
{
return monarchy.fetchAllCopiedSync((realm) -> realm.where(Book.class));
}
#Override
public Book getBookById(final String id)
{
List<Book> books = monarchy.fetchAllCopiedSync((realm) -> realm.where(Book.class).equalTo("id", id));
if(books.isEmpty()) {
return null;
} else {
return books.get(0);
}
}
#Override
public void saveBook(final Book book)
{
monarchy.runTransactionSync((realm) -> {
if (book.getId() == null)
book.setId(UUID.randomUUID().toString());
realm.insertOrUpdate(book);
});
}
#Override
public void deleteBook(final Book book)
{
monarchy.runTransactionSync((realm) -> {
realm.where(Counter.class).equalTo("id", book.getId())
.findFirst()
.deleteFromRealm();
});
}
}
P.S.: you're throwing away a lot of power/functionality if you return List<T> synchronously, instead of an observable like LiveData<List<T>> (or originally, RealmResults<T>).

How to inject into an interceptor?

I have configured a security-interceptor which should have the user-session object (which is a singleton) injected here is what I tried:
public DependencyInjection extends AbstractModule{
//Class that has AccessLevel Annoation
bind(InterfaceA.class).to(ImplA.class);
bind(UserPersistor.class).to(UserPersistorImpl.class);
//My session that I wish to inject
bind(UserSession.class).to(UserSessionHandler.class);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AccessLevel.class),
new SecurityInterceptor(getProvider(UserSession.class)));
}
Here my UserSessionHandler:
#Singleton
public class UserSessionHandler implements UserSession {
private UserLevel userLevel = UserLevel.DEFAULT;
private final UserPersistor userPersistor;
#Inject
public UserSessionHandler(UserPersistor userPersistor) {
this.userPersistor = userPersistor;
}
#Override
public boolean loginUser(String userName, String password) {
Benutzer user = userPersistor.getUserByName(userName);
if (user == null) {
return false;
} else {
if (user.getKennwort().equals(password)) {
userLevel = UserLevel.valueOf(user.getRolleId().getBezeichnung().toUpperCase());
return true;
} else {
return false;
}
}
}
#Override
public boolean logoutUser() {
userLevel = UserLevel.DEFAULT;
return true;
}
#Override
public UserLevel getUserLevel() {
return userLevel;
}
}
Here how the SecurityInterceptor currently looks like:
#Singleton
public class SecurityInterceptor implements MethodInterceptor {
private final Provider<UserSession> session;
#Inject
public SecurityInterceptor(Provider<UserSession> session){
this.session = session;
}
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
AccessLevel[] acessRoles = invocation.getMethod().getAnnotationsByType(AccessLevel.class);
List<UserLevel> allowedRoles = new ArrayList<>();
for(AccessLevel accessRole: acessRoles){
allowedRoles.add(accessRole.value());
}
//Make sure that User has one of the allowed Access-Levels
if (!allowedRoles.contains(session.get().getUserLevel())) {
throw new InvalidAccessException("No Access allowed with userlevel" + session.get().getUserLevel());
}
return invocation.proceed();
}
}
Now I managed to get it working in my Mockito-Test with a binding to an instance like this:
bind(MockInterface.class).to(MockClass.class);
bind(UserSession.class).toInstance(user);
bind(UserPersistor.class).toInstance(mockUserPersistor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AccessLevel.class),
new SecurityInterceptor(getProvider(UserSession.class)));
However I don't want to create an instance myself but want the once guice is creating. How can I do that, or what am I currently doing wrong?
EDIT: My main issue is that the usersession seems to be different to the one that is generated. Here a simple example:
Injector injector = Guice.createInjector(new DependencyInjection());
UserSession session = injector.createInstance(UserSession.class);
InterfaceA methodCaller = injector.createInstance(InterfaceA.class);
if(session.loginUser("a","b")){
System.out.println(session.getUserLevel().toString()); //Returns Admin
}
methodCaller.callMethodWithAnnotation();
Now when I check the session.getUserLevel in the interceptor I get "Default"
EDIT2: My endgoal is to have the same session instance in my interceptor and anywhere I used UserSession

Spring boot - Service class is null

I'm dwelling with an autoWired service class which is null in a Spring Boot application.. Every object is instantiated by Spring, so I don't know why it happens.
The situation is:
I have a Rele.java class which is the following:
#Component
public class Rele {
private Pin pin;
private GpioController gpio;
private GpioPinDigitalOutput relePin;
private static final Logger logger = Logger.getLogger(Rele.class);
private Interruttore interruttore;
#Autowired AccensioneService accensioneService;
public Rele(){
}
// Costruttore
public Rele(Pin pin, Interruttore interruttore) {
this.pin = pin;
this.gpio = GpioFactory.getInstance();
this.relePin = gpio.provisionDigitalOutputPin(pin, "MyRele", PinState.LOW);
this.interruttore = interruttore;
}
public void lightOn() {
try {
if (relePin.isLow()) {
relePin.high();
updateAccensione(interruttore, true);
logger.debug("Rele acceso");
}
} catch (NullPointerException e) {
logger.debug("relepin è:" +relePin);
logger.debug("gpio è:"+gpio);
}
}
public void lightOff() {
if (relePin.isHigh()) {
relePin.low();
updateAccensione(interruttore, false);
logger.debug("Rele spento");
}
}
public void updateAccensione(Interruttore interruttore, boolean acceso) {
Date lastDateAccensione = new Date();
try {
logger.debug("accensioneService is"+accensioneService);
lastDateAccensione = accensioneService.findLastDate(interruttore);
} catch(NullPointerException npe){
logger.debug("accensioneService is: "+accensioneService);
logger.error("Error is:", npe);
lastDateAccensione = new Timestamp(lastDateAccensione.getTime());
}
Accensione accensione = new Accensione();
Date date = new Date();
logger.debug("lastDate:" + lastDateAccensione);
accensione.setDateTime(new Timestamp(date.getTime()));
accensione.setInterruttore(interruttore);
accensione.setIsLit(acceso);
accensione.setLastDateTime(lastDateAccensione);
logger.debug("Accensione è:"+accensione.toString());
accensioneService.saveAccensione(accensione);
}
public Pin getPin() {
return pin;
}
public void setPin(Pin pin) {
this.pin = pin;
}
public Interruttore getInterruttore() {
return interruttore;
}
public void setInterruttore(Interruttore interruttore) {
this.interruttore = interruttore;
}
public GpioPinDigitalOutput getRelePin() {
return relePin;
}
public void setRelePin(GpioPinDigitalOutput relePin) {
this.relePin = relePin;
}
public GpioController getGpio() {
return gpio;
}
public void setGpio(GpioController gpio) {
this.gpio = gpio;
}
}
When trying to call for updateAccensione, this is null.
Rele is created from a Controller, by this method
#RequestMapping(value="/illuminazione")
public ResponseEntity<Illuminazione> findIlluminazione(#RequestParam(value="idLuce") int idLuce,
#RequestParam(value="lit") boolean lit,
#RequestParam(value="suServer") boolean suServer) {
Illuminazione illuminazione = new Illuminazione();
Date lastDate = illuminazioneService.findLastDate(idLuce);
illuminazione.setLastDateTime(lastDate);
illuminazione.setIdLuce(idLuce);
illuminazione.setIsLit(lit);
Date date = new Date();
illuminazione.setDateTime(new Timestamp(date.getTime()));
illuminazioneService.saveIlluminazione(illuminazione);
logger.debug("Aggiornata luce " + idLuce + " accesa: "+lit);
//managing rele
if(suServer){
//check if status has changed
Luce luce = luceService.findById(idLuce);
int idInterruttore = luce.getInterruttore().getIdInterruttore();
Interruttore interruttore = interruttoreService.findById(idInterruttore);
Rele rele = releService.findByInterruttore(interruttore);
logger.debug("rele="+rele.toString());
if(lit){
rele.lightOn();
} else {
rele.lightOff();
}
}
return new ResponseEntity<Illuminazione>(illuminazione,HttpStatus.OK);
}
Rele is created, i find it in my logs.
AccensioneService is an interface, it's concrete implementation is AccensioneServiceImpl:
#Service("accensioneService")
#Transactional
public class AccensioneServiceImpl implements AccensioneService{
#Autowired AccensioneDao dao;
#Override
public void saveAccensione(Accensione accensione) {
dao.saveAccensione(accensione);
}
#Override
public Accensione findById(int id) {
return dao.findById(id);
}
#Override
public Date findLastDate(Interruttore interruttore) {
return dao.findLastDate(interruttore);
}
#Override
public boolean findLastStatus(int id) {
return dao.findLastStatus(id);
}
#Override
public void updateAccensione(Interruttore interruttore) {
}
}
I don't know if anything else is needed. AccensioneService is also called in other methods and controller, and it works... only when called inside Rele gives me this error...
Edited to add
You must be calling new Rele() or the other Rele(Pin, Interruttore ) constructor? If you are calling these in your code, the accensioneService will be null because Spring needs to create the bean, you cannot create it with its constructor if you want beans Autowired into it or for it to be Autowired. If you want it to behave like this, Spring has to know about it, so it has to be in (and come from) the Spring context.
Put a log statement in each constructor and find out who is calling them, and fix that so that instead of calling the constructor, you get the bean from Spring.
Old answer below
You need to post this method to be sure:
Rele rele = releService.findByInterruttore(interruttore);
I'll bet you are creating rele somewhere by calling new Rele(), which is not correct. You need to let Spring create it for you.
You did not post enough code to give further suggestions.
Also, you say this is null. What this are you talking about?

Categories

Resources