EclipseLink Descriptor Customizer & History Policy and JSF. How to insert user principals in history? - jsf

In my JSF web application, I use EclipseLink
Descriptor Customizer
and
History Policy
to populate a history table in database.
The corresponding JPA entity class is annotated with #Customizer(beans.HistoryLesionhCustomizer.class)
The history table has the same fields as the source table, plus two fields (start_date & end_date) to specify the start and end of operation on a row.
It is fully working. But what I need is to populate another field in the history table.
This field I called user, should be populated with the User Principals, and this will allow me to trace the user who performed the CUD (Create/Update/Delete) operation.
I thought History Policy would allow me to add a field by just indicating its corresponding name in the database and indicate the object value that must be inserted. But that is not the case, or may it be I am not able to figure how this can be done.
In other words, along with start_date and end_date, i want to populate user field with :
FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
package beans;
/**
* Whenever there is a change on a record or an insert, change will be traced.
* #author mediterran
*
*/
import javax.faces.context.FacesContext;
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.history.HistoryPolicy;
public class HistoryLesionhCustomizer implements DescriptorCustomizer {
#Override
/**
* Implementation method to use
*/
public void customize(ClassDescriptor cd) throws Exception {
String user = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
HistoryPolicy policy = new HistoryPolicy(); // Instantiates a new policy
//policy.postUpdate();
policy.useDatabaseTime(); // Use the default database time to avoid date conflict
policy.addHistoryTableName("history_lesionh"); // Indicate the source table name
policy.addStartFieldName("start_date"); // indicate the start date DB column
policy.addEndFieldName("end_date"); // Indicate the end date DB column
cd.setHistoryPolicy(policy); // Use the Policy for the entity class where used #Customizer(HistoryLesionhCustomizer.class)
}
}
Any help or workarounds would be appreciated.
Thanks

Unfortunately HistoryPolicy only adds start and end date. But you can add user information to your entity with the help of an EntityListeners . Here is an example. It will add user information to each persist/update of the customer table:
import javax.persistence.EntityListeners;
#Entity
#EntityListeners(AuditListener.class)
#Table(name = "customer")
public class Customer implements Serializable {
#Column(name = "User")
private String user;
// getter and setter
}
and the AuditListener:
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
//...
public class AuditListener {
#PrePersist
#PreUpdate
public void setUserInformation(Object entity) {
if (entity instanceof Customer) {
Customer myEntity = (Customer) entity;
myEntity.setUser(FacesContext.getCurrentInstance().getExternalContext().getRemoteUser());
}
}
}
If you have more than one column that needs user information, you can use a MappedSuperclass entity and put the user column in this class. Then let all your auditable entities extend this MappedSuperclass and check in the AuditListener if the entity is an instance of the superclass.

Related

How to add field to User (jhi_user) entity Jhipster?

