jsr372-experts@javaserverfaces-spec-public.java.net

[jsr372-experts] Re: [jsr372-experts mirror] Re: Re: Re: problems with jsf.push.init(...) specification and token generation

From: Bauke Scholtz <balusc_at_gmail.com>
Date: Wed, 7 Dec 2016 10:09:46 +0100

Hi all and specifically Leo,

How about this ViewHandler#getWebsocketURL() spec? I just copypasted from
getResourceURL() and altered where necessary. I myself only find the
wording of "will select the argument xxx for" somewhat unclear, but it is
as how those other getXxxURL() are specified.

    /**
     * <p class="changed_added_2_3">If the value returned from this
     * method is used as the <code>file</code> argument to the
     * four-argument constructor for <code>java.net.URL</code> (assuming
     * appropriate values are used for the first three arguments), then
     * a client making a websocket push handshake request to the
<code>toExternalForm()</code> of
     * that <code>URL</code> will select the argument <code>channel</code>
     * for connecting the websocket push channel in the current view.
     * It must match the {_at_link PushContext#URI_PREFIX} of the endpoint.</p>
     *
     * @param context {_at_link FacesContext} for the current request.
     * @param channel The channel name of the websocket.
     *
     * @throws NullPointerException if <code>context</code> or
     * <code>channel</code> is <code>null</code>.
     *
     * @return the websocket URL.
     * @see PushContext#URI_PREFIX
     */
    public abstract String getWebsocketURL(FacesContext context, String
channel);


A reference implementation would be this:

    @Override
    public String getWebsocketURL(FacesContext context, String channel) {
        requireNonNull(context, "context");
        requireNonNull(channel, "channel");

        String contextPath =
context.getExternalContext().getRequestContextPath();
        return contextPath + PushContext.URI_PREFIX + "/" + channel;
    }


Cheers, B


On Tue, Dec 6, 2016 at 4:25 PM, Bauke Scholtz <balusc_at_gmail.com> wrote:

