Is reflection my best option here? - java

We allow users to set some preferences in our web application. When they login to certain sites, they are allowed to do/see different screens vs. when they are in another site. One of our requirements is that we hide preferences on the page which they can't access in the currently logged in site. Currently we are using Spring MVC to handle these requests.
Here is a simplified example of our POJO:
public class Preferences {
boolean prefA; //Valid when can do A
String prefB; //Valid when can do B
Integer prefC; //Valid when can do C
....
Long prefZ; //Valid when can do Z
}
Here is the controller code:
#RequestMapping(method = RequestMethod.POST, value = "preferences.xhtml")
public ModelAndView updateRequestPreferences(#ModelAttribute(USER_PREFERENCES) final Preference preferences, final BindingResult results)
{
return updatePreferences(preferences, results);
}
Currently updatePreferences does reflection, and ensures that the value is not null before it persists the input preferences -- This is due to Spring MVC creating a new instance of Preferences and then populating the values with what was on the UI.
We could do the following in the setter of the preferences:
public void setPreferences(Preferences preferences) {
if (preferences.getPrefA() != null) {
this.preferences.setPrefA(preferences.getPrefA());
}
if (preferences.getPrefB() != null) {
this.preferences.setPrefB(preferences.getPrefB());
}
...
if (preferences.getPrefZ() != null) {
this.preferences.setPrefZ(preferences.getPrefZ());
}
}
It would get unwieldy even with a helper function for all the checks in the setter method (and seems like an easy step to forget when a new preference is created); at the same time reflection seems like a cop out. Is there a better way to refactor this?

You can configure Spring MVC to populate fields of a preexisting object rather than to create a new one.
For example, you can use #SessionAttributes(USER_PREFERENCES) in order to store an instance of Preferences in a session between rendering a form and processing its submit.
Another approach is to load new instance of Preferences from the database as an implicit model attribute:
#ModelAttribute(USER_PREFERENCES)
public Preferences loadPreferences(#RequestParam("key") String key) {
return loadPreferencesByKey(key);
}
Note that in all cases (and your original approach needs it as well) you need to specify a set of allowed fields in #InitBinder method, to prevent malicious user from modifying fields that was not rendered in a form:
#InitBinder(USER_PREFERENCES)
public void initBinder(WebDataBinder b) {
b.setAllowedFields("prefA", "prefB");
}

Related

LazyList is not defined in ZUL Page

I'm trying to pass data from DAO with flexbile search query trough to the zul page using de the widgetModel. But when I print the widgetModel.orders it says...
(index):93 Uncaught ReferenceError: LazyList is not defined
at window.onload ((index):93)
zul page
window.onload = function () {
const myChart = new Chart(
document.getElementById('myChart'),
config
);
const test = [[${widgetModel.orders}]];
console.log(test);
};
controller class
public class customGraphController extends DefaultWidgetController {
private static final long serialVersionUID = 7954736389190109887L;
#WireVariable
private transient customGraphService customGraphService;
#Override
public void preInitialize(Component comp) {
super.preInitialize(comp);
WidgetModel model = getWidgetInstanceManager().getModel();
model.put("orders", customGraphService.getAllOrders());
}
}
service class
public class customGraphService {
#Autowired
private OrdersDataDao ordersDataDao;
public List<OrderModel> getAllOrders() {
return ordersDataDao.getAllOrders();
}
}
dao class
public class OrdersDataDao {
#Resource
private FlexibleSearchService flexibleSearchService;
public List<OrderModel> getAllOrders() {
final String stringQuery = "select {o.pk} from {order as o}";
final FlexibleSearchQuery query = new FlexibleSearchQuery(stringQuery);
final SearchResult<OrderModel> result = flexibleSearchService.search(query);
if (null != result.getResult()) {
return result.getResult();
} else {
return null;
}
}
}
Does someone knows a solution?
window.onLoad is always too early to manipulate ZK widgets. The onLoad callback will trigger once the initial document has been loaded, but that document itself will load ZK libaries, initialize the client engine, etc.
If you need to do something "after libraries have been loaded", you can use zk.afterload. This hook is good if you need to modify a framework function before the widgets actually use it.
However, this is still before widget instantiation by the client engine, so if your goal is to access a widget, it is still too early.
If you want to do something to a widget after that widget has been added to the page an initialized, what you actually need is a client-side onBind listener.
You can set that listener in zul or in java, but the simplest way to do that is like this: https://zkfiddle.org/sample/1v9phuk/1-Another-new-ZK-fiddle
Lastly, if you are going to use ZK's client-side API to access / modify content at client-side, I'd recommend looking into the ZK client-side selectors. Way easier than trying to manually lookup elements by ID, and way more robust in the long run.
document.getElementById('myChart') will only work if you have a dom element with the actual ID "myChart", which is not how ZK works (not with default UUID generator anyway).
Instead, you can select a widget by it's ZK ID.
Assuming you have <charts id="myChart" /> in your zul, you can get the ZK widget directly as argument to the onBind listener, or you can get it with the ZK selector:
zk.$("$myChart"), and from there you can get the DOM node: zk.$("$myChart").$n()
Make sure you know what is client-side (JavaScript), and what is Server side.
(Lastly)^2, keep in mind that in a ZK architecture, the server is the source of the state, and the client only update the state by sending client commands back to the server.
If you use JS to modify the client-side state, you can create desynchronization between the server-side state and the client-side state, so proceed with caution.
I found a solution, I used GSON to stringify the list and than pass it to the zul page :-)
private Object convertAllOrderModelsToJSON() {
//get all the models
List list = customGraphService.getAllOrdersModels();
//convert models to JSON
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(list);
return json;
}

