Liferay 7 - Multiple resourceCommands in single class? - liferay

I'm about to move from Liferay 6.2 to 7. I'v been using Spring in Liferay 6.2, but apparently using Spring on 7 doesn't have benefits of using component specific configuration via classes.
It seems to me that every single Liferay 7 ajax endpoint needs to be configured as single command class, leading to dozens of files per logical model/controller.
On LR 6.2 Spring I have had single controller which wraps every resource-endpoint to single file. Is this possible on LR 7 with components? If LR7 enforces to use single class-file per command, while is this forced instead of supporting single class with multiple methods (design-wise)?

I assume that you're talking about the serveResource phase of a portlet when you talk about Ajax endpoints.
If you go the ResourceCommand route: Yes, you'll need a single resource command for every named resource handler. However, you do not have to go this way, you can still implement in a single portlet class.
The one difference:
You're in control of your own portlet, which means that you can easily change and update it should you need a different behavior. Thus it's not a problem to go with a single (potentially larger) portlet class.
On the other hand Liferay's built-in components sometimes need to be updated by others (e.g. you), so separating them out into many smaller services is a great thing for Liferay users who intend to modify tiny aspects: They only need to override the single ResourceCommand they have in mind for their change.
Thus, you'll see excessive use of the ResourceCommand pattern all over Liferay. But you can totally ignore this for your own code and continue with individual named resource handlers in a single class.
That being said, some pseudocode (only written here, never compiled and tested) for such a portlet:
#Component(...)
public class MyPortlet extends GenericPortlet {
public void serveResource(ResourceRequest req, ResourceResponse res) {
String name = req.getParameter("name");
// handle request for named activity
}
}
(Edit of the code: Apologies, I completely mixed up the Action and Resource phases, rather suggested "Action" logic than "Resource")
And yet another alternative is to ditch the portlet implementation and just implement a Web Service - either through Liferay's Service Builder, or through REST. There are plenty of samples available for these cases as well, but your question appeared as if you were going the portlet route.

Related

Spring-Content 1.2.7: Identifier for the ContentStore

What I intend to do: I'd like to use multiple ContentStores in the same system: one for freshly uploaded files (filesystem), one for (long term) archiving (AWS S3 or maybe GCS).
What I tried (and what actually does work):
Extended class File by another attribute private String contentStoreName;
Creating two ContentStores like described here: Spring-Content: Moving files from content store to another content store
Extending gettingstarted.FileContentController.setContent(Long, MultipartFile) by setting an identifier for the used ContentStore: f.get().setContentStoreName("regular");
Getting the content in dependence of the stored contentStoreName:
InputStream input;
if (Objects.equals(f.get().getContentStoreName(), "archive")) {
input = archiveContentStore.getContent(f.get());
} else {
input = regularContentStore.getContent(f.get());
}
Changing the contentStoreName when moving from one ContentStore to another:
Resource resource = regularContentStore.getResource(fileEntity.get());
archiveContentStore.setContent(fileEntity.get(), resource);
fileEntity.get().setContentStoreName("archive");
filesRepo.save(fileEntity.get());
The smell about this: Despite this code works, I guess it's not the intended way, because Spring-content usually does a lot with annotations and some magic in the background. But I can't find an annotation for an identifier / name for the ContentStore.
Question: Is there a more intended way of doing this in Spring-Content?
Beyond supporting multiple storage modules in a single application (via the FilesystemContentStore, et al annotations) Spring Content does not currently provide any logic for supporting classes of storage. That would be a layer that you need to create on top of Spring Content as you are starting to do.
In terms of Spring Content annotations it might be helpful for you to understand what modules manage what annotations.
Spring Content storage modules; FS, S3, etc all implement ContentStore and in doing so all provide management of the #ContentId and #ContentLength attributes (in addition to managing the actual content operations). Looks like you are using the ContentStore API (getContent/setContent) and therefore your entity's content id and length attributes will be managed for you.
Spring Content REST then provides management of #MimeType and #OriginalFileName attributes (in addition to providing comprehensive REST endpoints). My guess is that you are not using this and instead providing your own custom controller REST API that uses the 'contentStoreName' attribute to decide which store to put/get the content from. This approach seems fine.
That is all to say that a slightly more elegant approach, perhaps, that would allow you to use Spring Content REST might be to implement your own custom Archiving Storage module and encapsulate the "switching" logic you have above in its setContent/getContent/unsetContent methods. Note, this is actually quite easy (just 4 or 5 classes and I would point you at the GCP and Azure modules for inspiration). Note, that the REST API (for ContentStore) only calls those 3 APIs too so those are the only ones you would need to implement. This would mean you get to use Spring Content REST and all the features it provides; set of rest endpoints, byte range support and so on as well as encapsulating your "archiving" logic nicely.