I want to add new field description to User entity. I can't use yo jhipster:entity User because it is forbidden. So, what I have to do?
If we need to store more information concerning a user than what JHipster provides by default, a few tweaks are needed.
To illustrate this, let’s assume we want to store the user’s phone number.
Creating a new entity in a One to One relationship with JHI_User
The best way to add information that is not handled by the default JHipster User is by using composition in a new entity linked to it with a One to One relationship.
After this entity is created, let’s call it UserExtra, the best way to handle its id is by mapping it to the JHI_User’s one. This way, our UserExtra will have the same id as the User’s, accelerating the different requests. To achieve this, you will need to use the #MapsId annotation :
public class UserExtra implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
#Column(name = "phone")
private String phone;
#OneToOne
#MapsId
private User user;
...
}
Note that the #GeneratedValue annotation on the id needs to be removed.
Updating the register HTML page to take this change into account
Now that an entity exists to store the phone number, we need to add an input in the register form to ask for the user’s phone number.
Nothing easier than that, just update webapp/app/account/register/register.html to add an input field bound to the variable already used to store the basic information (vm.registerAccount) :
<input class="form-control" id="phone" ng-model="vm.registerAccount.phone" placeholder="global.form.phone.placeholder" />
Updating ManagedUserVM
The registerAccount() function from java/com.mycompany.myapp/web/rest/AccountResource is the one receiving the request from the registration page. Its only parameter is a ManagedUserVM object containing the information initially contained in the vm.registerAccount variable from the client.
This ManagedUserVM class located in web/rest/vm has to be updated as well so that it holds the phone number sent by the client. The only thing to do here is adding the phone number attribute and its getter :
public class ManagedUserVM extends UserDTO {
// Default attributes omitted for brevity
private String phone;
...
public String getPhone() {
return phone;
}
}
Updating the registerAccount() function from AccountResource
The registerAccount() function now receives a ManagedUserVM object that also contains the phone number of the user. The only thing left to do is saving this phone number into a new UserExtra associated with the JHipster User.
To do so, we are going to add the phone parameter to the createUser() function from UserService. But first, add this parameter where this function is called in registerAccount() :
public ResponseEntity<?> registerAccount(#Valid #RequestBody ManagedUserVM managedUserVM) {
HttpHeaders textPlainHeaders = new HttpHeaders();
textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
return userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase())
.map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
.orElseGet(() -> userRepository.findOneByEmail(managedUserVM.getEmail())
.map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
.orElseGet(() -> {
User user = userService
.createUser(managedUserVM.getLogin(), managedUserVM.getPassword(),
managedUserVM.getFirstName(), managedUserVM.getLastName(),
managedUserVM.getEmail().toLowerCase(), managedUserVM.getLangKey(),
managedUserVM.getPhone());
mailService.sendActivationEmail(user);
return new ResponseEntity<>(HttpStatus.CREATED);
})
);
}
Updating the createUser() function from UserService
Finally, we update the service layer function that saves the JHI_User to now save the UserExtra as well. Rather than updating the existing function, I suggest you create a new one with the additional parameter. This way, updating the test classes isn’t necessary.
Do not forget to inject the UserExtra repositories :
#Inject
private UserExtraRepository userExtraRepository;
#Inject
private UserExtraSearchRepository userExtraSearchRepository;
...
public User createUser(String login, String password, String firstName, String lastName, String email,
String langKey, String phone) {
User newUser = new User();
Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
Set<Authority> authorities = new HashSet<>();
String encryptedPassword = passwordEncoder.encode(password);
newUser.setLogin(login);
// new user gets initially a generated password
newUser.setPassword(encryptedPassword);
newUser.setFirstName(firstName);
newUser.setLastName(lastName);
newUser.setEmail(email);
newUser.setLangKey(langKey);
// new user is not active
newUser.setActivated(false);
// new user gets registration key
newUser.setActivationKey(RandomUtil.generateActivationKey());
authorities.add(authority);
newUser.setAuthorities(authorities);
userRepository.save(newUser);
userSearchRepository.save(newUser);
log.debug("Created Information for User: {}", newUser);
// Create and save the UserExtra entity
UserExtra newUserExtra = new UserExtra();
newUserExtra.setUser(newUser);
newUserExtra.setPhone(phone);
userExtraRepository.save(newUserExtra);
userExtraSearchRepository.save(newUserExtra);
log.debug("Created Information for UserExtra: {}", newUserExtra);
return newUser;
}
https://jhipster.github.io/tips/022_tip_registering_user_with_additional_information.html
Add column to entity User.java:
#Column(name = "description")
private String description;
Modify method toString().
In UserDTO.java add:
private String description;
Generate getter and modify method toString().
Add liquidbase diff or add column.
Now is time to modify some webapp folder.
File user-managment.state.js find state user-managment.new and to entity: function () new return ,description: null
If you want to add this field to display find and modify files:
user-managment.html
user-managment-detail.html
user-managment-dialog.html
For example: <th><span data-translate="some.some.user.description">Description</span></th>

OpenXava populating Collection from database

I have a problem populating a detail view from the database.
application.xml snippet:
<module name="ModuleB">
<model name="B"/>
<view name="ViewB"/>
...
<mode-controller name="DetailOnly"/>
</module>
I have three entity classes:
#Entity
#Table(name="A")
class A {
#OneToMany(mappedBy = "a")
#ListProperties("col1")
#CollectionView("cs")
private Collection<C> cs;//+getter/setters
}
#Entity
#Table(name="A")
#View(name = "ViewB", ...)
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<C> cs;//+getter/setters
}
#Entity
#Table(name="C")
#View(name = "ViewC", ...)
class C {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
}
I want to read an existing B instance from the database by clicking on a link, then editing/modify it.
When I set the model object of the view with getView().setModel() or even using getView().findObject(), on the screen everything looks good, the collection shows its proper content.
On the other hand when i try to save it back, in the save action the getView().getEntity().getCs() collection is null.
What do I have to do to make the view being correspond to the entity behind?
I am using OpenXava 5.0.1, java 1.7
Important note: I am not allowed to change OpenXava version, since it is a legacy project.
My editing (20170126)
I've made a new class to avoid a reference problem:
#Entity
#Table(name="C")
#View(name = "ViewC", ...)
class D {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private B a;
}
and modified the class B accordingly:
#Entity
#Table(name="A")
#View(name = "ViewB", ...)
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<D> cs;//+getter/setters
}
But the result is the same: The records of the subview (Collection ViewC) are loaded from the DB,
and shown on the screen correctly, but I get an error message if I want to edit the Collection ViewC
( eg: add a new entry ):
"Impossible to execute Save action: Impossible to create: an object with that key already exists"
+ As before: in the save action the getView().getEntity().getCs() collection is null
OpenXava uses JPQL to obtain collection data instead of just calling the collection getter, this is for allowing filtering and ordering. The problem here is that the query sentence generated by OpenXava gets the data but the getter of the JPA entity not. Surely JPA/Hibernate is not very happy with a mappedyBy to a reference to another type. That is you write:
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<C> cs;//+getter/setters
}
Where a is a reference to A, not to B.
Given that A and B map the same table, why not to use just one entity?

