WSRP Portlets in Oracle WebCenter: transforming task flows(ADF) in portlets - portlet

I am doing some research on the portlets offered by WebCenter, but I have some problems related with transferring parameters between them. My idea was to create 2 portlets: a department portlet where I can chose a departmentId which is sent as a parameter to the second portlet, employees, so I will have a table with the corresponding employees from the specified department. These 2 portlets are constructed based on some page flows. The department portlet works fine, but with the employees portlet, I have some problems.
The JSP page fragment corresponding to the employees has a table based on a ViewObject which has behind him a query based on a bind variable. I have created an EmployeesBean, where I have the method that takes the received parameter and executes the query with this bind variable. Here is the code:
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import oracle.adf.view.rich.context.AdfFacesContext;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
public class EmployeesBean {
private static final String DEPARTMENT_NUMBER_KEY = "DEPTNO";
private static final int DEPARTMENT_NUMBER_NULL_VALUE = -1;
public EmployeesBean() {
super();
}
public void getEmployees(String deptno) {
System.out.println("enters in getEmployees()");
int filterDeptno = findDepartmentValue(deptno);
FacesContext facesContext = FacesContext.getCurrentInstance();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp =
elFactory.createValueExpression(elContext, "#{data.AppModuleDataControl.dataProvider}",
Object.class);
ApplicationModule am = (ApplicationModule)valueExp.getValue(elContext);
ViewObject emplVO;
emplVO = am.findViewObject("EmployeesVO1");
emplVO.setNamedWhereClauseParam("deptno", filterDeptno);
emplVO.executeQuery();
Row r = emplVO.first();
System.out.println(r.getAttribute("FirstName"));
}
public void setDepartmentNumber(String deptno) {
selectDepartment(deptno);
}
public void selectDepartment(String deptno) {
System.out.println("aici e problema");
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
System.out.println(deptno);
afContext.getPageFlowScope().put(DEPARTMENT_NUMBER_KEY, deptno);
}
public int findDepartmentValue(String defaultValue) {
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
String deptno =
(defaultValue == null ? (String)afContext.getPageFlowScope().get(DEPARTMENT_NUMBER_KEY) :
defaultValue);
return (deptno == null ? DEPARTMENT_NUMBER_NULL_VALUE :
Integer.valueOf(deptno));
}
}
I have also dragged on the employees.jsff the getEmployees() method so if I go to page definition I have there a binding, which will determine the getEmployees method to be executed every time an event appears. All this mixed with the departments.jsff works in a .jspx page if I create the Event mapping
Now I am trying to transform this task flow into a portlet. After I create a portlet entry for the page flow, I need to create a navigational parameter, and I am doing this in the employees.xml:
<input-parameter-definition>
<description>Main context parameter</description>
<display-name>Department Number</display-name>
<name>deptno</name>
<value>#{pageFlowScope.contextProvider.departmentNumber}</value>
<class>java.lang.String</class>
</input-parameter-definition>
<managed-bean>
<managed-bean-name>contextProvider</managed-bean-name>
<managed-bean-class>view.EmployeesBean</managed-bean-class>
<managed-bean-scope>pageFlow</managed-bean-scope>
</managed-bean>
Everything works fine, but when I am trying to use this as a portlet in a WebCenter application, when I select a department the departmentId is transferred to the employees portlet, the selectDepartment is called, but the getEmployees() is never called(the event is not propagated), so no data is returned in my table. I'm a beginner in portlets and I can't see what the problem is. Can anyone give me some ideas?

when you consume the employee portlet , it creates the page parameter in the page def. You will have to pass the data to that parameter

Related

EF Core - many-to-many: how to add middle entity when one entity does not yet exist?

