[ 
http://jira.nuxeo.org/browse/NXP-1720?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_29650
 ] 

Radu Darlea commented on NXP-1720:
----------------------------------

Excerpts from previous emails regarding ExternalEditWS Web Service: 
"Here is the first draft spec for the new LiveEdit architecture.
Overview :
==========
The purpous is to have generic desktop integration architecture that allow 
Nuxeo5 users to edit files directly with the native local editor :
 - the user clicks on "edit on line" link
 - the associated editor is started on the client side
 - the editor must comunicate with Nuxeo server to :
    - download the file to edit
    - acquire lock if needed
    - save the file back to nuxeo server
    - create new version if needed
    - release lock if needed
    - ...

This somehow what we already have with current version of LiveEdit. The 
purpouse is :
  - to define a more generic architecture : be able to have editor plugins to 
support other editors that MSOffice2003
  - to clearly define the WS interface used by editors

Global Architecture :
=====================
[...]
Server side :
-------------
On the server side we will have a dedicated WebService. The WebService will 
mainly handle 2 types of methods :
 - download/upload file
 - fetch available actions / do actions

Actions availables on the document may depend on the client project 
specifications, and it's important that it is totally transparent for the 
client plugin UI : we don't want to build a version of the clients plugins for 
each project.
So the idea, as in existing LiveEdit, is that the WebService will provide the 
client a simple list of actions, the client will simply display availables 
actions, and eventually ask the server to execute them without knowing the 
underlying logic. This "action logic" is somehow close to what we already do in 
the web layer, an action is :
  - an action id
  - a label : generated on the server side
If several actions can be done (like checkout + lock), then it will be 
presented as 3 actions : chekout, lock, checkout_and_lock.
This way we won't have to handle associations conditions on the client side : 
the user can always select one and only one action : none / lock / checkout / 
chekout_and_lock 
[...]
Authentication management :
---------------------------
As the Bootstrap client needs to do a http call, it needs to be able to
authenticate.
There are 2 solutions :
  - reuse JSESSIONID
  - use basic authentication
        => means the bootstrap client has a configuration file or registry keys
to store login/password entered by the user.
[...]
The Editor :
============ 
The editor process will :
 - receive and read the bootstrap file
 - start the client editor (MSOffice via COM interop interface, OOffice via ??? 
...)
 - call WS to get list of pre-edit actions
 - display a dialog for letting user select action
 - call WS to download the file
 - call WS to get list of post-edit actions
 - display a dialog for letting user select action
 - save and upload the file to Nuxeo Server
 - terminate

For MSOffice, this Editor process could be seperated in 2 parts :
 - the bootstrap part : 2 first steps
 - the pure editor part : all other steps
        => This could be implemented as an office plugin

The WebService :
================
This Web Service will be exposed by a dedicated EJB3. It will be pretty much 
like the existing FileManagerWS : but cleaner
code with better wsdl. I propose to call it ExternalEditWS (feel free to 
propose a sexier name  :)  ).

API :
-----
For now here is what I see in the API :
 - Map<String,String> getPreEditActions(String sid, String repo, String docRef, 
String fieldName) : send possible action for pre-edit
 - Map<String,String> getPostEditActions(String sid, String repo, String 
docRef, Stringe fieldName) : send possible action for post-edit 
 - Boolean doAction(String sid, String repo, String docRef, String 
fieldName,String actionId) : execute action, returns true if client must 
re-read the file from server
 - getFile(String sid, String repo, String docRef, String fieldName)
 - saveFile(String sid, String repo, String docRef, String fieldName, Blob blob)
 - String getProperty(String sid, String repo, String docRef, String fieldName)
 - String setProperty(String sid, String repo, String docRef, String fieldName, 
String value)

Authentication :
----------------
The call to WS must be authenticated.
2 ways for doing that :
  - reuse the JSESSIONID : get UserPrincipal and Login context from the 
incomming request
        => call with sid=null
  - explicitly create a session and forward it in each call
        => call with non null sid"