> Hi,
>
> As per https://java.net/projects/mojarra/sources/git/revision/
> acb658925ed54206f0844f7eb88d5035218e22b3 the WebsocketHandler has been
> turned into UIWebsocket implementing ClientBehaviorHolder. Now f:ajax is
> supported as per below javadoc update:
>
> * <h3 id="ui"><a href="#ui">Ajax support</a></h3>
> * <p>
> * In case you'd like to perform complex UI updates depending on the
> received push message, then you can nest
> * <code>&lt;f:ajax&gt;</code> inside <code>&lt;f:websocket&gt;</code>.
> Here's an example:
> * <pre>
> * &lt;h:panelGroup id="foo"&gt;
> * ... (some complex UI here) ...
> * &lt;/h:panelGroup&gt;
> *
> * &lt;h:form&gt;
> * &lt;f:websocket channel="someChannel" scope="view"&gt;
> * &lt;f:ajax event="someEvent" listener="#{bean.pushed}"
> render=":foo" /&gt;
> * &lt;/f:websocket&gt;
> * &lt;/h:form&gt;
> * </pre>
> * <p>
> * Here, the push message simply represents the ajax event name. You can
> use any custom event name.
> * <pre>
> * someChannel.send("someEvent");
> * </pre>
>
> And, jsf.push.init/open/close now take client ID instead of channel name.
>
> One thing that's still open is having a ViewHandler#getWebsocketURL(). I
> will work on that later this week.
>
> Cheers, B
>
> On Fri, Oct 28, 2016 at 5:54 AM, Leonardo Uribe <leonardo.uribe_at_irian.at>
> wrote:
>
>> Hi
>>
>> Good to know that. Thanks!
>>
>> regards,
>>
>> Leonardo Uribe
>>
>>
>> 2016-10-27 3:07 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>
>>> I have reopened https://java.net/jira/browse/JAVASERVERFACES_SPEC_P
>>> UBLIC-1396
>>>
>>> Cheers, B
>>>
>>> On Fri, Oct 21, 2016 at 4:08 AM, Leonardo Uribe <leonardo.uribe_at_irian.at
>>> > wrote:
>>>
>>>> Hi
>>>>
>>>> I just wanted to mention the declaration of jsf.push.init(...) that
>>>> works better in my personal opinion, based on the previous
>>>> discussions:
>>>>
>>>> jsf.push.init(clientId, url, onopen, onmessage, onclose,
>>>> behaviorScripts, connected);
>>>>
>>>> Example:
>>>>
>>>> <div>
>>>> <h:outputText id="currentTime" value="#{clockBean.currentTime}"/>
>>>> </div>
>>>>
>>>> <f:websocket channel="clock" scope="application"
>>>> onopen="socketOpen"
>>>> onclose="socketClose">
>>>> <f:ajax event="showCurrentTime"
>>>> execute="currentTime"
>>>> render="currentTime"/>
>>>> </f:websocket>
>>>>
>>>> Generates this script:
>>>>
>>>> <script type="text/javascript"><!--
>>>> jsf.push.init('mainForm:j_id_p',
>>>> '/test-webapp-cdi/javax.faces.
>>>> push/clock?e82c26b5ccc2a5651571de6818cbfab0',
>>>> socketOpen,
>>>> null,
>>>> socketClose,
>>>> {showCurrentTime:[function(event){
>>>> jsf.ajax.request('mainForm:j_i
>>>> d_p',event,{execute:'mainForm:currentTime
>>>> ',render:'mainForm:currentTime ','javax.faces.behavior.event'
>>>> :'showCurrentTime'})}
>>>> ]},true);
>>>> //--></script>
>>>>
>>>> Only if the port is set in the global web config param, the url will
>>>> include it full.
>>>>
>>>> The only thing to mention is the channel name and the channel token are
>>>> derived from the URL, which means the URL should always end in that way
>>>> if it is overriden (portlet case usually overrides everything).
>>>> The other option is pass both as parameters of jsf.push.init(...), but
>>>> maybe
>>>> that's not necessary, better to keep it simple.
>>>>
>>>> regards,
>>>>
>>>> Leonardo Uribe
>>>>
>>>>
>>>> 2016-10-18 15:33 GMT-05:00 Leonardo Uribe <leonardo.uribe_at_irian.at>:
>>>>
>>>>> Hi
>>>>>
>>>>> BS>> As to clustering, do server and WS specific specifics really have
>>>>> to end up in JSF specification too?
>>>>>
>>>>> No, instead we do what we can with what we have. This is not something
>>>>> that we can fix from here.
>>>>>
>>>>> But we could ask websocket api EG about this for clarification.
>>>>>
>>>>> BS>> As to auto-reconnect (not auto-connect), yes I agree that some
>>>>> API-provided configuration settings
>>>>> BS>> would be useful. Mojarra's implementation does 25 attempts with
>>>>> an incremental interval
>>>>> BS>> of 500ms, starting with 0ms. I only wonder what parts should be
>>>>> configureable by API. The
>>>>> BS>> amount of reconnect attempts? If zero or negative, then it's
>>>>> considered as disabled.
>>>>>
>>>>> I was thinking about use a exponential backoff algorithm. But the
>>>>> problem here is it depends of what
>>>>> the developer wants. If he/she wants real time, the reconnection could
>>>>> be different. The key point is
>>>>> this could be a candidate parameter to be decided at deployment stage.
>>>>> If this is a config parameter,
>>>>> you can override it in web.xml without change the code. But if this is
>>>>> not considered relevant for
>>>>> the spec, we can just let this as an implementation detail.
>>>>>
>>>>> This is a design decision. My only intention here is to mention it.
>>>>>
>>>>> regards,
>>>>>
>>>>> Leonardo Uribe
>>>>>
>>>>>
>>>>> 2016-10-15 1:19 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> As to clustering, do server and WS specific specifics really have to
>>>>>> end up in JSF specification too?
>>>>>>
>>>>>> As to auto-reconnect (not auto-connect), yes I agree that some
>>>>>> API-provided configuration settings would be useful. Mojarra's
>>>>>> implementation does 25 attempts with an incremental interval of 500ms,
>>>>>> starting with 0ms. I only wonder what parts should be configureable by API.
>>>>>> The amount of reconnect attempts? If zero or negative, then it's considered
>>>>>> as disabled.
>>>>>>
>>>>>> Cheers, B
>>>>>>
>>>>>>
>>>>>> On Fri, Oct 14, 2016 at 8:06 PM, Leonardo Uribe <
>>>>>> leonardo.uribe_at_irian.at> wrote:
>>>>>>
>>>>>>> Hi
>>>>>>>
>>>>>>> BS>> As to clustering, see writeObject() and readObject() methods of
>>>>>>> OmniFaces
>>>>>>> BS>> o:socket SocketChannelManager class (on which Mojarra's
>>>>>>> f:websocket
>>>>>>> BS>> WebsocketChannelManager class is largely based).
>>>>>>>
>>>>>>> Please note I'm writing an implementation of f:websocket from
>>>>>>> scratch, so I'm
>>>>>>> trying do not see omnifaces code. I have checked the methods and
>>>>>>> anyway it
>>>>>>> doesn't solve the problem related to Session migration, because this
>>>>>>> code is
>>>>>>> not detecting when the Session instance has been migrated. But it
>>>>>>> supports the
>>>>>>> point that the token is not application scope, because you need to
>>>>>>> move
>>>>>>> the related data using a session scope bean. But as I said, this is
>>>>>>> implementation specific, doesn't change the spec.
>>>>>>>
>>>>>>> BS>> As to dropping "connected", I don't understand why you consider
>>>>>>> it an
>>>>>>> BS>> application wide setting instead of a socket specific setting.
>>>>>>> How do you
>>>>>>> BS>> want to let the client have control over manually opening a
>>>>>>> specific
>>>>>>> BS>> socket via jsf.push.open("channel") during e.g. an onclick
>>>>>>> event only?
>>>>>>> BS>> See also javax.faces.push.Push javadoc and o:socket showcase.
>>>>>>>
>>>>>>> My mistake here, I just read this in the javadoc:
>>>>>>>
>>>>>>> "... You can use the optional connected attribute to control whether
>>>>>>> to
>>>>>>> auto-connect the web socket or not. ..."
>>>>>>>
>>>>>>> But I was thinking about how the websockets connects in case of a
>>>>>>> disconnection between the client and the server.
>>>>>>>
>>>>>>> In other words, what I wanted to say is the way how the client
>>>>>>> attempts to
>>>>>>> reconnect to the server is something that you would like to control
>>>>>>> using a
>>>>>>> web config param. How many attemps, the timeout and so on. Maybe
>>>>>>> pass
>>>>>>> these config as an array in jsf.push.init(...) is a good idea.
>>>>>>>
>>>>>>> Please note I haven't implemented this attribute on my code yet, so
>>>>>>> I'm
>>>>>>> reporting on the mailing list what I'm seeing. I have not checked
>>>>>>> the
>>>>>>> showcase, because I do not want to bias my own judgment. The idea is
>>>>>>> the documentation in the spec should be good enough to give an idea
>>>>>>> about how it works. That's the way we ensure spec's quality. So, I
>>>>>>> have
>>>>>>> not digested every single line, but I'm on the way.
>>>>>>>
>>>>>>> regards,
>>>>>>>
>>>>>>> Leonardo Uribe
>>>>>>>
>>>>>>> 2016-10-14 2:39 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>>>>>>
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> As to clustering, see writeObject() and readObject() methods of
>>>>>>>> OmniFaces o:socket SocketChannelManager class (on which Mojarra's
>>>>>>>> f:websocket WebsocketChannelManager class is largely based).
>>>>>>>>
>>>>>>>> As to dropping "connected", I don't understand why you consider it
>>>>>>>> an application wide setting instead of a socket specific setting. How do
>>>>>>>> you want to let the client have control over manually opening a specific
>>>>>>>> socket via jsf.push.open("channel") during e.g. an onclick event only? See
>>>>>>>> also javax.faces.push.Push javadoc and o:socket showcase.
>>>>>>>>
>>>>>>>> Cheers, B
>>>>>>>>
>>>>>>>> On Fri, Oct 14, 2016 at 4:19 AM, Leonardo Uribe <
>>>>>>>> leonardo.uribe_at_irian.at> wrote:
>>>>>>>>
>>>>>>>>> Hi
>>>>>>>>>
>>>>>>>>> BS>> I do agree that the full url should be generated by the
>>>>>>>>> server
>>>>>>>>> BS>> and passed to the client. I will look into it.
>>>>>>>>>
>>>>>>>>> Ok, thanks. I just want to add that the "port" attribute is
>>>>>>>>> something that
>>>>>>>>> should be set using a global web config param, because this is
>>>>>>>>> common
>>>>>>>>> for all the application and depends on the deployment
>>>>>>>>> configuration.
>>>>>>>>> Please also consider if "connect" can be a global web config param
>>>>>>>>> instead
>>>>>>>>> an attribute too.
>>>>>>>>>
>>>>>>>>> BS>> The "token" is application scoped not session scoped.
>>>>>>>>>
>>>>>>>>> The logic behind this is something I have been thinking for a long
>>>>>>>>> time.
>>>>>>>>>
>>>>>>>>> Before start, I want to be clear that the suggested proposal is
>>>>>>>>> provide
>>>>>>>>> a point in the spec to allow customize the token generation by
>>>>>>>>> security
>>>>>>>>> purposes. The way how a token is used is considered an
>>>>>>>>> implementation
>>>>>>>>> specific concern, but in order to enrich the understanding of this
>>>>>>>>> component I'll put my thoughts about this here.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> There is a precedent that suggest an application scope token is
>>>>>>>>> not a good
>>>>>>>>> idea. See:
>>>>>>>>>
>>>>>>>>> - https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-694
>>>>>>>>> Current Flash specification breaks clustering
>>>>>>>>> - https://java.net/jira/browse/JAVASERVERFACES-1449
>>>>>>>>> Modify flash to support clustering
>>>>>>>>>
>>>>>>>>> The description says: "... Currently the flash implementation is
>>>>>>>>> entirely
>>>>>>>>> stored in the Application scope. This makes it unclusterable. Make
>>>>>>>>> it so
>>>>>>>>> data stored in the flash is cluster friendly. ..."
>>>>>>>>>
>>>>>>>>> Now, as you already know, f:websocket requires to register an
>>>>>>>>> Endpoint
>>>>>>>>> and in that place there is no HttpSession (there is on handshake),
>>>>>>>>> so from that point the only valid CDI beans are application scoped.
>>>>>>>>> So, the active websocket Session instances should be registered in
>>>>>>>>> an
>>>>>>>>> application scoped bean.
>>>>>>>>>
>>>>>>>>> There are two points to keep in mind:
>>>>>>>>>
>>>>>>>>> - Clustering relies on session replication, and application scope
>>>>>>>>> is not
>>>>>>>>> replicated among nodes.
>>>>>>>>> - Application scope means a potential for memory leaks, if the map
>>>>>>>>> does not
>>>>>>>>> have proper cleanup measures.
>>>>>>>>>
>>>>>>>>> If you make the token application scope, that means multiple views
>>>>>>>>> share the
>>>>>>>>> same token, but it also means there is no way to keep track of
>>>>>>>>> each
>>>>>>>>> connection in the application scope bean. So, at the end you will
>>>>>>>>> end up with
>>>>>>>>> stale session instances, and that's not good.
>>>>>>>>>
>>>>>>>>> If the websocket lifetime is shorter than the view lifetime, it is
>>>>>>>>> only
>>>>>>>>> natural that the token should be view scope. That means, for each
>>>>>>>>> combination
>>>>>>>>> of channel, scope and user it should be one channel token per view.
>>>>>>>>>
>>>>>>>>> The channel token is generated on render response time, so the way
>>>>>>>>> to do it
>>>>>>>>> is generate the token and store it in a view scope bean. If
>>>>>>>>> @PreDestroy is
>>>>>>>>> properly implemented for view scope beans, we can rely on that to
>>>>>>>>> destroy in
>>>>>>>>> a ordered way the token and the associated websocket Session
>>>>>>>>> instances from
>>>>>>>>> the application scope bean. If the session is destroyed,
>>>>>>>>> @PreDestroy will
>>>>>>>>> be called anyway, so with this we can avoid the memory leak.
>>>>>>>>>
>>>>>>>>> If you see the problem from a "cluster" point of view, if the
>>>>>>>>> token is
>>>>>>>>> application scope and the session is migrated to another node, the
>>>>>>>>> token will
>>>>>>>>> not be registered in the application scope of the target node, so
>>>>>>>>> the
>>>>>>>>> connection will not be accepted. Please note there is no CDI
>>>>>>>>> session scope
>>>>>>>>> in Endpoint.onOpen, but there is on the handshake request, so the
>>>>>>>>> solution
>>>>>>>>> is to register the valid tokens on session scope, so they can be
>>>>>>>>> validated
>>>>>>>>> later in that point. It could be variants to avoid store the
>>>>>>>>> tokens on
>>>>>>>>> session scope, but the problem is this feature is "CDI centric",
>>>>>>>>> so it is
>>>>>>>>> hard to initialize a FacesContext instance on the Endpoint. It can
>>>>>>>>> be done,
>>>>>>>>> but we need more "plumbing" to do it. In that case, we need a
>>>>>>>>> method
>>>>>>>>> on ViewHandler to validate the websocket token (remember
>>>>>>>>> ResponseStateManager is tied to RenderKit, and here there is view
>>>>>>>>> and
>>>>>>>>> no renderkit reference).
>>>>>>>>>
>>>>>>>>> Websocket spec says this about clustering:
>>>>>>>>>
>>>>>>>>> "... Websocket implementations that are part of a distributed
>>>>>>>>> container may
>>>>>>>>> need to migrate websocket sessions from one node to another in the
>>>>>>>>> case of a
>>>>>>>>> failover. ..."
>>>>>>>>>
>>>>>>>>> This blows up any storage of Session into an application scope
>>>>>>>>> bean, because
>>>>>>>>> it says the session can be moved between nodes, so in that case,
>>>>>>>>> the migrated
>>>>>>>>> Session will not be registered into the application scope bean.
>>>>>>>>> But this
>>>>>>>>> can be accepted, because probably in that case the websocket
>>>>>>>>> connection
>>>>>>>>> could be reopened, and the problem silently dissapear. This could
>>>>>>>>> be a
>>>>>>>>> potential websocket spec pitfall, where the spec says that the
>>>>>>>>> websocket
>>>>>>>>> sessions can be moved between nodes, but does not provide a way to
>>>>>>>>> identify
>>>>>>>>> when this has happened and take proper actions (like register the
>>>>>>>>> Session in
>>>>>>>>> the application scope bean).
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> In conclusion, the important thing about the websocket token is
>>>>>>>>> that it helps
>>>>>>>>> to identify the connection and its associated properties. If two
>>>>>>>>> f:websocket
>>>>>>>>> declarations share the same channel, user and scope, both should
>>>>>>>>> have the
>>>>>>>>> same token "at least" at view level. I think both implementations
>>>>>>>>> can agree
>>>>>>>>> on that assumption.
>>>>>>>>>
>>>>>>>>> No, thinking on jsf.push.init(...) I would like to propose this
>>>>>>>>> structure for
>>>>>>>>> the EG consideration:
>>>>>>>>>
>>>>>>>>> jsf.push.init(clientId, url, onopen, onmessage, onclose,
>>>>>>>>> behaviorScripts);
>>>>>>>>>
>>>>>>>>> removing "port" and "connected" (use web config parameters for
>>>>>>>>> that).
>>>>>>>>>
>>>>>>>>> The clientId helps to identify from which tag these properties
>>>>>>>>> belong, and
>>>>>>>>> make possible to reuse the same websocket connection among
>>>>>>>>> f:websocket
>>>>>>>>> declarations. I'll explain this in another email, this is a work
>>>>>>>>> in progress,
>>>>>>>>> but for now I'm sure we could agree with this function declaration.
>>>>>>>>>
>>>>>>>>> regards,
>>>>>>>>>
>>>>>>>>> Leonardo Uribe
>>>>>>>>>
>>>>>>>>> 2016-10-13 2:10 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>>>>>>>>
>>>>>>>>>> Hi,
>>>>>>>>>>
>>>>>>>>>> The "token" is application scoped not session scoped. I do agree
>>>>>>>>>> that the full url should be generated by the server and passed to the
>>>>>>>>>> client. I will look into it.
>>>>>>>>>>
>>>>>>>>>> Cheers ,B
>>>>>>>>>>
>>>>>>>>>> On Tue, Oct 4, 2016, 05:36 Leonardo Uribe <
>>>>>>>>>> leonardo.uribe_at_irian.at> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi
>>>>>>>>>>>
>>>>>>>>>>> Following a more exhaustive review of f:websockets, I have found
>>>>>>>>>>> some
>>>>>>>>>>> inconsistencies related to the way how a connection is specified.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Please note there is no mention of jsf.push.init(...) in the
>>>>>>>>>>> javascript documentation, but the package jsf.push is there.
>>>>>>>>>>>
>>>>>>>>>>> Here is an example:
>>>>>>>>>>>
>>>>>>>>>>> <script type="text/javascript">
>>>>>>>>>>> jsf.push.init(8080,'clock?1e4ac202-2b21-434f-8431-de5d5e4d37fe',
>>>>>>>>>>> socketOpen,socketListener,socketClose,true);</script>
>>>>>>>>>>>
>>>>>>>>>>> If you use "jsf" javascript package, every method there should
>>>>>>>>>>> be specified
>>>>>>>>>>> without exceptions. I know this feature is a "work in progress"
>>>>>>>>>>> but I just
>>>>>>>>>>> wanted to notice this fact.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Other thing to notice is there is a session token used to
>>>>>>>>>>> identify the
>>>>>>>>>>> connection. This is something related to state management, so I
>>>>>>>>>>> was expecting
>>>>>>>>>>> a method inside ResponseStateManager, in the same way there is a
>>>>>>>>>>> method
>>>>>>>>>>> there called:
>>>>>>>>>>>
>>>>>>>>>>> public String getCryptographicallyStrongTokenFromSession(FacesContext
>>>>>>>>>>> context)
>>>>>>>>>>>
>>>>>>>>>>> to handle protected views. Something like:
>>>>>>>>>>>
>>>>>>>>>>> public String getCryptographicallyStrongToke
>>>>>>>>>>> nForWebsocketSession(FacesContext context)
>>>>>>>>>>>
>>>>>>>>>>> It is only the token generation what we need here, not the
>>>>>>>>>>> websocket
>>>>>>>>>>> session handling.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> At last, there is a problem with passing params to assemble the
>>>>>>>>>>> url used
>>>>>>>>>>> for the connection in the client. It has been a long standing
>>>>>>>>>>> policy in JSF
>>>>>>>>>>> to generate urls on the server and pass them to the client. But
>>>>>>>>>>> in the
>>>>>>>>>>> javascript documentation a new variable was added called:
>>>>>>>>>>>
>>>>>>>>>>> jsf.contextpath
>>>>>>>>>>>
>>>>>>>>>>> with description "... The result of calling
>>>>>>>>>>> ExternalContext.getRequestContextPath(). ..."
>>>>>>>>>>>
>>>>>>>>>>> It looks like something recently added, so I guess it should be
>>>>>>>>>>> something
>>>>>>>>>>> related to jsf.push.init(...) script.
>>>>>>>>>>>
>>>>>>>>>>> The problem here is there is no way for the server to override
>>>>>>>>>>> this path,
>>>>>>>>>>> and in cases like portlets for example, it is necessary to
>>>>>>>>>>> provide a way
>>>>>>>>>>> to override this part properly. So the url used to connect must
>>>>>>>>>>> be built
>>>>>>>>>>> fully on the server and use the known ViewHandler -
>>>>>>>>>>> ExternalContext
>>>>>>>>>>> pattern. Additionally, embed this variable in the js file breaks
>>>>>>>>>>> any
>>>>>>>>>>> caching strategy. If the content is dynamic, embed it as an
>>>>>>>>>>> inline script,
>>>>>>>>>>> but in this case it is better to avoid that.
>>>>>>>>>>>
>>>>>>>>>>> regards,
>>>>>>>>>>>
>>>>>>>>>>> Leonardo Uribe
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>