I am curios if I can make this easier/better.
The user is able to create new tickets and with each ticket can be several files associated, one file can be related to multiple tickets.
Now, when the user creates a ticket, he can already add files. Meaning I have no Id for the ticket and thus no way to build a relation. How should I solve this?
public class FilesPerTicket
{
public int TickedId;
public Ticket Ticket;
public int FileId;
public File File;
}
public class File
{
public ICollection<FilesPerTicket> FilesperTicket;
}
public class Ticket
{
public ICollection<FilesPerTicket> FilesperTicket;
}
Now the user creates the ticket and adds several files to it.
public IActionResult Create(MyModel model)
{
// .....
var filesPerTicket = model.Files.Select(x => new FilesPerTicket() { FileId = x.Value }).ToList();
var newTicket = new Ticket() { //...... };
newTicket.FilesPerTicket = filesPerTicket;
// ....
context.Add(newTicket);
context.SaveChanges();
}
This doesn't work because we haven't provided a Ticketid and therefore every TicketId in FilesPerTicket is 0.
I know that I just can save the ticket and afterwards it will have the primary key in it. Which I then can use to fill the FilesPerTicket and save that one.
But then I would have those in separate transactions (because SaveChanges was called) and use another try/catch.
Is there an way to tell EF Core to fill this TicketId automatically when this entity gets saved at the same time as a ticket? Or some other way to save that entity?

Why is my data not persisted/accessible in an Spring-Boot integration-test with HTTPGraphQLTester and TestEntityManager

I have a bare-bones Spring-Boot app with some GraphQL endpoints and a Postgres database and want to run an integration test against an endpoint. It should find an entity by its ID and does so without a problem when I send a request manually via Postman. However when I write an integration test for the controller it doesn't. The data seems to be saved after using
TestEntityManager (or the JpaRepository directly) an I get the entity back with its ID. I then stick that ID into a query with HttpGraphQlTester which fails with an empty result/null. I traced it with the debugger and discovered that when the endpoint calls the repository to retrieve the entity with the given ID it gets null or when I look at all the repo-contents it's just an empty list. So my data seems to be accessible in my test but not in my repo/service. Any pointers would be very much appreciated.
Test
#SpringBootTest
#AutoConfigureHttpGraphQlTester
#AutoConfigureTestEntityManager
#Transactional
public class BackboneTreeControllerTest {
#Autowired
HttpGraphQlTester tester;
#Autowired
private TestEntityManager testEntityManager;
#Test
void findTaxon() {
Taxon taxon = Taxon.builder()
.path(Arrays.asList("path", "to", "taxon"))
.nameCanonical("Cocos nucifera")
.authorship("Me")
.extinct(false)
.numDescendants(1l)
.numOccurrences(1l)
.build();
Taxon savedTaxon = testEntityManager.persistFlushFind(taxon); // (1)
this.tester.documentName("queries")
.operationName("FindTaxon")
.variable("taxonId", savedTaxon.getId())
.execute()
.path("findTaxon.authorship")
.entity(String.class)
.isEqualTo("Me");
the testEntityManager returns successfully with an ID.
Query
query FindTaxon($taxonId: ID!) {
findTaxon(id: $taxonId) {
authorship
}
}
Controller
#Controller
#AllArgsConstructor
public class BackboneTreeController {
private final TaxonService taxonService;
#QueryMapping
public Taxon findTaxon(#Argument Integer id) {
Optional<Taxon> taxon = taxonService.findTaxon(id);
return taxon.orElse(null);
}
}
Service
#Service
#AllArgsConstructor
public class TaxonService {
private final TaxonRepository taxonRepository;
public Optional<Taxon> findTaxon(Integer id) {
return taxonRepository.findById(id); // (2)
}
}
This is where I would expect the repo to return the entity but it does not. Also using .findAll here returns an empty list.
Repository
#Repository
public interface TaxonRepository extends JpaRepository<Taxon, Integer> {
}
Note that everything works fine when I just run the app and send the exact same query manually!
I don't know HttpGraphQlTester but I'd assume that it generates requests which then get processed in a separate thread.
That thread won't see the changes made in the test because they aren't committed yet.
If this is the reason resolve it by putting the setup in it's own transaction, for example by using TransactionTemplate.

GWT RequestFactory with Set sub-collections

I have a little problem with RequestFactory regarding persistence of children collections in the shape of Set . I am using gwt 2.5 with requestfactory, and Hibernate4/Spring3 at the backend. I am using the open-session-in-view filter by Spring so that collections can be persisted after findByID in the save method of my DAO. My problem is everything seems to work ok when children collections are based on List , but when they are based on Set , not all of the items from the client reach the server aparently.
My code looks like this:
-The root entity IndicationTemplate:
#Entity
#Table (name = "vdasIndicationTemplate")
#org.hibernate.annotations.Table ( appliesTo = "vdasIndicationTemplate", indexes =
{#Index (name = "xieIndicationTemplateCreateUser", columnNames= {"createUserID"}),
#Index (name = "xieIndicationTemplateModifyUser", columnNames= {"modifyUserID"})})
public class IndicationTemplate extends AbstractEditable <Integer> implements IEntity <Integer>, IDateable, IDescriptable {
//...
private Set <ProposalTemplate> proposalTemplates = null;
//...
#OneToMany (fetch = FetchType.LAZY, mappedBy = "indicationTemplate"
, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.DETACH})
public Set <ProposalTemplate> getProposalTemplates () {
return proposalTemplates;
}
public void setProposalTemplates (Set <ProposalTemplate> proposalTemplates) {
this.proposalTemplates = proposalTemplates;
}
//...
}
-The child entity ProposalTemplate of course has the opposite ManyToOne mapping and has 3 sub-collections as well of the same sort with 3 different entities.
-Client-side proxy for root entity:
#ProxyFor (value = IndicationTemplate.class, locator = PersistenceEntityLocator.class)
public interface IIndicationTemplateProxy extends IEntityProxy, IDeletableProxy, IDescriptableProxy {
//....
Set <IProposalTemplateProxy> getProposalTemplates ();
void setProposalTemplates (Set <IProposalTemplateProxy> proposalTemplateProxy);
}
-On the client, i render the attributes of root entity and also the list of children entity. Then the user can update them, and the changes are stored back into the collection like this:
Set <IProposalTemplateProxy> newList = getElementsFromUiSomehow (); //these elements can be new or just the old ones with some changes
indicationTemplate.getProposalTemplates ().clear ();
indicationTemplate.getProposalTemplates ().addAll (newList);
-And then at some point:
requestContext.saveIndicationTemplate ((IIndicationTemplateProxy) entityProxy)
.fire (new Receiver <IIndicationTemplateProxy> ()
-The RequestContext looks something like:
#Service (value = TemplateService.class, locator = SpringServiceLocator.class)
public interface ITemplateRequestContext extends RequestContext {
/** saves (creates or updates) one given indication template */
Request <IIndicationTemplateProxy> saveIndicationTemplate (IIndicationTemplateProxy indicationTemplate);
//....
}
The problem is only 1 child entity is added per request to the collection server-side. For example, indicationTemplate has 2 proposalTemplates, and i add 4 more, then on the server-side saveIndicationTemplate the entity contains only 3 instead of 6. If happens no matter how many entities i have previously and how many i add, i only get 1 more than before on the server. I did check the proxy object right before firing the requestContext method and it is fully loaded, with all of its children. And finally the weirdest thing is, if i replace Set per List (and all subsequent changes), everything works sweet!
May there be any problem why RF fails to transfer all the changes to the server when using Sets instead of Lists?? Btw, i do prefer Sets in this case, so that is why i am asking.
Anyone?
Thanks for helping!
I assume you are hitting this error. It is a known gwt bug which is still unfixed.
https://code.google.com/p/google-web-toolkit/issues/detail?id=6354&q=set&colspec=ID%20Type%20Status%20Owner%20Milestone%20Summary%20Stars
try to use list instead of set and it should be fine.

Spring MVC custom editor and select-options bad performance

Im using custom editor in Spring MVC to map string valuest to my domain objects. Simple case: User object refers to Company (User.company -> Company). In User form I register data binder:
protected void initBinder(WebDataBinder binder) throws Exception {
binder.registerCustomEditor(Company.class, new CompanyEditor(appService));
}
Editor is defined as folows:
class CompanyEditor extends PropertyEditorSupport {
private AppService appService;
public CompanyEditor(AppService appService) {
this.appService = appService;
}
public void setAsText(String text) {
Company company = appService.getCompany(text);
setValue(company);
}
public String getAsText() {
Company company = (Company) this.getValue();
if (company != null)
return company.getId();
return null;
}
}
When I use dropdown in my form
<form:select path="company">
<form:options items="${companies}" itemLabel="name" itemValue="id"/>
</form:select>
I experience severe performance problems because (to check if company is selected, I suppose) fires setAsText and getAsText for each option, which makes it to run a SQL query for each company.
I thought that setAsText is used when I commit form to make application know how to translate compnany id to Company (persisted) object. Why should it fire it in dropdowns. Any ideas how to fix it?
If your form backing object is stored as session attribute(i.e. you have something like #SessionAttributes("command") in your controller), so you can try to modify your setAsText(String text) method
public void setAsText(String text) {
Company currentCompany = (Company) this.getValue();
if ((currentCompany != null) && (currentCompany.getId().equals(text)))
return;
Company company = appService.getCompany(text);
setValue(company);
}
but I think that Spring 3.1 #Cacheable abstraction was introduced exactly for the such kind of things and is preferable
see examples in documentation
#Cacheable("books")
public Book findBook(ISBN isbn) {...}
P.S. Consider using new Converter SPI instead of Property Editors.
In general, it's possible to implement a generic converter for your look-up entities, so it will automatically convert entities from text using id if they have some specific attribute, for example, in one of my projects all #Entity types are being automatically converted using a global ConditionalGenericConverter implementation, so I neither register custom property editors during binding nor implement specific converters for types which are simple #Entity classes with #Id annotated primary keys.
Also it's very convenient when Spring automatically converts textual object ids to the actual entities when they are specified as #RequestParam annotated controller method arguments.

How can I use RequestFactory to create an object and initialize a collection whithin it with objects retrieved from another ReqFactory?

I am struggling with an issue using RequestFactory in GWT.
I have a User object : this object has login and password fields and other fields which are of collection type.
public class User {
private String login;
private String password;
private Set<Ressource> ressources;
// Getters and setters removed for brievety
}
I need to persist this object in db so I used RequestFactory because it seems like a CRUD-type operation to me.
Now for the RequestFactory part of the code, this is how I have tried to do it :
I create a UserRequestContext object to create a request object for the new User. Which gives something like :
public interface MyRequestFactory extends RequestFactory {
UserRequestContext userRequestContext();
RessourceRequestContext ressourceRequestContext();
}
and to create the user object I have something like this :
public class UserAddScreen extends Composite {
private UserProxy userProxy;
EventBus eventBus = new SimpleEventBus();
MyRequestFactory requestFactory = GWT.create(MyRequestFactory.class);
...
public UserAddScreen() {
...
requestFactory.initialize(eventBus);
}
public showUserAddScreen() {
// Textbox for password and login
// Listbox for ressources
}
}
I have tried to implement it as a wizard. So at the beginning of the UserAddScreen, I have a
a userProxy object.
This object fields are initialized at each step of the wizard :
the first step is adding the login and password
the second step is adding ressources to the userProxy object.
for this last step, I have two list boxes the first one containing the list of all the ressources i have in my DB table Ressources that I got from RessourceRequestContext.getAllRessource (I have a loop to display them as listbox item with the RessourceId as the value) and the second allows me to add the selected Ressources from this first listbox. Here is the first listbox :
final ListBox userRessourcesListBox = new ListBox(true);
Receiver<List<RessourceProxy>> receiver = new Receiver<List<RessourceProxy>>() {
#Override
public void onSuccess(List<RessourceProxy> response) {
for(RessourceProxy ressourceProxy : response) {
ressourcesListBox.addItem(ressourceProxy.getNom() + " " + ressourceProxy.getPrenom(), String.valueOf(ressourceProxy.getId()));
}
}
};
RessourceRequestContext request = requestFactory.ressourceRequestContext();
request.getAllRessource().fire(receiver);
So, as you can see, my code loops over the retrieved proxies from DB and initializes the items within the listbox.
Here are the control buttons :
final Button addButton = new Button(">");
addButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
for (int i = 0; i < ressourcesListBox.getItemCount(); i++) {
boolean foundInUserRessources = false;
if (ressourcesListBox.isItemSelected(i)) {
for (int j = 0; j < userRessourcesListBox
.getItemCount(); j++) {
if (ressourcesListBox.getValue(i).equals(
userRessourcesListBox.getValue(j)))
foundInUserRessources = true;
}
if (foundInUserRessources == false)
userRessourcesListBox.addItem(ressourcesListBox
.getItemText(i), ressourcesListBox
.getValue(i));
}
}
}
});
So when somebody selects one or more users and click on a ">" button, all the selected items go to the second listbox which is named userRessourceListBox
userRessourcesListBox.setWidth("350px");
userRessourcesListBox.setHeight("180px");
After that, I have a FINISH button, which loops over the items in the second listbox (which are the ones I have selected from the first one) and I try to make a request (again) with RequestFactory to retrieve the ressourceProxy object and initialize the userProxy ressources collection with the result
final Button nextButton = new Button("Finish");
nextButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
RessourceRequestContext request = requestFactory.ressourceRequestContext();
for(int i = 0; i < userRessourcesListBox.getItemCount(); i++) {
Receiver<RessourceProxy> receiver = new Receiver<RessourceProxy>() {
#Override
public void onSuccess(RessourceProxy response) {
userProxy.getRessource().add(response);
}
};
request.find(Long.parseLong(userRessourcesListBox.getValue(i))).fire(receiver);
}
creationRequest.save(newUserProxy).fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
Window.alert("Saved");
}
});
}
});
Finally, (in the code above) I try to save the UserProxy object (with the initial request context I have created userProxy with)... but it doesn't work
creationRequest.save(newUserProxy).fire(...)
It seems like when looping over the result in the onSuccess method :
userProxy.getRessource().add(response);
I retrieve the response (of type RessourceProxy) but beyond this method, for example when I try to save the userProxy object AFTER THE LOOP, there are no RessourceProxy objects in the ressourceProxy collection of userProxy...
Have you guys ever experienced something like this ?
Perhaps I am not doing it right : do I have to get the ressource with the UserRequestContext ? so that my newUser object and ressources are managed by the same request Context ?
if yes then I think it's a little bit weird to have something mixed together : I mean what is the benefit of having a Ressource-related operation in the User-related request context.
any help would be really really ... and I mean really appreciated ;-)
Thanks a lot
The message "… has been frozen" means that the object has been either edit()ed or passed as an argument to a service method, in another RequestContext instance (it doesn't matter whether it's of the same sub-type –i.e. UserRequestContext vs. RessourceRequestContext– or not) which hasn't yet been fire()d and/or the response has not yet come back from the server (or it came back with violations: when the receiver's onViolation is called, the objects are still editable, contrary to onSuccess and onFailure).
UPDATE: you have a race condition: you loop over the resource IDs and spawn as many requests as the number of items selected by the user, and then, without waiting for their response (remember: it's all asynchronous), you save the user proxy. As soon as you fire() that last request, the user proxy is no longer mutable (i.e. frozen).
IMO, you'd better keep the RessourceProxys retrieved initially and use them directly in the user proxy before saving it (i.e. no more find() request in the "finish" phase). Put them in a map by ID and get them from the map instead of finding them back from the server again.