Home Help Center

Ice for Javascript client with java server

bmaranvillebmaranville Member Brian MaranvilleOrganization: National Institute of Standards and TechnologyProject: NICE instrument control
Our organization is developing a Java application using Ice. I was wondering if there is a way to use Ice for Javascript to develop a web-based client - I've run into the problem that there doesn't seem to be a plugin for IceWS available for Java.

Can we add the websocket capability in Glacier2 or something like that?

Comments

  • bernardbernard Jupiter, FLAdministrators, ZeroC Staff Bernard NormierOrganization: ZeroC, Inc.Project: Ice ZeroC Staff
    Hi Brian,

    Welcome to our forums!

    The solution for now is indeed to use Glacier2 between your JavaScript client and your Java server: your Ice for JavaScript browser-app will communicate with Glacier2 (a C++ service) using ws or wss, and in turn Glacier2 will communicate with your Java server over TCP.

    In this diagram Attachment not found.

    the Ice C++ server is Glacier2, and your Java server is further to the right.

    All the best,
    Bernard
  • bmaranvillebmaranville Member Brian MaranvilleOrganization: National Institute of Standards and TechnologyProject: NICE instrument control
    configuring glacier2 to point to Java server

    Ah - excellent.

    Can I configure Glacier2 to point to the Java server without modifying the server code?
  • bernardbernard Jupiter, FLAdministrators, ZeroC Staff Bernard NormierOrganization: ZeroC, Inc.Project: Ice ZeroC Staff
    When you use Glacier2, you don't need to change your server at all: one of its clients is now the Glacier2 router.

    You won't configure Glacier2 to point to your Java server. It's the proxies in your client (Ice for JavaScript client) that will contain the addressing information to your Java server, and tell Glacier2 how to reach this Java server.

    Best regards,
    Bernard
  • bmaranvillebmaranville Member Brian MaranvilleOrganization: National Institute of Standards and TechnologyProject: NICE instrument control
    Thank you so much for your help -

    I feel like I am really close. I have a glacier2router running with the IceWS plugin installed, and I have some test client code that is able to connect to the router and start a session:
    (javascript: after importing Ice.js, Glacier2.js and all the compiled slices)

    var Promise = Ice.Promise;
    var RouterPrx = Glacier2.RouterPrx;
    var SessionPrx = Glacier2.SessionPrx;
    var id = new Ice.InitializationData();
    var secure = false;
    var hostname = "localhost";
    var router = secure ? "Glacier2/router:wss -p 4065 -h " + hostname :
    "Glacier2/router:ws -p 4064 -h " + hostname;
    id.properties = Ice.createProperties();
    id.properties.setProperty("Ice.Default.Router", router);
    //id.properties.setProperty("Ice.ACM.Client", "0");
    //id.properties.setProperty("Ice.RetryIntervals", "1");
    s = null;
    Promise.try(
    function() {
    communicator = Ice.initialize(id);
    Router = RouterPrx.checkedCast(communicator.getDefaultRouter());
    return Router;
    }
    ).then(
    function(r) {
    Router = r;
    s = Router.createSession('','');
    return s
    }
    ).exception(
    function(ex)
    { alert(ex.toString()); }
    );
    /// end test code

    The implementation class in javascript is nice.api.ClientApiManager (and of course there's also a nice.api.ClientApiManagerPrx).

    What I'm trying to do next is get a proxy to the "ClientApiManager" on the java server, which I normally connect to at this endpoint: "ClientApiManager:tcp -h localhost -p 4321". Everything I have tried has failed - if I try communicator.stringToProxy I get an error because the tcp endpoint is not supported on a direct connection...
  • benoitbenoit Rennes, FranceAdministrators, ZeroC Staff Benoit FoucherOrganization: ZeroC, Inc.Project: Ice ZeroC Staff
    Hi,

    The Ice for JavaScript runtime doesn't know how to parse a stringified TCP endpoint. We will consider fixing this.

    In the meantime, the best is to change your Java server to implement the Glacier2::SessionManager interface and return a session object that provides a session object to your client when it creates a session with Glacier2.

    This session object would implement a session interface which provides a method to return the proxy of your client API manager, for example:
    // Slice
    interface ClientAPISession extends Glacier2::Session
    {
         Object* getClientAPIManager();
    };
    

    You can check out the C++ Glacier2 chat demo (from the demo/Glacier2/chat directory) from the Ice 3.5 distribution for an example on how to implement a Glacier2 session manager and configure Glacier2 to use this session manager.

    Let us know if you need more information on this.

    Cheers,
    Benoit.
  • bmaranvillebmaranville Member Brian MaranvilleOrganization: National Institute of Standards and TechnologyProject: NICE instrument control
    Working but ObjectFactory exception

    Thanks for all the help - I was able to get a Glacier2 session manager running that connects me to the Java server by implementing what you suggested above.

    Now I am able to connect to Glacier2, get a session, and get a ClientAPI object from that session, from the browser.

    I'm running into an unexpected error - when I call a remote method that returns a string or a simple datatype, it works just fine, but when the return type is a structure there is an exception in the javascript - "no factory method". For example, when the return value is of type ::nice::api::data::Doublevalue as defined in the data.ice slice file...
    module nice {
    module api {
    module data {
        ...
        class Value {
        };
    
        sequence<Value> ValueArray;
        dictionary<string, Value> StringToValueMap; 
    
        /* Scalar values */
        class BoolValue extends Value { bool val; };
        class IntValue extends Value { int val; };
        class LongValue extends Value { long val; };
        class DoubleValue extends Value { double val; };
        class StringValue extends Value { string val; };
    	class DeviceStateValue extends Value { DeviceState val; };
    	class SansSampleModeValue extends Value { SansSampleMode val; };
    	class CountAgainstValue extends Value { CountAgainst val; };
    	class FixedEnergyModeValue extends Value { FixedEnergyMode val; };
    	class SampleGeometryValue extends Value { SampleGeometry val; };
    	class HklStrategyValue extends Value { HklStrategy val; };
    	class SpinEchoConfigurationValue extends Value { SpinEchoConfiguration val; };
    	class PowerSupplyStatusValue extends Value { PowerSupplyStatus val; };
    	class TimeStampValue extends Value { long val; };
        class DisplayROIValue extends Value { DisplayROI val; };
        class FlipperPositionValue extends Value { FlipperPosition val; };
        class PolarizationMeasurementTypeValue extends Value { PolarizationMeasurementType val; };
        class SampleStateValue extends Value { SampleState val; };
        ...
    

    what's strange is that the java and python clients that are created with the same slices do not have the same exception - they are able to get these values with no problem.

    I was able to hack an ObjectFactory to try to cover all the data types with this Javascript code, but what is different about a Javascript client compared to Java or Python?

    Javascript snippet:
        // ...
        var data_objs = Object.keys(nice.api.data);
        var data_types = {};
        for (var i in nice.api.data) {
            if ('__id' in nice.api.data[i]) {
                data_types[nice.api.data[i].__id] = nice.api.data[i];
            }
        }
        var MyObjectFactory = function()
        {
            Ice.ObjectFactory.call(this);
        };
    
        var data_objs = Object.keys(nice.api.data);
        var data_types = {};
        for (var i in nice.api.data) {
            if ('__id' in nice.api.data[i]) {
                data_types[nice.api.data[i].__id] = nice.api.data[i];
            }
        }
        
        MyObjectFactory.prototype = new Ice.ObjectFactory();
    
        MyObjectFactory.prototype.constructor = MyObjectFactory;
    
        MyObjectFactory.prototype.create = function(type)
        {
            if (type in data_types) {
                return new data_types[type]();
            }
           return null;
        };
    
        MyObjectFactory.prototype.destroy = function()
        {
        };
        // ...
                communicator = Ice.initialize(id);
                
                var factory = new MyObjectFactory();
                for (var i in data_types) {
                    communicator.addObjectFactory(factory, i);
                }
    
  • wconroywconroy Member Bo ConroyOrganization: Sky RoadProject: financial application
    Similar Problem

    I had a similiar issue where I was getting a "no factory method" exception. I found the issue is in BasicStream.js line 2768 (I am using version 0.1.0 on linux if that matters).

    To fix it, I replaced
    var typeId = id.length > 2 ? id.substr(2).replace("::", ".") : "";
    with
    var typeId = id.length > 2 ? id.substr(2).replace(new RegExp('\\:\\:', 'g'), '.') : "";


    More generally, I have encountered a few other problems if there are nested modules. For example, if I change Chat.ice in the Glacier2 example to be
    #pragma once

    #include <Glacier2/Session.ice>

    module Zeroc {
    module Demo {
    module Chat {

    interface ChatCallback
    {
    void message(string data);
    };

    interface ChatSession extends Glacier2::Session
    {
    void setCallback(ChatCallback* callback);
    void say(string data);
    };
    };
    };
    };

    When I build and run, I get the error
    /export/ice/IceJS-0.1.0-demos/demojs/Glacier2/chat/Chat.js:39
    Zeroc.Demo.Chat = global.Zeroc.Demo ? (global.Zeroc.Demo.Chat || {}) : {}
    ^
    TypeError: Cannot read property 'Demo' of undefined
    at /export/ice/IceJS-0.1.0-demos/demojs/Glacier2/chat/Chat.js:39:35
    at Object.<anonymous> (/export/ice/IceJS-0.1.0-demos/demojs/Glacier2/chat/Chat.js:80:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at /export/ice/IceJS-0.1.0-demos/demojs/Glacier2/chat/Client.js:14:1
    at Object.<anonymous> (/export/ice/IceJS-0.1.0-demos/demojs/Glacier2/chat/Client.js:273:2)
  • bmaranvillebmaranville Member Brian MaranvilleOrganization: National Institute of Standards and TechnologyProject: NICE instrument control
    nested namespace

    I think I had the same problem -

    I added the top names in my classes to the namespace at the start of the javascript application, to get around it...
    in my case:
    <script type="text/javascript">
        nice = {"api": {}};
    </script>
    
  • wconroywconroy Member Bo ConroyOrganization: Sky RoadProject: financial application
    Thanks, that worked well.

    Do you (or anyone) know the correct procedure to get the 2 changes needed incorporated back into IceJS? do they have a bugzilla or jira or something where I can log an issue?
  • xdmxdm La Coruña, SpainAdministrators, ZeroC Staff Jose Gutierrez de la ConchaOrganization: ZeroC, Inc.Project: Ice Developer ZeroC Staff
    Hi,

    Thanks for reporting this, we have posted a patch that you can apply to Ice for JavaScript 0.1.0.
  • wconroywconroy Member Bo ConroyOrganization: Sky RoadProject: financial application
    Thanks for the patch, I have been testing it this morning and it is working well.

    I am trying to call existing ice services deployed with a java server through javascript, so this seems like the right thread for further questions, let me know if i should move elsewhere.

    I am not using a session manager, but instead trying to just pass through glacier to access my services.

    In the Node client, I am setting up the router and getting the service proxy like this and it is working properly...
    Ice.Default.Router = Glacier2/router:tcp -h localhost -p 50000
    ServicePxy = communicator.stringToProxy('MyService:tcp -p 30000')

    When I move to the browser client, I simply changed the transport for the router to be ws like the following...
    Ice.Default.Router = Glacier2/router:ws -h localhost -p 50000
    ServicePxy = communicator.stringToProxy('MyService:tcp -p 30000')

    When i do that, I get the error "invalid endpoint `tcp -h localhost -p 30000". Is it possible to communicate to the java server without using a session manager? Or do I need to introduce that concept into what I am doing?

    Thanks,
    Bo
  • xdmxdm La Coruña, SpainAdministrators, ZeroC Staff Jose Gutierrez de la ConchaOrganization: ZeroC, Inc.Project: Ice Developer ZeroC Staff
    Hi,

    As Benoit explained above you need to use the SessionManager to obtain proxies to the Java server objects. Most probably this will be fixed for the next release.
  • wconroywconroy Member Bo ConroyOrganization: Sky RoadProject: financial application
    Thanks, I didnt understand what he was saying when I read that before, but I implemented what he suggested and now I am working properly. Thanks for the quick patches.
Sign In or Register to comment.