How do I pass current logged in user information across my application

I'm new to Programming and Java and building a small Application in JAVA with SQL and UI(Jframes)
In the log-in frame after the user enters his user name and password I do a SQL select query to search USER table for this user. If the query returns 1 single row, the login button event handler triggers and the object of the next frame is created.
few frames later I have an SQL insert activity where I also have to insert the USER_ID of the person initiating the insert(the current logged in user).
What is the best practice to pass this information across a series of frame classes?
My initial idea is to pass the user_id as a parameter in the object so it gets set in the constructor of each frame class. but the problem is not all my frames really need this data. Only the final frame involved with the insertion needs the user ID. but in-order to get to that frame I have to pass through all other frames.
This doesn't seem like a good practice.
First Approach
The first possibility I thought of was to use java.util.Properties to store the application state inside of a properties file. The approach would be to use a singleton wrapper, let's say ApplicationState for reading/writing to properties file. This singleton class would be reachable from all frames, alternatively an instance of this class could be passed inside of constructor.
public class ApplicationState {
private static ApplicationState instance = new ApplicationState();
private ApplicationState() { }
public static ApplicationState getInstance( ) { return instance; }
public String read(String key) throws IOException {
try (InputStream input = new FileInputStream("path/to/config.properties")) {
Properties prop = new Properties();
prop.load(input);
return prop.getProperty(key);
}
}
...
}
Second Approach
Then I realized that a much cleaner solution would be to use the java.util.prefs.Preferences API. See Preferences API for details.
class A {
public void login() {
Preferences prefs = Preferences.userNodeForPackage(com.mycompany.MyClass.class);
prefs.putInt("userId", 11);
...
}
}
class B {
public void insert() {
Preferences prefs = Preferences.userNodeForPackage(com.mycompany.MyClass.class);
int userId = prefs.getInt("userId", 0); // 0 is default identifier
...
}
}
In addition because you want to store sensitive information, encrypted storage would be useful. For using encrypted preferences see this article.

Play framework (Java) model validation - different check for create() and update()

