Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedded XForms Server for mobile and offline clients #1221

Open
ebruchez opened this issue Aug 27, 2013 · 31 comments
Open

Embedded XForms Server for mobile and offline clients #1221

ebruchez opened this issue Aug 27, 2013 · 31 comments

Comments

@ebruchez
Copy link
Collaborator

@ebruchez ebruchez commented Aug 27, 2013

Status

The purpose of this RFE is to gather ideas and track progress of an Orbeon Labs investigation project. The purpose of the project is to make the Orbeon Forms forms engine available on clients, including online and offline web browsers and mobile applications.

As of October 2016, there has been some progress on this project, including:

  • conversion of our DOM library to Scala
  • build modularization
  • experimentation with XPath 2 in the browser

We expect to continue progress on this project incrementally.

Subprojects, dependencies, and related projecgts

  • XPath 2 processor/API with JVM and Scala.js support #2127
  • XPath: replace reflective calls with native functions #2214
  • Don't use dom4j XPath APIs #2164
  • Migrate to a minimal version of dom4j #2165
  • Complete conversion of XForms engine to Scala #2825
  • Requirements for an offline iOS/Android app #1545
  • Mobile/offline Form Runner UI

Historical note

With Orbeon Forms 3.7 (back in 2009!), we had an implementation which wasn't bad in concept, but was too limited and based on Google Gears (the precursor to HTML5 local storage among other things). Due to those limitations, Google abandoning Gears and lack of interest from customers at that time, we decided not to maintain it anymore starting Orbeon Forms 3.8. See:

Rationale

The idea is to bring our forms engine to Web apps and "native" mobile apps. This would serve multiple purposes:

  1. supporting offline form management and data entry with a Web app
  2. supporting offline form management and data entry from with a native phone/tablet app
  3. generally having more of the form processing handled on the client, as client capabilities improve

The general idea is to make the code which currently runs on the server run on the client, whether within a Web browser directly or as a native application on a mobile device.

We need to abstract and modularize the XForms engine to make it as small as possible and as independent as possible from other things including the servlet API.

The user-interface part would remain browser technology-based. We don't want to change this at all at first and we wants to leverage either an actual Web browser or a Web view within a native app.

One components that we need to have is some kind of abstraction for the client/server communication. Right now that communication is handled via Ajax in the JavaScript code and we have the XForms server on the server. What we would like to do here is allowing the option to have something local like at simple queue. That doesn't seem to be extremely hard to do.

Platforms