JSF 2.x simplify complex views into a single tag

I have a general JSF problem, I found no nice solution for yet. See the picture for a general idea. I have a workaround solution (sorry for the typo in the image) in place that solves the problem by a listbox. However the desired solution is to display all existing versions next to each other (probably always around 1-3).
I have a view with a tree and picklist. There is a complex flow regarding the interaction between list and tree, e.g. you can only move models to subgroups, not top-level-groups and much more. I created a handler class that manages this behavior and translates it to service calls.
Now, a new requirement came up. There are several versions of this tree that should be displayed all together on one page. My gut feeling is that managing n versions in one handler is a big mess as I need to store several things in the handler already for one version.
In React, I would create a component that wraps the tree and all of the interaction. However, in JSF I'm not so sure what is the best practice here?
I would be happy about suggestions and ideas, I'm not expecting Code :)
I found a solution that fits my needs and I post it here hoping that it might help other people as well :)
So on my view I have several tree views with complex interactions. For example, if an item within the tree is moved, the operation is immediately reflected in the database. As I use JPA, I need to translate this to an entitymanager call.
The views are either displayed in a list or just one-at-a-time via a dropdown select.
Anyway, the idea is that every complex view component has its own controller with a reference to an entitymanager and a transaction, while having just one JSF handler class. If JSF would allow to create multiple handlers (like #{handler_1}, {handler_2}), the problem could be solved in a different way. But as JSF works name based and the name {#handler} always refers to the same container managed thing, this is no option.
The handler class is ViewScoped (or SessionScoped, if you prefer). For each tree component it has a ComponentController class that receives the EntityManager and the UserTransaction as well as the related data form the handler via constructor injection. This way, the handler can delegate all commands to the Controller while being DRY.
With this solution, the controller logic can be re-used regardless how many tree components exist. Each view elements binds a specific controller via handler.controllers.get(id).
All other solutions did not work for me as they are not able to perform database operations on view interactions.

JSF separate DB logic from #Named bean

Good day
This question is more of a meta question than a specific problem based question.
It is always a good idea to separate any and all DB code from view related code and view files, correct?
Thus is my assumption when using JSF correct in that:
The xhtml file forms part of the view in the MVC.
The #Named backing beans also forms part of the view.
To ensure that one can relatively painlessly migrate away from JSF to another type of tech, one would ensure to not have ANY DB code inside the #Named backing beans.
All DB code should reside in a controller / service class.
The controller / service class will contain all the DB access code and business logic. This thus forms the controller of the MVC.
The #Entity classes are used to map the DB to JPA and this thus forms the model of the MVC.
Now, if my understanding of the above is correct, what would best methods and methodologies be when handling the following scenario:
I have an XHTML file displaying JSF components (Primefaces). The lists and component linked members that link the JSF components to the Java code all resides in the #Named backing bean.
Now for argument sake, let's say that the specific form is used to CRUD a supplier's information, which of the following methods is recommended as best practices (while attemping to maintain as much seperation of concern between the View and the Controller Java code) when for instance edits were made on the XHTML form:
Enforce ZERO DB code (thus never defining or using the Entity Manager) in the #Named backing bean file). The code to persist the changes after all input validation was successfully done, will reside in the Controller. To get these values to the Controller, we will have a function looking perhaps like this (basically send all the components on the xhtml form as parameters to a function in the Controller):public void supplierService (String supName, String SupAddress, String supTel....) The problem with this of course is that one may end up running into methods that takes tens of parameters. Very ugly and difficult to maintain.
Accept that separating some DB code is not possible and every #Named file must have the required JPA DB code to persist and or merge changes to the models (however if this is considered best practice, what is the use of having Controller classes?).
Create a temporary object of the same type as the model and set the attributes of this temporary object to the values obtained from the XHTML mapped components. Then only pass this temporary object to a method in the Controller. This method in the Controller will then persist and or merge the passed object's info. However I feel this may introduce unnecessary object instantiation overhead. Also I am not 100% sure what exactly happens 'behind the scenes' when I have a model named SupplierEntity.java that is mapped via JPA to a PostgreSQL DB and I call this code: SupplierEntity tempSup = new SupplierEntity(); Will JPA via Hibernate on Wildfly actually at this point create a new entity (record in the DB), and as such I cannot use this to create a temporary object to hold the values I am passing to the Controller as a temp instance of the underlying JPA entity, or will Hibernate (using JPA 2.1) ONLY create a new record when I do em.persist(mySupplier); and thus it is safe to use this method to pass objects to the controller's persisting method, instead of passing tens of parameters to the persisting method.
Something completely different than what I mentioned above is considered to be the best practice for separating the MVC components in JSF as much as possible, while still preventing having to pass 50 parameters to the Controller.
Please as said right in the start, this is a meta question regarding best practices. If Stackoverflow is not the right forum for these questions, instead of down voting this into oblivion, please let me know where I should ask instead and I will gladly delete the question from here and create it on the right forum.

User roles and workflow status xpages and managed bean

To not have to keep repeating some validations, for example, who can see a button in a certain status of a document in the worlflow, I'm using session, scope, and session variables to store the user roles and application variable to store the Status related to each area.
I was evaluating whether it would be better from a performance and build point of view to implement a managed bean, to return the user roles and the possible statuses of each participating workflow area. Would it be the best structure in fact? What do you think? I do not have much experience in java. How could I construct the structure in java, several methods, one for roles and the other for set of status associated with the area that would name the related method? You could return the results of this method in arrays, or there is a better return structure.
Thanks a lot!
My best suggestion is to adopt the pageController Methodology. Then it's more like true MVC. This has been talked about on NotesIn9 screencast many times but basically you have a java object that's bound to your XPage. In effect it's a viewScoped bean that holds all your page logic. Then you can have methods like isGroupMember(), hasRole() etc and calculate that on the pageInit. There's little need to hold onto that in sessionScope in my opinion. So for example I have this in my pageController :
public boolean isGroupMember(String groupName) {
return JSFUtil.getXSPContext().getUser().getGroups().contains(groupName);
}
So that's available to each page. BUT I don't need to copy that snippet onto every page controller. In Java you can have your page controllers extend a more generic class. so I have a "base.pageController" class. All the specific page controllers extend that. So this isGroupMember() code goes into the base and then it's available to be used on every XPage. Doing it this way gives you the ability to have generic functions like this and then hold more specific function that are only for the individual page.
You can also have a hasRole() function etc...
Recommend you check out this video : http://www.notesin9.com/2016/08/25/notesin9-196-no-dependency-page-controllers/
Also for a question like this, I recommend you just use the xpages tag. Adding others like javabeans can bring people in who know nothing about XPages and XPages is unique enough of a beast that outsiders can cause some confusion on occasion.

Storing properties of a custom component in session scope or bean

I've written a new back-end Java component (extending UIComponentBase) as an alternative for the ExtLib Application Layout control. The control needs to show a collection of data that is looked up from another Notes application. The data is user dependant and doesn't change from page to page so, to avoid repeatedly doing a lookup to the other application, I want to store it in the session scope. (Note that because this is a layout control, there will only ever be one per page.)
I know I could use a session-scoped maanged bean (and have done in previous iterations) but the data only needs to be used in this control and shouldn't be used elsewhere on the page which it could be with a bean. So my question is, what's the best practice approach I should take here? Should I just directly store the data in the sessionMap or am I missing a trick with the component stateHolder? Or am I stuck with using a bean?
Thanks
(Edited for clarification)
It looks like you're talking about your own back-end Java components rather than Custom Controls within a single NSF.
I'm not sure at what level, when you write your own native XPages components, the properties are cached by the stateHolder when calling saveState(). I would presume no higher than View, for the reasons Frantisek says, that otherwise it would be unclear which instance to update if you had multiple on one XPage but one on another. It couldn't update both at the same time on the same page, so I would guess that each is a separate instance. As a result, the same component on multiple pages would be a separate discreet instance.
But there's nothing stopping you, in specific setters of the component, writing to sessionScope as well as the private property, and then doing the reverse on the getter. I'm not sure if you'd want to try the internal property before trying sessionScope or vice versa. It would depend how you wanted to handle the situation of the same sessionScope being updated from multiple pages (if the collection could change).

Resources