Need a new method in CatalogEndpoint: findAllProductsForCategory

I have REST API working, but I need a way to return all products within a certain category. Right now I'm using findSearchResultsByCategoryAndQuery by passing the category id and using a very generic query to get almost all the products in the category. What I really need is a method called findAllProductsForCategory which returns all products in the category.
I'm new to Broadleaf and REST API, can someone please guide me through how to extend CatalogEndpoint to get the functionality I need.
Althought broadleaf provides SQL injection protection (ExploitProtectionServiceImpl) I recommend you to use ProductDao.
Extend org.broadleafcommerce.core.web.api.endpoint.catalog.CatalogEndpoint or add in your implementation new method that utilize ProductDao
#Resource("blProductDao")
private ProductDao productDao;
#RequestMapping(value = "search/products-by-category/{categoryId}") //GET is by default
public List<Product> findSearchResultsByCategory(HttpServletRequest request, #PathVariable("categoryId") Long categoryId {
return productDao.readProductsByCategory(categoryId);
}
It's querying database with:
SELECT categoryProduct.product_id
FROM BLC_CATEGORY_PRODUCT_XREF categoryProduct
WHERE categoryProduct.category_id = :categoryId
ORDER BY COALESCE (categoryProduct.display_order,999999)
Or if you want to create your own dao
public class MyProductDaoImpl extends ProductDaoImpl implements MyProductDao {
public static final String QUERY = "SELECT categoryProduct.product_id " +
"FROM BLC_CATEGORY_PRODUCT_XREF categoryProduct " +
"WHERE categoryProduct.category_id = :categoryId";
#Override
public List<Product> meFindingProductsByCategory(String categoryId) {
Query query = em.createQuery(QUERY);
query.setParameter("categoryId", categoryId);
return query.getResultList();
}
}
You can choose if you are producing JSON or XML. Be sure that you have coresponding Product model for binding results

ServiceStack OrmLite - Handle Auto increment column default seed

How can i set a auto increment column seed from 1 to 100??
in sql server can do this use
ADD Id INT NOT NULL IDENTITY(1000,1)
but in ormlite autoincrement attribute seems like always start with 1.
tried also
db.AlterTable<MSchool>("command") // DROP ID AND ADD AUTO INCREMENT COLUMN
it works, if Id doesn't related to any table.
can i set a column autoincrement with default seed and increment?
[AutoIncrement(1000,1)]
public int Id {get;set;}
UPDATE
Resolved, but not good
public class School
{
[AutoIncrement]
public int Id {get;set;}
}
//then create table
db.CreateTable<School>();
//then update seed
db.ExecuteSql("DBCC CHECKIDENT ('School',reseed,1000)");
OR
[PostCreateTable("DBCC CHECKIDENT ('School',reseed,1000)")]
public class School : BaseModel
{
[AutoIncrement]
public int Id {get;set;}
}
Is there no easier way to do this??
Personally I don't believe this behavior belongs inside source code and would just modify the database out-of-band.
But if I were to add it in source code I'd do something like:
if (db.CreateTableIfNotExists<School>())
{
db.ExecuteSql("DBCC CHECKIDENT ('School',reseed,1000)");
}
So it only resets the seed if the table doesn't exist.
Another option is attach it to the model at runtime so it's decoupled from the class definition with:
typeof(School)
.AddAttributes(new PostCreateTableAttribute(
"DBCC CHECKIDENT ('School',reseed,1000)"));

TableView with different objects (javafx)