I'm using Play 2.3.7 and I basically have the same question as this but I am using Java and I don't know Scala.
In my case I have a CSVData class with a name attribute that I want to be unique. In my controller actions for both create() and update(Long id) I bind a form to this class with
Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();
As explained in the Play documentation, the validate() function gets called on bindFromRequest(). Here's my current validate function:
public List<ValidationError> validate() {
List<ValidationError> errors = new ArrayList<ValidationError>();
// Unique validation on name
if(CSVData.find.where().eq("name", getName()).findRowCount() != 0) {
errors.add(new ValidationError("name", "Name must be unique. That value is already taken."));
}
// other stuff
}
Clearly the issue with my current solution is the fact that when update() gets called, there already is a row in the DB with that name. This answer suggests checking the id attribute of the object in the controller action, and then provides a Scala code snippet, but that example contained a User class, and my application does not. How can I can check if the object in the database with the matching name is the same one that I am currently updating? Should I check for this in the validate() function or in my controller?
Create two separate classes one for insert() - InsertCsvData and one for updated() - UpdateCsvData with two different validate() implementations. Optionally, create base class for storing common properties/behaviours.
It's not possible to perform different validations for create() and update() in the validate() function in the model, but you can perform the validations in the respective controller actions, like so
public static Result create() {
Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();
}
public static Result update() {
Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();
if( !formData.hasErrors() ){
if(formData.field("name").value().equals( .... ) {
// do some check here
// add ValidationError if needed
}
}
}
I had not considered performing the validations directly in the controller before, but this is possible.

check if a form field has changed using a play Form object

Is there any way to check directly, if the content of a form field in play framework has changed?
for example my Device.java is something like this:
class Device{
String name;
String type;}
and then somewhere in my controller, I have a form of type Device. is there any way to check using boundForm if the value of the name property has changed?
public class Devices extends Controller {
private static final Form<Device> deviceForm = Form.form(Device.class);
public static Result details(Device device) {
if (device == null) {
return notFound(String.format("Device does not exist. "));
}
Form<Device> filledForm = deviceForm.fill(device);
return ok(views.html.devices.details.render(filledForm));
}
public static Result save() {
Form<Device> boundForm = deviceForm.bindFromRequest();
...
[here]
...
}
}
note: details method will show the user the filled form, user may or may not change the values, and then by pressing a Save button , the save() method will be called.
In shortest words Form<T> isn't able to check if fields are changed it's just stateless between request and to check it you just need to get record from DB and compare field, by field.
Also you shouldn't rely on client-side validation as it's mainly for cosmetic, NOT for safety. Remember that it can be manipulated or omitted quite easy with common webdev tools.
Finally you shouldn't resign from Form validation possibilities,as it's very handy tool, instead you can cooperate with it, i.e. it can be something like:
public static Result save() {
Form<Device> boundForm = deviceForm.bindFromRequest();
if (boundForm.hasErrors()){
return badRequest(devices.details.render(boundForm));
}
Device boundDevice = boundForm.get();
Device existingDevice = Device.find.byId(boundDevice.id);
if (boundDevice.name.equals(existingDevice.name)){
boundForm.reject("Contents are identical");
return badRequest(devices.details.render(boundForm));
}
// else... form hasn't errors, name changed - should be updated...
boundDevice.update(boundDevice.id);
}
So you can display it in your view i.e.:
#if(yourForm.error("identicalContent")!=null) {
<div class="alert alert-danger">#yourForm.error("identicalContent").message</div>
}
As you can see from this sample - if you want just to skip UPDATE query if no changes - to save resources - it does not make sense, as you need to make SELECT query anyway to compare. In other cases (like i.e. additional logging ONLY if changed) above snippet is correct solution.

Spring Partial Update Object Data Binding