"Anyway, a complete WS should include some other methods: getting/setting 
properties of the document, session management. And I think download / upload 
are in fact actions. So, WS should:
- user session management
- supply the list of actions available for a particular stage
- do that actions
The user session management should allow:
- fresh new session creation through the user authentication (user name / 
password, cert, etc.)
- propagate an already created session. If the user authenticated already in 
the browser for instance, there should be no need for a new authentication, and 
the browser could not know the credentials. So we need to provide both ways of 
ensuring the user access.
About this, a little later.

Looking at the current implementation, I see 2 different approaches:
1. Based on Seam (UI authentication) - navigationContext == FileManageWS
2. Independent self content pure WS == NuxeoRemotingBean + 
AbstractNuxeoWebService
I would certainly prefer the second approach because I am not sure that the 
navigationContext is available outside the UI. My intention is to prepare a 
self content WS API which should be available to any client. Making the 
assumption the client would be a browser limits the usability of WS. This 
assumption could be a valid prerequisite though, and if it is accepted it could 
simplify the overall implementation. But in that case I don't see why use WS 
when other options are available (for instance recently developed NuxeoClient).
The specific difference between the 2 approaches is the mechanism of obtaining 
the user context. I mean: authentication and managing the session.
This problem is solved by the navigationContext/Seam, in the first case. The 
second approach is using a pair of methods connect/disconnect and a sessionID 
string required for all the exported methods.
These mechanism are legitimated mechanisms. The third would be WS Addressing. 
Well, maybe it is time to describe a little what WS Addressing does...

WS-Addressing was originally authored by Microsoft, IBM, BEA, Sun, and SAP and 
submitted to W3C for standardization. The W3C WS-Addressing Working Group has 
refined and augmented the specification in the process of standardization.
WS-Addressing is currently specified in three parts:
    * The Core specification of Endpoint References and Message Addressing 
Properties.
    * A binding of these properties to SOAP.
    * The Metadata specification defines how the abstract properties defined in 
Core are described using WSDL, how to include WSDL metadata in endpoint 
references, and how WS-Policy can be used to indicate the support of 
WS-Addressing by a Web service.
The last updated occurred in 2004 December and it is now a mature standard.

Specification abstract: "WS-Addressing provides transport-neutral mechanisms to 
address Web services and messages. Specifically, this specification defines XML 
elements to identify Web service endpoints and to secure end-to-end endpoint 
identification in messages. This specification enables messaging systems to 
support message transmission through networks that include processing nodes 
such as endpoint managers, firewalls, and gateways in a transport-neutral 
manner."
What does this mean?
WS-Addressing initiates a major transformation of the web services landscape 
with the advent of the "end-point".  WSDL let you defines the interface and the 
classes that implement a particular interface whole an end-point maps to an 
instance that conform a particular class. As such, end points can be and are 
generally dynamically created. An end-point is uniquely identified by its 
"address" and properties. Note that the end-point address may and is generally 
different from the service port address. In addition to specifying an end-point 
definition, WS-Addressing specifies also the way end-points are used and mapped 
within SOAP 1.2.
An Endpoint Reference (EPR) is an XML structure encapsulating information 
useful for addressing a message to a Web service. This includes the destination 
address of the message, any additional parameters (called reference parameters) 
necessary to route the message to the destination, and optional metadata (such 
as WSDL or WS-Policy) about the service. Endpoint references allow applications 
to augment a traditional URI-based address with application-specific 
information that can be used to better dispatch the message.
Normally, the WS Server side only creates one instance of your service class, 
and have it handle all incoming requests concurrently. This effectively takes 
you back to pre-OO programming, as you will not be able to make use of instance 
fields at all — it's as if all your methods are static. The problem with that 
is that if the rest of your application is modeled in OO, this mismatch becomes 
painful. It would be much nicer if you can make it OO all the way, like you do 
with RMI, where you can export "objects". The stateful web service support 
brings you that. On the wire, it uses WS-Addressing standard, so it's fully 
interoperable.
On a simple phrase, WS Addressing takes out the localization of the required 
resource within the container from the HTTP header and places it inside the 
SOAP envelope. And that allows dynamic addressing. And that enables sessions.

