Here is the first post of -i hope- many posts dedicated to web development !
In this article I would like to share an experience with Exoplatform portal and ReactJS
Exoplatform comes with a frontend portlet framework called Juzu to build interactive client-side UI, but the platform allows you to use the technologies you are confortable with . My organization recently modernized its web development stack with React as a main component. So when we started our intranet project, we decided to use it in portlet development as well. If you dont know React, take a look at the site first. I’m not here to advocate React, but it’s a really good tool to manage complex UI and to easily reuse components accross applications.
You’re still there ?! OK so let’s learn how to set up a simple reactjs and nodejs development stack, build a standalone app, and then package to a portlet.
In order to efficiently manage JS library dependencies, we will use nodejs and npm. We’ll then use maven to build and package the portlet part.
OK now it’s time to open your IDE, If you dont have one try Atom. It is lightweight and opensource and works well with JS. Dont forget to install JSX plugin !
Source code is available here. Please follow instructions for building and deploying at the Portlet section.
nodejs stack and standalone application
As said, the stack is based on Node. First Install it on your system. It will come with npm, with which you can install js dependencies (works like maven).
Update npm first ! Current version of npm included in node install is 2.something… Dont use it :) Big improvement has been made in dependencies resolving : it uses flattened strucutre resulting in smaller disk usage and faster build
- Create a directory named “react-portlet” and generate a starting package.json file :
Let the default values for this time :)
- Next install the React libraries, plus another one, “moment”, to deal with date format later :
Note : react has a core part and a specific lib to render to DOM
- Install the babel transpilers as dev dependency :
- We will need the webpack library in order to gather all the JS modules into a “bundle” file, and also to do some “hot rebuild”. We’ll also install the express Http server, in order to quickly test our app in standalone mode.
At this point, package.json has been updated (with the –save argument), and node dependencies installed in “node_modules” directory. package.json must be added to version control so anyone can build your project.
- A word about project structure, it has to be maven-compliant (keep in mind we have to build a portlet!) :
/ ├─src │ ├─main │ │ ├─java │ │ ├─js │ │ ├─webapp │ │ ├─css │ │ ├─META-INF │ │ ├─WEB-INF │ ├─static ├─package.json ├─pom.xml
- Now we can create a first React component “Activities” that will fetch the user’s activities from server and transform them into html. But in order to use the modularity of react, lets also create a child Component to render a single activity. First Create the file “Activities.jsx” in /src/main/js :
the file “Activity.jsx” in /src/main/js will be :
We are using the ES6/ES2015 syntax with class inheritance. The React lifecycle in short will : init the state, mount component to DOM, fetch data from API and render.
There’s a method corresponding to each lifecycle step. For example doing fetch in componentDidMount is the safest way because of asynchronous update.
Whenever the state is modified, render method is called and check what part of the DOM has to be updated.In the example we wont do actions so state will not be modified. A good React pattern to use whenever possible, is to manage state at the highest level component (here Activities) and pass data to child components (here Activity) via the “props”. So we could replace the Activity class with the react stateless function pattern, in order to get rid of the useless lifecycle.
Did you Notice the weird attribute “dangerouslySetInnerHTML” ? React is xss proof, but sometimes you have to inject preformated html code. Do it if html has been sanitized server side :)
- Now we create the index.html in /src/static and declare the React mount tag as a simple div :
Note : we include the “bundle.js” script that will be generated with webpack.
- In order to generate the bundle, we declare a file “/src/main/js/index.js” that will import the root component of our app, here Activities :
Note : Webpack will use this file as an entrypoint from which to pack all the imports.
- Then declare “/webpack.config.js” :
Here we declare file “index.js” just created before. Then we define the ouput folder of the bundle.js. This folder is corresponding to maven target dir (it will be usefull later) Then we define the babel-loader, which takes over the transpilation part. Define Plugin is just transmitting NODE_ENV variable to loader parser, in order to prune DEV code in React lib at build time.
- Before we run the app, our static files need to be copied to target dir as well, it can be done with scripts in “/package.json” :
Note : in a real project, you should consider a library like gulp to externalize complex build tasks and be OS independant!
To call the script :
- Next we create the express http server in the file /server.js
Express will manage our standalone mode : it will serve static files (css, js, html) and proxy api requests to static files
Note: you have to record real api responses in static files before (here you can pick in source project).
- Before starting the server, we also want to start webpack in “watch mode” in order to be able to build again on any change. Add a “watch” script with the following commands in /package.json :
Now just type :
You should see something like this :
Look at the size… don’t worry it is a not optimized yet !
The map file will map source lines from the generated bundle code to the original es2015 file. It will be downloaded by browser only when you open the debugger ! Note : static files are not watched, you have to restart server. We could improve that by writing a simple gulp script for example, and add it to start script.
- Now start the server
and enjoy the result at http://localhost:3000. It should look like this :
- When you’re ready to release, you can add the following script in /package.json :
So we set NODE_ENV=production in order to disable React dev mode (it helps a lot, be its a lot slower), and started webpack with optimizers.
After optimization, the bundle will be 3 times smaller:
Note : did you notice few warnings from the optimizer ? Well, library all not always cleaned :)
Important : optimized bundle should not be too big (< 1 Mo), so for large app you can look at webpack’s code splitting feature.
Note : you can still debug in original files in production, as map file is also updated !
You’re done for that part ! You could use this app outside Exoplatform but you’ll have to adapt the proxy routes in server.js to exo backend (easy!) and deal with sso authentication (actually the hard part !)
To get started, you can look at the source code here : https://github.com/exo-samples/docs-samples/tree/4.3.x/portlet/js. It’s a simple javax Portlet that forward to an index.jsp (the view part of the portlet).
modify index.jsp and only declare a html fragment with “app” mount point :
- open webapp/WEB-INF/gatein-resources.xml to declare both the bundle.js, as a JS module, and the main.css stylesheet :
Note : These modules will be automatically loaded by Exoplatform when loading portlet.
- now the build part. When we build the portlet, it would be interesting to 1) install JS dependencies and 2) do webpack release. The maven exec plugin will do the job. In the ““/pom.xml”:
Then simply type :
This will build in dev mode (webpack.release=release-debug)
Just add a profile to build in production mode (set webpack.release=release), so :
Incompatibility with the gatein minifier
- You’ll quickly notice that Non-minified version of React cant bear with gatein minifier (Uses the google closure minifier)!
Actually the only way to disable gatein minifier is running exo in dev mode… not great :) But there’s weird thing : when you minify your bundle with “webpack -p”, gatein minifier works ! So the simplest solution is to use the minified version of our bundle.js with exo normal mode, and Non-minified in exo dev mode ! But there’s a bad news : you will loose the source mapping because of the double-minification :(
- another solution would be to disable minifier on some libs, and let us build and supply the minified and map files. Actually you can override the UIPortalApplication.gtmpl script in portal module, filter js paths and remove the “-min” when you need…but it’s tricky :) It would be great if exo/gatein come with a parameter in module definition !
- I’ve heard about webjars, it’s probably a more elegant way. Have to look at it in the future:)
Those who are used to exo can skip this part !
- Requirements : have an exo account, and a JDK 8 installed.
- Then Download the latest community edition of Exo (4.3+), unzip and launch start_eXo script.
- Simply copy the target/react-portlet.war in webapps dir and wait for deployment.
- Log into exo and create a “test” site
- Go to the site, edit the page layout and add the portlet.
You should see something very similar to standalone mode, but its now dynamic (you must have created some activities before)
Note on docker
- sorry, it doesn’t work in an exo container, unless starting it in debug mode i have to look into it
Sharing common modules :
- When you’re developing several portlets it’s legitimate to reuse some libs (like React), You may already know that gatein allow you to share modules.
- Before that, edit webpack.config.js, we have to tell webpack to gather React, ReactDOM and moment libs in another bundle, that we’re calling the “vendor” bundle :
Note : Take a look at chunking. It’s a powerfull way to optimize web app first load!
- After rebuilding, you’ll see the new size of your bundle !
- Then declare vendor-bundle.js as a shared module. To keep things simple, we declare it in our portlet, otherwise, you could package it into another war :
- Now when you look at the reactsample.js resource downloaded by portlet, it now depends on the shared module :
- We’ve learned how to set up a standalone JS app based on React and built with a nodejs/npm/es2015//babel/webpack stack. There’s a lot of choice here and you could replace some of elements of the stack : npm vs bower, es2015 vs typescript, webpack vs browserify … each has pros and cons you should be aware of before choosing.
- We’ve learned how to siply integrate npm and maven to next build a portlet on top of standalone app.
- Unfortunately, exo gatein minifier hates your react code :) even if there’s a work around, gatein should really permits lib exclusion from minifier.
- Last words : On a real project you’ll have to deal with unit testing.
Just for the record, we're currently using Mocha to write tests, Phantomjs as a runtime platform and Istanbul as a coverage tool. In order to manage complex build tasks you should use a lib like Gulp or Grunt.