Im currently developing a application for watching who is responsible for different Patients, however i havent been able to solve how to fill a table with different object types.
Below is my code for my TableView controller. The TableView will end up with four different object typs, all will be retrieved from a database.
I want my table to hold Patient objects, User objects (responsible) and a RelationManager object.
Below is my code, if you need more of the code, please let me know :-).
package fird.presentation;
import fird.Patient;
import fird.RelationManager;
import fird.User;
import fird.data.DAOFactory;
import fird.data.DataDAO;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
/**
* FXML Controller class
*
* #author SimonKragh
*/
public class KMAMainFrameOverviewController implements Initializable {
#FXML
private TextField txtCPRKMAMainFrame;
#FXML
private TableColumn<Patient, String> TableColumnCPR;
#FXML
private TableColumn<Patient, String> TableColumnFirstname;
#FXML
private TableColumn<Patient, String> TableColumnSurname;
#FXML
private TableColumn<User, String> TableColumnResponsible;
#FXML
private TableColumn<RelationManager, String> TableColumnLastEdited;
#FXML
private TableView<RelationManager> tblPatients;
#FXML
private Button btnShowHistory;
#FXML
private TableColumn<?, ?> TableColumnDepartment;
/**
* Initializes the controller clas #FXML private Button btnShowHistory;
*
* #FXML private TableColumn<?, ?> TableColumnDepartment; s.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// Start of logic for the KMAMainFrameOverviewController
DataDAO dao = DAOFactory.getDataDao();
TableColumnCPR.setCellValueFactory(new PropertyValueFactory<Patient, String>("CPR"));
TableColumnFirstname.setCellValueFactory(new PropertyValueFactory<Patient, String>("Firstname"));
TableColumnSurname.setCellValueFactory(new PropertyValueFactory<Patient, String>("Surname"));
TableColumnResponsible.setCellValueFactory(new PropertyValueFactory<User, String>("Responsible"));
TableColumnLastEdited.setCellValueFactory(new PropertyValueFactory<RelationManager, String>("Last Edited"));
ObservableList<RelationManager> relationData = FXCollections.observableArrayList(dao.getAllActiveRelations());
tblPatients.setItems(relationData);
tblPatients.getColumns().addAll(TableColumnCPR, TableColumnFirstname, TableColumnSurname, TableColumnResponsible, TableColumnLastEdited);
System.out.println(tblPatients.getItems().toString());
}
}
relationData is a RelationManager object returned. This object contains a User object, a Patient object and a Responsible object.
Best,
Simon.
The exact details of how you do this depend on your requirements: for example, for a given RelationManager object, do the User, Patient, or Responsible objects associated with it ever change? Do you need the table to be editable?
But the basic idea is that each row in the table represents some RelationManager, so the table type is TableView<RelationManager>. Each column displays a value of some type (call it S), so each column is of type TableColumn<RelationManager, S>, where S might vary from one column to the next.
The cell value factory is an object that specifies how to get from the RelationManager object to an observable value of type S. The exact way you do this depends on how your model classes are set up.
If the individual objects associated with a given RelationManager never change (e.g. the Patient for a given RelationManager is always the same), then it's pretty straightforward. Assuming you have the usual setup for Patient:
public class Patient {
private StringProperty firstName = new SimpleStringProperty(...);
public StringProperty firstNameProperty() {
return firstName ;
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
// etc etc
}
then you can just do
TableColumn<RelationManager, String> firstNameColumn = new TableColumn<>("First Name");
firstNameColumn.setCellValueFactory(new Callback<CellDataFeatures<RelationManager,String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<RelationManager, String> data) {
return data.getValue() // the RelationManager
.getPatient().firstNameProperty();
}
});
If you are not using JavaFX properties, you can use the same fallback that the PropertyValueFactory uses, i.e.:
TableColumn<RelationManager, String> firstNameColumn = new TableColumn<>("First Name");
firstNameColumn.setCellValueFactory(new Callback<CellDataFeatures<RelationManager,String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<RelationManager, String> data) {
return new ReadOnlyStringWrapper(data.getValue().getPatient().getFirstName());
}
});
but note that this won't update if you change the name of the patient externally to the table.
However, none of this will work if the patient object associated with the relation manager is changed (the cell will still be observing the wrong firstNameProperty()). In that case you need an observable value that changes when either the "intermediate" patient property or the firstNameProperty change. JavaFX has a Bindings API with some select(...) methods that can do this: unfortunately in JavaFX 8 they spew out enormous amounts of warnings to the console if any of the objects along the way are null, which they will be in a TableView context. In this case I would recommend looking at the EasyBind framework, which will allow you to do something like
firstNameColumn.setCellValueFactory( data ->
EasyBind.select(data.getValue().patientProperty())
.selectObject(Patient::firstNameProperty));
(EasyBind requires JavaFX 8, so you if you get to use it, you also get to use lambda expressions and method references :).)
In either case, if you want the table to be editable, there's a little extra work to do for the editable cells in terms of wiring editing commits back to the appropriate call to set a property.

Resources