Introduction
In this article, I would like to talk about a small study on the crossover of GWT and Webtop.
First I will answer the legitimate question: “Why is this necessary?”. I pursued several goals:
1) Examine the GWT.
2) To study in more detail the insides of WDK and Webtop.
3) It is possible to find a way to create GWT components for the webtop.
GWT was chosen because it allows you to quickly create UI and client-side Java code to programmers. It also supports AJAX out of the box.
It was the bundle with Webtop that was used, rather than creating your application from scratch, since it would take more time (Documentum session support, invention of action and precondition replacement) and would not allow using already written code on Webtop.
Border conditions
According to the logic of working with objects:
CRUD, business transactions on objects.
Ability to work with action and, as a result, Webtop's precondition. Those. A way to call the action and check the precondition for the action from the client must be provided.
According to the contents of the objects:
Maximum 100 attributes. Maximum 100 repeating attribute values.
By UI:
GWT must be integrated into an existing solution on the Webtop. At least at the level of a separate component. Those. It should be possible to call the GWT component from a simple Webtop component. (The word “component” refers to the standard Webtop component) And there must also be a way to exit the GWT component back to its calling component.
')
Comment:
Below are the main aspects of GWT that I will use. I want to draw your attention to the fact that I will not consider in detail the work with each technology - the article is not about that. If you want to read about something in more detail - links at the end of the article.
Ui
To create a UI, I first tried the simple GWT method - a la Swing, when widgets' are created directly in the code. It turned out well - all the code in one file and it is immediately clear that where it is created. But when the form becomes quite large (at least more than 100 lines) and many listeners of events appear on the form, the code becomes overloaded and difficult to read.
In contrast to creating widgets from GWT code, there is another mechanism - UI Builder. The main difference - the UI is described separately from the logic in the xml file. Of the minuses of the UI Builder - not all widget's available in GWT are available in the UI Builder component palette. I would even say that widget's support in the Window Builder is very limited. So in the end, if you need to do something specific (at least add a column to the table), you will have to write code.
Receiving objects by the client
All work with Documentum objects will be performed through a call to server methods. It remains only to determine how to organize this work. The first obvious way is to use standard RPC from GWT. We simply create the necessary service, which will be responsible for processing any database object — for example, an employee service and an organization service for working with objects. The objects in this case are the POJOs that are transferred between the client and the server. In articles on GWT there is a note on how to organize work with Hibernate
developers.google.com/web-toolkit/articles/using_gwt_with_hibernate?hl=en-RUThere is considered a similar way of communication. The difference (besides being used by Hibernate) in the use of DTO is the data transfer object. Following from the title and from the wiki, these are intermediate objects for transferring database entities to the client. They are needed in order to minimize the sending of unnecessary data to the client. Those. there is an entity containing 100 attributes, but the client will only use 10. This is how a special DTO is created for the desired entity with these 10 fields.
The advantages of this method are very simple to understand and implement.
Cons - use DTO or, otherwise, the potential transfer of a large number of unnecessary attributes of the object to the client.
The second way is Request Factory. This is an alternative to RPC, designed just to perform CRUD operations on objects. The main difference from a simple RPC call is that the objects on which operations take place are immediately separated into two categories — the objects that the server manipulates and the presentation objects (inherited from EntityProxy) that the client manipulates. In principle, proxy objects are nothing more than DTO. Just in this case, the transformation of the main object in the DTO deals with GWT.
Discussion on StackOverflow about RPC and RequestFactory
stackoverflow.com/questions/4119867/when-should-i-use-requestfactory-vs-gwt-rpcThe method is certainly interesting, but it requires writing additional code. Besides, I didn’t like the implicit dependency between the methods inheriting from RequestContext and the objects that implement these methods, as well as between getter & setters Proxy objects and the objects themselves. Although for the first case GWT uses a validation module for RequestFactory which finds inconsistencies at compile time.
Passing parameters between Webtop and GWT
After searching, I found several ways to pass parameters to GWT - via JSP (inserting values ​​into JS and then parsing), via url (parsing get request) and using a servlet that will return parameters.
For Webtop, the JSP version is a good fit, since the component assumes the presence of a jsp page, which will just contain the parameter insertion code, and the component will allow forwarding these parameters in the JSP.
To do this, in jsp you need to add this code:
<script type="text/javascript"> var wdk2gwt_param = '${wdk2gwtParam}'; </script>
Here is described the JS variable wdk2gwt_param which will be assigned the value of the attribute wdk2gwtParam.
And in the code components for writing parameters to write this:
@Override public void onRender() { getPageContext().setAttribute(“wdk2gwtParam”, “ ”); super.onRender(); }
Here, when displaying the page, we assign the wdk2gwtParam attribute, which was described above in jsp, value.
To be able to exit a component, add an event handling method:
public void onReturnClick(Control control, ArgumentList list) { setComponentReturn(); }
Here the standard method of the component (setComponentReturn ()) is called, which allows you to return to the component that called it.
And in the GWT above, use the following methods:
public native String getParam() ; public native void callPostServerEvent() ;
Here is the standard way to call JS code from Java code. In the getParam () method, it simply returns the value of the variable wdk2gwt_param (which was specified in jsp). In the callPostServerEvent () method, the standard JS function WDK is used to call the component's methods. In this case, the onReturnClick method, which was described above, is called. For the JS function postServerEvent to work, it must first be defined. To do this, the following tags must be specified in jsp: <dmf: webform /> and <dmf: form>
In this regard, the full jsp code will look like this:
<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/WEB-INF/tlds/dmform_1_0.tld" prefix="dmf"%> <html> <head> <dmf:webform /> <title></title> </head> <body> <dmf:form> <script type="text/javascript"> var wdk2gwt_param = '${wdk2gwtParam}'; </script> <script language="javascript" src="/Webtop/ru.tim.gwt.correspondent.Correspondent/ru.tim.gwt.correspondent.Correspondent.nocache.js"></script> </dmf:form> </body> </html>
Work with sessions
In work quality with Documentum sessions we will use SessionManagerHttpBinding. It allows you to get an instance of the current user's IDfSessionManager. From which you can get the session itself.
IDfSessionManager sessionManager = SessionManagerHttpBinding.getSessionManager(); IDfSession session = null; try { session = sessionManager.getSession(SessionManagerHttpBinding.getCurrentDocbase()); } catch (Exception e) { e.printStackTrace(); } finally { sessionManager.release(session); }
This code can be used in GWT servlets to retrieve and, subsequently, close the session. Of course, you need to make the correct output in the log and add a null check before invoking sessionManager.
Execution of action WDK
It is necessary for performance of action and check of precondition. Use case - check that the specified action with the listed parameters is executed and execute it.
The first idea that came to mind is to conduct reverse engineering of form calls. (There are no docks on the network). Having rummaged in the code, traced a chain of method calls. As a result, there is a lot to do to get the form and the context.
The second option is AJAX using Documentum
community.emc.com/docs/DOC-8012 - the so-called inline calls.
Those. the point is to make a method in a component that will be called asynchronously from a client with parameters — the name of action and the list of parameters of action. And the result of the execution is returned to the client. There is one note here: if the action of switching to other components is used in action: setComponentReturn, setComponentJump, setComponentNested (and possibly others), then there will be an error (Component Configuration Base has not been established) when re-requesting methods. This is most likely due to the fact that the component has gone out of its current state (has switched to another component), but when asked for the server, the parameters indicate the already obsolete state of the component. More precisely, the parameters specify a unique string within the current session, according to which the server restores the component state. And this state is outdated.
In principle, a more or less working solution has already been found. But I also decided to check the onaction method of the Component class. This is a method that performs all the action of its form (this can be noticed by the stack trace, if you set a break point in the action and call it). To be precise, this method is called by all standard action control's forms. This is an ordinary component method that can be called externally. And this method supports inline calls. So, to call this method, you need to give it the name action and parameters for it. The first time the usual action is called, we get an error - the action must implement the IInlineCapableAction interface. Having corrected it, it will turn out to successfully call action. To transfer a value from action, use Map to return the value (6th parameter IActionExecution. Execute) and place the value with the key RESPONSE_DATA there.
The complete code looks like this:
Content xml with action description.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <config version="1.0"> <scope> <action id="test_action"> <params> <param name="test_param" required="false"/> </params> <execution class="ru.tim.wdk.test.TestAction"/> </action> </scope> </config>
Note that the action takes the test_param parameter.
The action method that will be called asynchronously.
public boolean execute(String actionName, IConfigElement config, ArgumentList args, Context context, Component component, Map resultMap) { System.out.println("Action param: " + args.get("test_param")); resultMap.put("RESPONSE_DATA", "actionResultValue"); return true; }
The parameter test_param is displayed here. Above it was described in xml. To output the response, the response map is placed in the result map actionResultValue with the RESPONSE_DATA key. RESPONSE_DATA - hard-coded response key for inline calls to action. I found it when viewing the onaction method code. When inline calling action from the handler class (CallbackDoneListenerWrapper), the value from the Map is taken using the RESPONSE_DATA key. When sending a response to the client, the RESPONSE_DATA key is also used (the processInlineActionResponseData method in the Component class).
The jsp part of the page with methods for sending and receiving the result of the action:
<script type="text/javascript"> function executeAction() { var prefs = InlineRequestEngine.getPreferences(InlineRequestType.JSON); prefs.setCallback(callBack); postInlineServerEvent(null, prefs, null, null, "onaction", "action", "test_action", "test_param", "test_param_value"); } function callBack(data) { if (isEventPostingLocked()) { releaseEventPostingLock(); } if (data) { var result = data['RESPONSE_DATA'] window.alert(result); } } </script>
The executeAction method performs an asynchronous request to the server to execute action. The callBack function is used to process the response.
In the postInlineServerEvent method, the execution method is called action - onaction. With parameters - action, in the parameter the name of the called action is passed. test_action - the name of the called action. test_param - the name of the action parameter. test_param_value is the parameter value. If you need to add more parameters, they should be further listed in pairs - <parameter name>, <parameter value>.
To use the action from GWT, you need to wrap the call to the postInlineServerEvent function from the GWT method, and setCallback to throw the GWT function as a parameter.
It now remains to implement the precondition check method for the specified action.
You can do this using the method in the component that will perform the precondition for the specified action. The precondition itself must be performed using the ActionService.queryExecute function (strAction, args, context, component)
Environment setup
For convenient work with Webtop and Eclipse, let's create a Gwt project. In the org.eclipse.wst.common.project.facet.core.xml project (located in the .settings folder in the project folder), add the following lines
<fixed facet="jst.web"/> <fixed facet="java"/> <fixed facet="wst.jsdt.web"/> <installed facet="java" version="1.6"/> <installed facet="jst.web" version="2.5"/> <installed facet="wst.jsdt.web" version="1.0"/>
The result should be the following:
<?xml version="1.0" encoding="UTF-8"?> <faceted-project> <fixed facet="jst.utility"/> <fixed facet="java"/> <fixed facet="jst.web"/> <fixed facet="wst.jsdt.web"/> <installed facet="java" version="1.6"/> <installed facet="jst.web" version="2.5"/> <installed facet="wst.jsdt.web" version="1.0"/> <installed facet="jst.utility" version="1.0"/> </faceted-project>
Here we have indicated that the project will be a web project and can be launched in Tomcat. Add the Weptop and Web App Libraries folder to the Java Build Path window. Set up the Deployment assembly to unload the gwt and webtop source folder of the project and the gwt and weptop web content folders.
There is only one problem left - setting up web.xml to allow gwt to run separately from Webtop. Since now web.xml includes configuration and servlet's gwt and servlet's webtop. Alternatively, you can put all the settings in one web.xml Webtop and upload only it. For gwt, leave only its settings. Only you need to keep the correct order of uploading - first unload Webtop, and then gwt. In this case, the data will not be overwritten.
Implementation
For testing, I made a small component that displays an object of a certain type and allows you to edit several single fields. Sources can be downloaded from here
dl.dropbox.com/u/7519092/gwt2wdk.rar (you need a Webtop to work)
Conclusion
The basics of interaction between GWT and Webtop turned out to be done. But for the full implementation of the template component editing card, you need to do several things:
1) Form with the choice of the object. In Webtop, it is called selector.
2) Widget's with action support, so that you can configure them to start the action, rather than writing code for it.
3) Plug-in components for editing certain attributes. For example, in Webtop, you can use them in the Properties component.
Plus a few things about the GWT - checking input on the client side, the correct distinction between UI and events.
Links
RequestFactory Manual
developers.google.com/web-toolkit/doc/latest/DevGuideRequestFactory?hl=en-EN#locatorsRequestFactory Example (1)
cleancodematters.wordpress.com/2011/06/04/tutorial-gwt-request-factory-part-iRequestFactory Example (2)
javaasylum.blogspot.com/2010/11/gwt-21-request-factory.htmlRequestFactory Example (3)
turbomanage.wordpress.com/2011/03/25/using-gwt-requestfactory-with-objectify