General considerations

  • We plan to compile our code to JavaScript with Scala.js.
    • it is no longer experimental as of 2015
    • it can run in a WKWebView as of iOS 8
  • We have a mix of Java and Scala code and we plan to migrate the Java code that is needed to Scala. (#2825)
  • We are ruling out the use of GWT, which is a tool chain made obsolete (for us) by Scala.js.

Web app

For a web application we have to compile our code to JavaScript. For offline support we can use modern browser APIs.

Native iOS app

The current plan is to use JavaScript as well. In the future, and if it matures and gains the appropriate bindings, Scala Native could become an option as well.

HIstorical considerations

We also considered these options, but have opted not to use them:

  1. Scala -> scalac -> IKVM -> Xamarin.iOS -> ARM x86 assembly
    • iKVM is stable
    • MonoTouch is stable (?)
    • uses optimizing compiler (I think)
    • drawback: painful toolchain
    • benefit: might be the best performance possible as of 2013
    • opens the door of Microsoft phones/tablets as well
  2. Avian VM
    • promising (2013-08-30 update: Scala runs fully)
    • JIT/AOT compilers and GC performance still poor

Abandoned projects, for reference:

  1. RoboVM compiles Java bytecode to native code (project/product is dead)
  2. ScalaGWT
    • run in WebView
    • no JIT in WebView probably
  3. LLVM backend
  4. J2ObjC from Google
    • compiles Java source to Objective-C
    • see also Going under the hood of Inbox
    • would not work for us almost half of our server-side code is in Scala

Native Android application

Android already supports Java bytecode. This means we have two options:

  1. Compile our Java/Scala code to Android.
  2. Compile our Scala code to JavaScript as in the case of the browser and iOS.

We would need to use a WebView probably and then we would need to interact between the application and the WebView. On Android the JavaScript side of a WebView is able to call a Java object that has been exposed.

Modularization effort

The Orbeon code needs to be modularized and abstracted. In particular:

  • client-server connection
  • temporary files
  • form compilation vs. form execution
  • HTTP client for submissions
  • etc.

Subsequent steps

Once a prototype is developed, we needs to think about the general architecture of the mobile application, including lifecycle, how to load forms and so on. Form Runner needs to be included, and local storage at least needs to be used.

@ebruchez ebruchez added this to the Review milestone Jun 26, 2014
@avernet avernet removed this from the Review milestone Oct 17, 2014
@ebruchez ebruchez removed this from the Review milestone Oct 17, 2014
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Jan 13, 2015

Steps for a prototype

Strategy

As of 2015-01-13, the plan to use Scala.js. This way a single compilation can be used:

  • in standalone web browsers
  • in native apps via web views

This also allows us to master Scala.js, which we want to use more for Form Builder (and later Form Runner).

First goals

We are trying to define reasonable chunks of work which lead us somewhere. First goals:

  1. run some of our code in the browser
  2. run an XForms hello world prototype in the browser

It is ok to hack to get to these goals. The purpose is to get familiar with the tool chain, potential issues, and learn more about what remaining work will be needed.

Known challenges

  • Java code/libraries don't compile with Scala.js right now
    • XPath 2 processor (#2127)
    • dom4j (#2165)
    • joda-time/java-time
    • Apache commons
      • commons-codec
      • commons-io
      • lang3 (#2944)
    • our own Java code (which we will migrate) (#2825)
  • handling of uploads will be different
  • handling of submissions will be different
    • 2016-09-16: Noting RosHTTP which is 100% Scala and works on the JVM, Node.js and browsers. (not maintained)
      • 2020: can use Fetch API directly
  • modularize/refactor
    • we only want to pull in a small subset of our code

Things the client must not need

  • XSLT 2
    • later dynamic XBL might require a solution, preferably NOT using XSLT
  • XPL and processors
  • eXist, iText, Flying Saucer
  • most other XML processing code (MSV, XML Schema validator)
    • XML Schema validation could be added at a later time
  • Java libraries: httpclient, ehcache
  • Java logging libraries (need some kind of wrapper)
  • at first: no HTML handlers
    • client receives always same HTML "template" from server
    • so no full updates at first

General architecture

  • XForms
    • what we really want to run
      • XFormsContainingDocument, model, control tree, control comparator, and related
      • so basically the core XForms engine
    • client takes
      • HTML page produced by server
      • serialized version of static state
        • could be XML or JSON or anything that can rebuild ElementAnalysis trees
        • this way complex XBL logic, XSLT etc. remains on server
    • client initialization
      • all controls are non-relevant
      • initial show does a diff with fully non-relevant tree (null)
    • client operation
      • state handling is changed
        • static state
          • explicitly kept, as it represents the "form definition taken offline"
        • dynamic state
          • app is not multi-user
          • there could be only one such dynamic state at any given time
          • later, we could support switching between form sessions, keeping a number of them
  • Form Runner
    • persistence
      • use some kind of local storage/db on the phone

Q&A

  • Q: Why wouldn't we compile the form on the client too? A: Form compilation right now requires XML, SAX, and XSLT 2, in particular for some key XBL components. We can't rule this out in the future, but for now it doesn't seem unreasonable to say that forms are compiled on the server, and that the client gets HTML + serialized data structures that it needs.
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented May 14, 2015

form-runner

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Jul 29, 2016

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Oct 5, 2016

@ebruchez ebruchez added the Top RFE label Oct 5, 2016
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Oct 12, 2016

Immediate concrete next steps:

  • create an XForms module in the build (we have progressed a lot on build modularization already) (#3292)
  • convert at least P1 classes to Scala (#2825)
  • after that: determine next step for further modularizing XForms engine
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Nov 4, 2016

+1 from evaluator, with a preference for a web app using browser storage, as native apps can be more challenging to deploy.

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Dec 20, 2016

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Jul 19, 2017

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 7, 2018

@siebertm
Copy link
Contributor

@siebertm siebertm commented Oct 31, 2018

We as a customer would also +1 that. Just the form runner, right?

@avernet
Copy link
Collaborator

@avernet avernet commented Oct 31, 2018

@siebertm Correct: this feature is specifically for forms created with Form Builder and running in Form Runner.

@siebertm
Copy link
Contributor

@siebertm siebertm commented Apr 29, 2020

@ebruchez that's a large chunk of work and i know this is rather far out but if you're at the point you've got an MVP or so to test out, ping me! I really want to try this in our real use cases!

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Aug 10, 2020

The initial refactoring requires separating the following:

  • form runtime
  • form compilation
  • submission module
  • client/server communication
  • TODO
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Aug 14, 2020

  • eventually: two separate subprojects, for example xforms-runtime and xforms-compiler
  • XFormsContainingDocument → Scala
  • static state: separare construction from data structures
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Aug 19, 2020

We will need the handlers offline to support instantiating new forms offline. Right now, this works as follows:

  • take the SAXStore with the template
  • replay this through the handlers system'
  • the output is SAX events
  • that's then serialized

So we need a way to do this when offline as well. We probably need to have SAXStore and some of the SAX API for this.

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Aug 19, 2020

Splitting this into modules/projects is not that trivial. We not only have the runtime/compile-time axis, but the "thin client" (what we have now)/full client axis, and the JVM/JS/shared axis.

For example we don't want the "thin client" to pull down the full client. Scala.js prunes the call tree but it's not perfect at doing that, probably, so it's better if we have a clean separation of the "full client" module, which would contain mainly the XForms runtime.

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 2, 2020

How static state construction works:

  • XFormsStaticStateImpl.restore / .createFromStaticStateBits / .createFromDocument (tests)
  • new PartAnalysisImpl
  • topLevelPart.analyze()
    • new RootControl
    • recursive call to build()
    • final hooking up
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 2, 2020

Client-server communication abstraction:

  • AjaxClient must be abstracted or call to a common interface
    • currently this depends on JS
  • we probably keep the event queue in all cases
  • we need a shared event representation ideally (so later, the client-server protocol can use those too)
    • currently AjaxEvent depends on JS
  • ClientServer still extracts information from Element
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 11, 2020

So for the client-server, we now have:

  • ClientServerChannel
  • WireAjaxEvent
  • The response is still XML and will have to be updated as well.

For XFormsModelSubmission:

  • RegularSubmission uses Connection, which uses PropertiesApacheHttpClient and InternalHttpClient
  • neither client is appropriate when offline
  • but when running fully in JS, something like a FetchHttpClient would be good, although it would have to be async
    • See "Switch the actions implementation to use async submissions #4077"
  • when fully offline, we should have an OfflineHttpClient, configurable to call back JavaScript functions

So probably that we don't need to modify much XFormsModelSubmission. One approach:

  • make Connection's choice of HttpClient pluggable
    • on the JVM, use PropertiesApacheHttpClient and InternalHttpClient
    • on JavaScript, use OfflineHttpClient
  • remove a few dependencies
    • org.apache.http.client.CookieStore/Cookie
    • etc.

Another approach is to make Submissions pluggable, and on JavaScript:

  • remove RegularSubmission, and possibly others
  • instead, use a new OfflineSubmission

The second approach might be easier as it's not pulling in Connection.

@siebertm
Copy link
Contributor

@siebertm siebertm commented Sep 14, 2020

My 2 cents on this:

when fully offline, we should have an OfflineHttpClient, configurable to call back JavaScript functions

Why not normal fetch/XHR which are then caught in a ServiceWorker? This generates less special cases.

Depending whether this must support IE, of course. But, even we (working with german hospitals) are phasing out IE support...

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 14, 2020

@siebertm Thanks for the feedback.

Why not normal fetch/XHR which are then caught in a ServiceWorker? This generates less special cases.

The main reason for a JavaScript callback API is embedding in native mobile applications which can then provide their own persistence and service support. It's also very easy to implement on both sides.

For regular online/offline mode in a browser then yes, Fetch support is needed.

Depending whether this must support IE, of course. But, even we (working with german hospitals) are phasing out IE support...

Agreed, and IE support is not a requirement at this point.

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 23, 2020

For the record, we have now made Connection implement ConnectionTrait, with two implementations (JVM/JS). The JS implementation is not done yet.

More work:

  • CacheableSubmission depends on XFormsServerSharedInstancesCache
  • provide implementation of Connection based on API callbacks and/or Fetch
@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 24, 2020

Which parts of the static analysis must not be present in the ElementAnalysis tree?

  • XPath analysis
    • this is done lazily (I think) in SimpleElementAnalysis
    • we could do it on the client, I guess, but we shouldn't need to
  • calls to ComponentControl.setConcreteBinding, which uses XBLBindingBuilder
    • this is the big one!
    • ShadowChildrenBuilder, which is implemented by ComponentControl, and uses binding.compactShadowTree

So we should probably separate the "builders": ChildrenBuilderTrait, ShadowChildrenBuilder, etc.

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 24, 2020

So now we are refactoring out all the building of the tree. This will take care of XBLBindingBuilder and others.

However we need to solve the question of how to pass in the XPath analysis information. We do this after we have built the tree:

  • rootControlAnalysis.analyzeXPath() // Analyze root control XPath first as nested models might ask for its context
  • analyzeModelsXPath() // Analyze all models XPath
  • analyzeControlsXPath() // Analyze controls XPath

For deserialization, the information should be passed to the constructor. So maybe the constructor of the classes should have the XPath analysis information as vars set to None, but then the vars are updated before serialization. Upon deserialization, the proper information can be passed.

@ebruchez
Copy link
Collaborator Author

@ebruchez ebruchez commented Sep 25, 2020

Next step is to move towards serialization/deserialization. This includes:

  • ElementAnalysis hierarchy
  • Scope
  • Metadata: must include Mark, NamespaceMappings (but no IdGenerator, XBL index, etc.)

We can use case classes with vars, or semi-automatic, or manual serialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.