For all which want to see these in the context from where I took them (of 
course I didn't write these out of my head):
http://en.wikipedia.org/wiki/WS-Addressing
ftp://www6.software.ibm.com/software/developer/library/ws-rm-exec-summary.pdf
http://hyperthink.net/blog/PermaLink,guid,8519a1bf-e1b7-4b9b-b10c-fce8d359b83e.aspx
http://www.ebpml.org/ws-addressing.htm
and for usage. a very clear example
http://weblogs.java.net/blog/kohsuke/archive/2006/10/stateful_web_se.html
As Kohsuke Kawaguchi shows in his example, the session issue became a simple 
standard implementation.
And not forget that WS Addressing is providing a solution transport-agnostic: 
it can be http, SMTP, TCP anything. 
[...]
Both have to be available, leading to the same result. And here is the problem. 
Let's take 2 scenarios:
A. The client is using WS from a stand-alone (i.e. independent of browser) 
application. She takes the entry point from WSDL, connect to Server which 
creates a "session" in behalf. Further, the client is working on that Stateful 
Bean (session).
B. The client is already authenticated in browser and wants to start using WS. 
She already has a Web Session and associated a App Session, a Core Session. 
Now, going to the entry point she has somehow supply and obtain the App session 
transparently, without a new authentication. How we can do that? I am not sure 
even if is possible."
"> In this moment it is implemented only the "Lock" action. What do you mean by 
the others?
 - Lock/Unlock
 - Checkin/Checkout
 - save, save as new minor version , save as new major version (Version Policy)
 - validate / reject (WF actions)
 - ..."
"are the "actions" are also available in WSDL?
or there will be a generic call executeAction(actionID)?

If the first case, I can see little advantage of that dynamic list, anyway the 
actions are visible from the start in WSDL and they can or not be used by the 
client. If a Client application doesn't care about locking, it simply doesn't 
call lock(), nor makes it visible.
If the second case, there is a little overhead on the server (checking the 
action ID and dispatching it) and it would be a little harder / riskier to link 
some parameters. "
"No parameter for now. I understand your question about parameters, but this is 
too complexe for what we need. Entering parameters in a generic client (ie : 
LiveEdit) means we are able to send to the client a layout definition (like 
list of parameters, with types, flag for compulsary parameters ...).
So for a start, no parameters.
[...]
If the user has a valid Nuxeo5 session, then all http calls will go throught 
the NuxeoAuthenticationFilter. This filter will reestablish the JAAS 
LoginContext and give you access to the user Proncipal via 
request.getUserPrincipal.
This means :
  - the EJB calls will be authenticated
  - if needed you can get the Principal, either via the http request object "
"> As you can see, I am trying to model the Nuxeo WS implementation with
> Stateful Web Service as sessions. SWS isn't compatible with EJB SFSB. AFAIK:
> - SWS is managed by the WS stack
> - only one callback is available: timeout()
> I intend to model a client - web service dataflow as:
> C1. Client gets the port of the stateless web service NuxeoWSMainEntrancePoint
> C2. Client asks the EPR for the wanted service
> S1. Server authenticates the Client and constructs a BasicSession containing 
> a CoreSession, a jaas LoginContext and a Repository
> S2. Server constructs the SWS and feeds the BasicSession to SWS
> S3. Server returns the EPR
> C3. Client constructs the web service port based on the EPR
> C4. Client uses a feature of the web service
> S4. Server accomplishes the task within the SWS
> .......................................
> Server destroys the SWS on timeout() or Client request.
>
> Is enough BasicSession in order to have a fully functional Nuxeo Session (I 
> mean to be able to access the documents stored in repository)?
>
> Is it ok to obtain these objects and store them in SWS during the life
> of the SWS? The life of SWS starts with the access request from client
> and ends on timeout or at the client's disconnect.
>
Yes.
> I have JAAS authentication exception if I don't perform a dummy 
> Framework.login() every time a web service feature is called. Why that? Is it 
> ok to perform this dummy login in every method?
> For an example on how it is made, take a look at the
> LEWS.getRootDocument() and LEWS.getChildren() in the sandbox. BTW, this idea 
> came to me looking at NuxeoRemotingBean: there it is exactly this 
> implementation.
>
> I really don't like this dummy login... Is it really needed? Isn't damaging? 
> Can be avoided?
> (Framework.login() ends up in LoginComponent.loginAs(null), not that it helps 
> me much understand why)
>
It is not really a dummy login :) It is a system login. Framework.login() or 
loginAs("asSomeone") should
be used from nuxeo code when you need to perform some background operation that 
may require or not admin. privileges.
For example let say you are a regular user and some operation you are doing 
using the client (under your real login) will trigger
an async. event that will later need to access to the repository as a "system 
user" to do something. (for example to fetch some data to be indexed)
In that case the user identity doesn't matter - we only need to do a login to 
avoid EJBAccessExceptions.
On your side it will be better to use a real login - this mean to use 
login(username, password) or the NuxeoAuthenticationFilter that thierry is 
talking about.
Look into 
nuxeo-platform-ui-web/src/main/resources/OSGI-INF/deployment-fragment.xml for 
examples on how to bind this filter on an URL pattern.