We are trying to implement a special partial update function in Spring 3.2. We are using Spring for the backend and have a simple Javascript frontend. I've not been able to find a straight-forward solution to our requirements, which is The update() function should take in any number of field:values and update the persistence model accordingly.
We have in-line editing for all of our fields, so that when the user edits a field and confirms, an id and the modified field get passed to the controller as json. The controller should be able to take in any number of fields from the client (1 to n) and update only those fields.
e.g., when a user with id==1 edits his displayName, the data posted to the server looks like this:
{"id":"1", "displayName":"jim"}
Currently, we have an incomplete solution in the UserController as outlined below:
#RequestMapping(value = "/{id}", method = RequestMethod.POST )
public #ResponseBody ResponseEntity<User> update(#RequestBody User updateUser) {
dbUser = userRepository.findOne(updateUser.getId());
customObjectMerger(updateUser, dbUser);
userRepository.saveAndFlush(updateUuser);
...
}
The code here works, but has some issues: The #RequestBody creates a new updateUser, fills in the id and the displayName. CustomObjectMerger merges this updateUser with the corresponding dbUser from the database, updating the only fields included in updateUser.
The problem is that Spring populates some fields in updateUser with default values and other auto-generated field values, which, upon merging, overwrites valid data that we have in dbUser. Explicitly declaring that it should ignore these fields is not an option, as we want our update to be able to set these fields as well.
I am looking into some way to have Spring automatically merge ONLY the information explicitly sent into the update() function into the dbUser (without resetting default/auto field values). Is there any simple way to do this?
Update: I've already considered the following option which does almost what I'm asking for, but not quite. The problem is that it takes update data in as #RequestParam and (AFAIK) doesn't do JSON strings:
//load the existing user into the model for injecting into the update function
#ModelAttribute("user")
public User addUser(#RequestParam(required=false) Long id){
if (id != null) return userRepository.findOne(id);
return null;
}
....
//method declaration for using #MethodAttribute to pre-populate the template object
#RequestMapping(value = "/{id}", method = RequestMethod.POST )
public #ResponseBody ResponseEntity<User> update(#ModelAttribute("user") User updateUser){
....
}
I've considered re-writing my customObjectMerger() to work more appropriately with JSON, counting and having it take into consideration only the fields coming in from HttpServletRequest. but even having to use a customObjectMerger() in the first place feels hacky when spring provides almost exactly what I am looking, minus the lacking JSON functionality. If anyone knows of how to get Spring to do this, I'd greatly appreciate it!
I've just run into this same problem. My current solution looks like this. I haven't done much testing yet, but upon initial inspection it looks to be working fairly well.
#Autowired ObjectMapper objectMapper;
#Autowired UserRepository userRepository;
#RequestMapping(value = "/{id}", method = RequestMethod.POST )
public #ResponseBody ResponseEntity<User> update(#PathVariable Long id, HttpServletRequest request) throws IOException
{
User user = userRepository.findOne(id);
User updatedUser = objectMapper.readerForUpdating(user).readValue(request.getReader());
userRepository.saveAndFlush(updatedUser);
return new ResponseEntity<>(updatedUser, HttpStatus.ACCEPTED);
}
The ObjectMapper is a bean of type org.codehaus.jackson.map.ObjectMapper.
Hope this helps someone,
Edit:
Have run into issues with child objects. If a child object receives a property to partially update it will create a fresh object, update that property, and set it. This erases all the other properties on that object. I'll update if I come across a clean solution.
We are using #ModelAttribute to achive what you want to do.
Create a method annotated with#modelattribute which loads a user based on a pathvariable throguh a repository.
create a method #Requestmapping with a param #modelattribute
The point here is that the #modelattribute method is the initializer for the model. Then spring merges the request with this model since we declare it in the #requestmapping method.
This gives you partial update functionality.
Some , or even alot? ;) would argue that this is bad practice anyway since we use our DAOs directly in the controller and do not do this merge in a dedicated service layer. But currently we did not ran into issues because of this aproach.
I build an API that merge view objects with entities before call persiste or merge or update.
It's a first version but I think It's a start.
Just use the annotation UIAttribute in your POJO`S fields then use:
MergerProcessor.merge(pojoUi, pojoDb);
It works with native Attributes and Collection.
git: https://github.com/nfrpaiva/ui-merge
Following approach could be used.
For this scenario, PATCH method would be more appropriate since the entity will be partially updated.
In controller method, take the request body as string.
Convert that String to JSONObject. Then iterate over the keys and update matching variable with the incoming data.
import org.json.JSONObject;
#RequestMapping(value = "/{id}", method = RequestMethod.PATCH )
public ResponseEntity<?> updateUserPartially(#RequestBody String rawJson, #PathVariable long id){
dbUser = userRepository.findOne(id);
JSONObject json = new JSONObject(rawJson);
Iterator<String> it = json.keySet().iterator();
while(it.hasNext()){
String key = it.next();
switch(key){
case "displayName":
dbUser.setDisplayName(json.get(key));
break;
case "....":
....
}
}
userRepository.save(dbUser);
...
}
Downside of this approach is, you have to manually validate the incoming values.
I've a customized and dirty solution employs java.lang.reflect package. My solution worked well for 3 years with no problem.
My method takes 2 arguments, objectFromRequest and objectFromDatabase both have the type Object.
The code simply does:
if(objectFromRequest.getMyValue() == null){
objectFromDatabase.setMyValue(objectFromDatabase.getMyValue); //change nothing
} else {
objectFromDatabase.setMyValue(objectFromRequest.getMyValue); //set the new value
}
A "null" value in a field from request means "don't change it!".
-1 value for a reference column which have name ending with "Id" means "Set it to null".
You can also add many custom modifications for your different scenarios.
public static void partialUpdateFields(Object objectFromRequest, Object objectFromDatabase) {
try {
Method[] methods = objectFromRequest.getClass().getDeclaredMethods();
for (Method method : methods) {
Object newValue = null;
Object oldValue = null;
Method setter = null;
Class valueClass = null;
String methodName = method.getName();
if (methodName.startsWith("get") || methodName.startsWith("is")) {
newValue = method.invoke(objectFromRequest, null);
oldValue = method.invoke(objectFromDatabase, null);
if (newValue != null) {
valueClass = newValue.getClass();
} else if (oldValue != null) {
valueClass = oldValue.getClass();
} else {
continue;
}
if (valueClass == Timestamp.class) {
valueClass = Date.class;
}
if (methodName.startsWith("get")) {
setter = objectFromRequest.getClass().getDeclaredMethod(methodName.replace("get", "set"),
valueClass);
} else {
setter = objectFromRequest.getClass().getDeclaredMethod(methodName.replace("is", "set"),
valueClass);
}
if (newValue == null) {
newValue = oldValue;
}
if (methodName.endsWith("Id")
&& (valueClass == Number.class || valueClass == Integer.class || valueClass == Long.class)
&& newValue.equals(-1)) {
setter.invoke(objectFromDatabase, new Object[] { null });
} else if (methodName.endsWith("Date") && valueClass == Date.class
&& ((Date) newValue).getTime() == 0l) {
setter.invoke(objectFromDatabase, new Object[] { null });
}
else {
setter.invoke(objectFromDatabase, newValue);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
In my DAO class, simcardToUpdate comes from http request:
simcardUpdated = (Simcard) session.get(Simcard.class, simcardToUpdate.getId());
MyUtil.partialUpdateFields(simcardToUpdate, simcardUpdated);
updatedEntities = Integer.parseInt(session.save(simcardUpdated).toString());
The main problem lies in your following code:
#RequestMapping(value = "/{id}", method = RequestMethod.POST )
public #ResponseBody ResponseEntity<User> update(#RequestBody User updateUser) {
dbUser = userRepository.findOne(updateUser.getId());
customObjectMerger(updateUser, dbUser);
userRepository.saveAndFlush(updateUuser);
...
}
In the above functions, you call some of your private functions & classes (userRepository, customObjectMerger, ...), but give no explanation how it works or how those functions look like. So I can only guess:
CustomObjectMerger merges this updateUser with the corresponding
dbUser from the database, updating the only fields included in
updateUser.
Here we don't know what happened in CustomObjectMerger (that's your function, and you don't show it). But from what you describe, I can make a guess: you copy all the properties from updateUser to your object at database. This is absolutely a wrong way, since when Spring map the object, it will fill all the data. And you only want to update some specific properties.
There are 2 options in your case:
1) Sending all the properties (including the unchanged properties) to the server. This may cost a little more bandwidth, but you still keep your way
2) You should set some special values as the default value for the User object (for example, id = -1, age = -1...). Then in customObjectMerger you just set the value that is not -1.
If you feel the 2 above solutions aren't satisfied, consider parsing the json request yourself, and don't bother with Spring object mapping mechanism. Sometimes it just confuse a lot.
Partial updates can be solved by using #SessionAttributes functionality, which are made to do what you did yourself with the customObjectMerger.
Look at my answer here, especially the edits, to get you started:
https://stackoverflow.com/a/14702971/272180
I've done this with a java Map and some reflection magic:
public static Entidade setFieldsByMap(Map<String, Object> dados, Entidade entidade) {
dados.entrySet().stream().
filter(e -> e.getValue() != null).
forEach(e -> {
try {
Method setter = entidade.getClass().
getMethod("set"+ Strings.capitalize(e.getKey()),
Class.forName(e.getValue().getClass().getTypeName()));
setter.invoke(entidade, e.getValue());
} catch (Exception ex) { // a lot of exceptions
throw new WebServiceRuntimeException("ws.reflection.error", ex);
}
});
return entidade;
}
And the entry point:
#Transactional
#PatchMapping("/{id}")
public ResponseEntity<EntityOutput> partialUpdate(#PathVariable String entity,
#PathVariable Long id, #RequestBody Map<String, Object> data) {
// ...
return new ResponseEntity<>(obj, HttpStatus.OK);
}

Categories