Anyway that filter is using JBoss specific code and won't work on glassfish or 
other app servers. The filter implementation should be reviewed to try to use 
generic code (for example use the jboss ClientLoginModule to do the 
SecurityAssociation bindings - and avoid doin that explicitely)
or at least to better isolate JBoss specific code. But this is another 
discussion.

Let's go back to our problem. So, why you have an exception at each request if 
you don't re-login?
The answer is simple: because the Framework.login() is creating an 
authentication context and binds it on the current thread.
When you make another client request the request will usually be served in 
another thread so you loose your previously
login context. This also means you must logout before ending the request. If 
you don' t do this and the thread is reused by another process
it will run inside your own login context. So you need to logout at request 
end. And you need to login at request start.
This is because the jboss ClientLoginModule which is used by Framework.login() 
is by default multi-threaded - it keeps the login context in a ThreadLocal 
variable (so this login context is existing only on the thread you performed 
the login).
In application like Apogee where you are always logged under the same identity 
until you completely disconnect from the server - the jboss ClienLoginModule is 
configured with multi threading off to be able to use the same login context on 
all threads (the login context will be stored in a static variable). But this 
is not possible to do on the server because here you may want to have plenty of 
concurrent threads each one with its own login context.

So what is the solution? One solution is to use the filter used at web client 
level another solution is to try to do the same stuff as the auth. filter do:
to create a login context the first time an user log in and then to restore 
this login context at each subsequent request from the same user.
Restoring the login context means to initialize some JBoss internal state using 
 SecurityAssociation jboss class. I think this context restore can also be done 
using a special login module (that we need to write) in conjunction with jboss 
ClientLoginModule to avoid to use jboss specific code in our app without 
re-authenticating each time. (this is also true for the web client auth. 
filter).

So there are two methods to solve this: whether to use the filter whether to 
reimplement the logic existing in that filter but in an app server independent 
manner
Anyway, sooner or later (glassfish integration?) we will need to reimplement 
the code in that filter and make it more generic so that you can use it outside 
jboss and why not outside http requests. And also we need to make that code 
generic - to avoid using JBoss internal API  which I think is possible. Or at 
least to isolate jboss specific code using extension points or something 
similar.

It will also work if you do an explicit Framework.login(user,pass) at each 
request and a logout at the end.
The downside is that the authentication will be performed each time. "
"his is listing the API for the ExternalEditWS. I think it is important not 
only for the Server side, but rather it is important for the users. The final 
approve that the API is complete and correct can be obtained only from the 
Client side application developers. They can say if there is need for more 
functions or not.

public interface ExternalEditWSable {


    /**
     * List the actions available before starting editing.
     * @param repo
     * @param docRef
     * @param fieldName
     * @return actions, all are exclusive, and the user will have to chose
     * one of the options.
     */
    public abstract HashMap<String, String> getPreEditActions(
            String repo, String docRef, String fieldName);

    /**
     * List the actions available after finishing editing.
     * @param repo
     * @param docRef have to be not null
     * @param fieldName
     * @return actions, all are exclusive, and the user will have to chose
     * one of the options.
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract HashMap<String, String> getPostEditActions(
            String repo, String docRef, String fieldName)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Executes the action.
     * @param repo
     * @param docRef have to be not null
     * @param fieldName
     * @param actionId have to be not null
     * @return if the document has to be reloaded
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract boolean doAction(
            String repo, String docRef, String fieldName, String actionId)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Gets a blob property content. The property is named by the fieldName.
     * @param repo
     * @param docRef has to be not null
     * @param fieldName has to be not null
     * @return
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract byte[] getFile(String repo, String docRef, String fieldName)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Sets a blob property content. The property is named by the fieldName.
     * @param repo
     * @param docRef has to be not null
     * @param fieldName has to be not null
     * @return if the document has to be reloaded
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract boolean saveFile(
            String repo, String docRef, String fieldName, byte[] content)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Gets a simple property content. The property is named by the fieldName.
     * @param repo
     * @param docRef has to be not null
     * @param fieldName has to be not null
     * @return
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract String getProperty(String repo, String docRef, String 
fieldName)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Gets all simple properties content in one shot.
     * @param repo
     * @param docRef has to be not null
     * @return
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract DocumentProperty[] getSimpleProperties(String repo, String 
docRef)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Gets blob properties name in one shot.
     * @param repo
     * @param docRef has to be not null
     * @return
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract String[] getBlobPropertyNames(String repo, String docRef)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Sets a simple property content. The property is named by the fieldName.
     * @param repo
     * @param docRef has to be not null
     * @param fieldName has to be not null
     * @return if the document has to be reloaded
     * @throws ClientAuthenticationException
     * @throws ItemNotFoundException
     * @throws ServerProcessingException
     */
    public abstract boolean setProperty(String repo, String docRef,
            String fieldName, String value)
            throws ClientAuthenticationException,
            ItemNotFoundException, ServerProcessingException;

    /**
     * Disconnects gracefully the client. If the method is not call, a timeout 
mechanism
     * will perform it anyway.
     *
     */
    public abstract void disconnect();

}


For accessing the Stateful Web Service:
public interface NuxeoWSMainEntrancePointable {

    /**
     * Gives access to Live Edit Stateful Web Service.
     * @param userName credential
     * @param password credential
     * @return the EPR to the stateful instance.
     * @throws ClientAuthenticationException the credentials are bad
     */
    public abstract W3CEndpointReference accessExternalEditWS(
            String userName, String password) throws 
ClientAuthenticationException;

} "
"> You are right. Moreover, I think always this will be a xpath. Otherwise  I 
don't see how can I uniquely identify the requested 
> property, without knowing the schema name and the complete name.
the problem is that fieldName can define a field that is in fact a list of 
other fields (Complex type).
=> we need to be able that in the files schmea we want to edit the doc that is 
the 3th element of the list of file
> OTOH, I don't see an usage for the repo argument. I kept it because of the 
> initial API design, do you think I should keep it for future > usage?
Nuxeo WebApp support to be connected to several repository. 
=> This parameter must be present even if in defaut deployment there is only 
one repository.

>> Since we may need to edit  Blob that are part of a complex type, I think the 
>> fieldName parameter should be renamed as xPath.
>> In simple cases, the xPath parameter will simply be the fieldName, but in 
>> more complexe cases (like editing a file containted
>> inside the files schema) we will use a real XPath expression."

> Implement LiveEdit Web Service on top of JAXWS
> ----------------------------------------------
>
>                 Key: NXP-1720
>                 URL: http://jira.nuxeo.org/browse/NXP-1720
>             Project: Nuxeo Enterprise Platform 5
>          Issue Type: New Feature
>    Affects Versions: 5.2
>            Reporter: Radu Darlea
>            Assignee: Radu Darlea
>             Fix For: 5.2
>
>
> Implement the new LiveEdit Web Service.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: 
http://jira.nuxeo.org/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira


_______________________________________________
ECM-tickets mailing list
[email protected]
http://lists.nuxeo.com/mailman/listinfo/ecm-tickets

Reply via email to