This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information

Eclipse Oxygen.2 with ANT and JRE6

The support for ANT and JRE6 with Eclipse Oxygen.2 (December 2017).

The plugin was created for:

Version: Oxygen.2 Release (4.7.2)
Build id: 20171218-0600

Don't forget the -clean start (read the original article for more details)!

One more thing you should do:

Oxygen.1 and Oxygen.2 contain ANT 1.10.1. This version requires Java 8. In order to use Java 6, you have to download ANT 1.9.x. Simply download ANT and set the ANT Home to your version, via

Window >> Preferences >> ANT >> Runtime >> Ant Home...

If you try to use the bundled ANT with JRE 6, it will fail with an Exception.

Download the plugin from here. It works for us - no warranty!

More Details: Eclipse Oxygen.1 with ANT and JRE6

More about Application styling

We showed you the upcoming CSS edit feature of VisionX. This feature was used in our new tutorial Style Invoice application.

The standard Invoice application was more or less a standard VisionX application with some standard colors and icons. Not super fancy.

The Styled Invoice application demonstrates how easy it can be to apply your own Style to an application, created with VisionX.

The new tutorial is available in our documentation system.

The comparison of both applications

The standard application:

Standard Invoice application

Standard Invoice application

The styled application:

Styled invoice application

Styled Invoice application

VisionX CSS Styling feature

The upcoming release of VisionX will support CSS styling. We'll show you a first impression with a short video:

Vaadin AddOn for VisionX

The editor supports autocompletion and syntax highlighting:

VisionX CSS Editor

VisionX CSS Editor

We did integrat the RSyntaxTextArea for this. If you want to know how this works, check the documentation.

Screen Embedding AddOn for VisionX

We have a new AddOn for all VisionX users. It's the Embedding AddOn.

The new AddOn makes it possible to embedd one work-screen into another work-screen. Simply re-use screens in other screens.

Get an impression

Vaadin AddOn for VisionX

The AddOn is commercial and not included in VisionX. Contact our sales team to get more information!

Documentation redesign

Some years ago, we put all our documentation into our Forum. This wasn't a perfect system for our documentation, but it was in one place. But now we know that the Forum was a bad idea for framework and product documentation.

Last year, we started an evaluation for a new documentation system and were shocked about the quality and features of available solutions. We found a SaaS solution but we didn't want to store our documentation elsewhere. After some research we made a decission and our new documentation system was DokuWiki. The system itself is very nice but the standard theme is not pretty. But it's pluggable and there are many nice looking themes and very useful plugins.

After some customization we were very happy with the result and the migration from our Forum to the DokuWiki was not a hard task. It's still in progress, but we're happy to announce that our new docu system is available for you!

The new link is https://doc.sibvisions.com/

The new system contains some many information about VisionX as well. We didn't publish this information in the past.

Have fun with our new documentation system!

Our Forum will be continued, but only as place for questions and answers.

DokuWiki XMLRPC with Java

DokuWiki

is a simple to use and highly versatile Open Source wiki software that doesn't require a database.

It's a really nice software for your documentation. The default theme isn't super modern, but it works. There are many custom themes available and styling is simple.

The really cool thing is the remote API of DokuWiki. It has a XML based RPC interface. Here are some details.

The documentation looks promise. There's a Java example :)
But the last update of the Java client was 2016 :(

After downloading and testing the client, it was clear that it doesn't work. It had a problem with cookie management. I tried to fix the problem in the base lib - aXMLRPC but it was tricky. The problem was the standard Http URL connection. After replacing it with Apache HttpClient, the dokujclient was working without problems.
I didn't try to find the root cause of the problem because it wasn't worth the time. It was faster to replace the communication part.

Here's a link to our patch and all required libraries (pre-built).

Here's a short snippet

DokuJClient client = new DokuJClient(url);
client.login(username, password);

client.putPage(pNamespace + ":" + pPageName, sText);

FileSearch fs = new FileSearch();
fs.search(dirImages, true);

for (String file : fs.getFoundFiles())
{
    client.putAttachment(pNamespace + ":" + pPageName + ":" + FileUtil.getName(file),
                         file, true);
}

client.logoff();

We use the remote interface for automatic article updates. It works great!

JVx 2.7 is available

We're happy to announce that JVx 2.7 is available. It's a bigger bugfix release with a small number of new features.

What's new?

  • Tibero Database Support
  • boolean support

    Our DBAccess implementation now supports boolean as parameter.

  • SimpleJavaSource

    It's now possible to create java objects from java source code, see our REST interface

  • Struct support

    Struct support for Oracle DB in procedures and functions.

  • Session timeouts

    We fixed the problem of session timeouts with long up/downloads.

  • WorkScreen

    It's now possible to open a work-screen with additional parameters. A new (optional) Parameter class is available.

  • Mac OS

    We fixed the problems with internal frame borders.

  • Many improvements

The full changelog is available here.

Start with JVx

VisionX, JVx and native Vaadin

If you create an application with VisionX, it's always a JVx based application. You get all advantages of JVx and its GUI independency, but sometimes the GUI indepency is not important and you want to use native GUI controls in your JVx application because JVx doesn't contain the GUI control or you need a commercial control. This is a very simple use-case and it's not a problem to mix JVx components with native components. We have different examples, with different GUI technologies, for this use-case.

Here are some links:

But we don't have a link for our Vaadin implementation. But no problem, here it is!

Simply create a new application with VisionX and a dummy screen like this one:

Simple screen

Simple screen

The screen contains a simple table and two editors. Nothing special. Now we want to add a custom Vaadin component in the empty space. It doesn't matter which component you use. Every vaadin component or AddOn component can be used. JVx doesn't do specific things, it's only an UI abstraction layer.

So, lets add an Accordion to the screen:

Integrated Accordion

Integrated Accordion

The Accordion component is a standard Vaadin component, simply added to the screen. But more... Do you see the "Show Vaadin Notification" Button? This is a standard JVx component. So we mix native vaadin components with standard JVx components and get the full power of both. One advantage of the JVx components is that the automatic translation works without additional hacks, or what about JVx' layouts, event handling, ...

Interested in the source code?

No worries, it's super simple to understand :)

Lets have a look at the custom code:

UIButton butNotification = new UIButton("Show Vaadin Notification");
butNotification.eventAction().addListener(new IActionListener()
{
        public void action(UIActionEvent arg0) throws Throwable
        {
                Notification noti = new Notification("Message",
                                                     "Description", Type.WARNING_MESSAGE);
                noti.setDelayMsec(2000);
                noti.show(Page.getCurrent());
        }
});

UIFormLayout flJVxPanel = new UIFormLayout();

UIPanel panJVxPanel = new UIPanel();
panJVxPanel.setLayout(flJVxPanel);

panJVxPanel.add(butNotification);

if (getApplication().getLauncher().isWebEnvironment())
{
        Accordion acc = new Accordion();
        acc.setHeight(100.0f, Unit.PERCENTAGE);

        for (int i = 1; i < 8; i++)
        {

                if (i >= 2)
                {
                        final Label label = new Label("Welcome sheet!", ContentMode.HTML);
                        label.setWidth(100.0f, Unit.PERCENTAGE);

                        final VerticalLayout layout = new VerticalLayout(label);
                        layout.setMargin(true);

                        acc.addTab(layout, "Tab " + i);
                }
                else
                {
                        groupPanelOverview.add(panJVxPanel);

                        acc.addTab(((Component)panJVxPanel.getResource()), "Tab " + i);
                }
        }

        groupPanelOverview.add(new UICustomComponent(acc), formLayout1.getConstraints(0, 2, -1, -1));
}

The Accordion source code was copied from Vaadin Sampler:

sample = new Accordion();
sample.setHeight(100.0f, Unit.PERCENTAGE);
 
for (int i = 1; i < 8; i++) {
    final Label label = new Label(TabSheetSample.getLoremContent(), ContentMode.HTML);
    label.setWidth(100.0f, Unit.PERCENTAGE);
 
    final VerticalLayout layout = new VerticalLayout(label);
    layout.setMargin(true);
 
    sample.addTab(layout, "Tab " + i);
}

So, what are the most interesting parts in our code?

First, we add the JVx panel to another JVx panel. This is important to get support for translation. If it's not important for your, simply ignore the line:

groupPanelOverview.add(panJVxPanel);

The groupPanelOverview is a simple UIGroupPanel with UIFormLayout:

UIFormLayout formLayout1 = new UIFormLayout();

UIGroupPanel groupPanelOverview = new UIGroupPanel();

groupPanelOverview.setText("Overview");
groupPanelOverview.setLayout(formLayout1);

groupPanelOverview.add(labelName, formLayout1.getConstraints(0, 0));
groupPanelOverview.add(editOverviewName, formLayout1.getConstraints(1, 0));
groupPanelOverview.add(labelDescription, formLayout1.getConstraints(0, 1));
groupPanelOverview.add(editOverviewDescription, formLayout1.getConstraints(1, 1, -1, 1));

Our JVx button will be added to the native vaadin Accordion with following code:

acc.addTab(((Component)panJVxPanel.getResource()), "Tab " + i);

We don't add the JVx component itself, we use the wrapped resource. This is a simple vaadin component: com.vaadin.ui.Button

And finally, we add the Accordion as custom component to our JVx group panel:

groupPanelOverview.add(new UICustomComponent(acc), formLayout1.getConstraints(0, 2, -1, -1));

This code:

if (getApplication().getLauncher().isWebEnvironment())

is important for VisionX because the vaadin components aren't available in Swing, so we use this check for the supported environment.

So far, we mixed native vaadin components with JVx components. It's super easy to use, isn't it?

But it's also possible to use CSS for JVx components:

Css style for Button

Css style for Button

The button got the friendly style, which is defined in Vaadin CSS. Check some examples.

To add the friendly style class to the JVx button, simple add:

Style.addStyleNames(butNotification, "friendly");

This example is using predefined CSS from vaadin. It's also possible to set custom styles in your own css file. Simple follow this instructions.

This article covered the integration of native vaadin components into an existing JVx application with all advantages of vaadin.

JVx REST interface update

Our generic REST interface of JVx got an update.

The REST services are a powerful feature of JVx and built-in. Usually you would implement your own REST services, but JVx has the concept of lifecycle objects and the powerful action mechanism. It would be ugly to implement another layer on top of JVx just for REST services. This is a framework feature.

We had this powerful feature for a long time and it is still in use for different use-cases:

AngularJS 4 with VisionX and JVx REST services
AngularJS with JVx in action

Our REST interface just works and you are able to create microservices very fast and efficient.

But the interface has a problem with some kind of parameters because they are also generic. Suppose you have the following method:

public IFileHandle createReport(ICondition pFilter, SortDefinition pSort)

The result type (IFileHandle) isn't a problem because JVx will send the bytes in the REST response, but the parameters: pFilter and pSort are specific types and not base types like String, Array, int, float.

It wasn't possible to call such methods without wrapper methods like:

public IFileHandle createReport(String pFilter, String pSort)
{
   return createReport(createCondition(pFilter), createSort(pSort));
}

Still not hard to solve, but parsing the filter and sort definition weren't trivial tasks. In fact, it was annoying.

We now have support for such parameters and much more. It's not super modern but an awesome solution!

Assume you have following code in one of your lifecycle objects:

public DBStorage getAdrData() throws Throwable
{
        DBStorage dbs = (DBStorage)get("adrData");
       
        if (dbs == null)
        {
                dbs = new DBStorage();

                dbs.setDBAccess(getDataSource());
                dbs.setWritebackTable("ADDRESS");
                dbs.open();
               
                put("adrData", dbs);
        }
       
        return dbs;
}

The DBStorage class offers a public method:

public IFileHandle createCSV(String pFileName, String[] pColumnNames, String[] pLabels,
                             ICondition pFilter, SortDefinition pSort) throws Exception

This method creates a CSV report (address data in this example). It has some parameters for the expected filename, optional column names which should be used for the report, optional labels for the column names, the filter condition and sort definition.

To call this method as REST service, simply send a post request with following information:

e.g. URL:

http://localhost/jvx/services/rest/demo/Address/object/adrData/createCSV

(demo is the application name, Address is the lifecycle object name, adrData is the object name)

Body:

["test.rtf", null, null,
 "{j:new LessEquals('NR', 10)}",
 "{j:new SortDefinition(new String[] {'NR', 'STAIR'}, new boolean[] {true, false}}"]);

The body contains Java Code :) (in the JSON string).

W00t?

JVx got support for executing simple Java code. We introduced the new utility class SimpleJavaSource. It's a backport from our commercial product VisionX. The SimpleJavaSource is able to execute the given parameters and creates real Java objects. It doesn't support conditions or loops, so it's really simple - but powerful!

With our new SimpleJavaSource and the support for parameter Code, it'll be possible to call most methods without additional wrapper methods.

The SimpleJavaSource class is now included in JVx and ready to use. It's simple but powerful:

SimpleJavaSource ssj = new SimpleJavaSource();
ssj.addImport("javax.rad.model.condition.*");

String sEqualsCode = "new Equals('ID', 0).or(new Like('NAME', '%john%'))";

ICondition cond = (ICondition)ssj.execute(sEqualsCode);

The new features will be available with our next JVx release and are already available via nightly builds.

AngularJS 4 with VisionX and JVx REST services

Some time ago I wrote articles about my "web-technology" tests. The first article covered AngularJS with JVx in action. It was a simple list view for contacts, had an Edit form and was based on AngularJS 1.4. The second article was about Oracle JET with JVx. The result was another list view with contacts, without Edit form.

Both solutions were more or less a demonstration for the automatic REST interface of JVx but not a real technology PoC.

The second article about Oracle JET was written in Feb 2016 and the first, about AngularJS, was written in July 2015 - long time ago. Currently, Angular 4 is available and Angular 5 is coming. Big version jumps with big API changes and many new features.

Last week, I thought it might a good idea to start a new evaluation of AngularJS. One week later, I think different... but one step after another.

My plan was the creation of a very simple application with some enhanced features, like routing, CRUD, styling, custom components, REST with authentication, Deployment. This was the base idea because I read really cool marketing articles about Angular and thought it might be easy to create a simple application with expected features.

So, what did I do to start?

The Angular tutorial was great for a jump start. You'll build a smart heroes application with a small dashboard. Sure, it's not a real world application but has everything on board and hey, the Tutorial will show how to integrate REST services. I thought it might be an easy task and won't last more than 1 day!

The tutorial has 7 chapters and the last one integrates REST. The chapter 1 was easy, because it was the instruction :) The second chapter contains the setup process which should be trivial, because I manually tried to create an AngularJS 4 application from scratch and it was super simple. But the setup for the tutorial wasn't easy because the creation of a new project from scratch contained too many files and the expected project layout (shown in the tutorial) was reduced to a bare minimum. It wasn't possible to delete all additional files because the project preview in the browser didn't work afterwards. So I decided to download the example archive.

This was the right decission because everything was pre-configured :) First lesson learned: Project configuration is not easy and woha, many different config files.

It was super easy to follow the tutorial and chapters 3, 4 were relative short and fast to do. Starting with chapter 5, the whole thing was starting to get complex because Service integration, Routing and HTTP(REST) were complex things. The chapters 5, 6 and 7 are very long and boring because you have to do so many things (hey, it's a tutorial and it's good as it is!).
No worries because I'm an "expert" and so I jumped to Chapter 6 and downloaded the finished example archive. The example was working without problems in my test environment :) but it was without support for HTTP/REST calls because I thought it might be a good idea to integrate the features based on the tutorial on my own.

So the next step was the integration of my own REST service instead of the dummy service in the tutorial. But first... I needed a REST service.

The creation of a web service was super easy because of our Low Code Development Platform - VisionX. It creates JVx based applications with all gimmicks. I did create a simple Heroes application with VisionX. The application has one screen:

Heroes app created with VisionX

Heroes app created with VisionX

The application was ready to use in 1 minute and had one database table:

Heroes table

Heroes table

The heroes table contains the columns: ID and NAME. Thanks to JVx, the REST service was ready-to-use. The request: http://localhost/services/rest/Heroes/HeroesWorkScreen/data/heroes

returned the JSON string with my test data:

[
  {
    "ID": 1,
    "NAME": "Superman"
  },
  {
    "ID": 4,
    "NAME": "Rambo (John)"
  },
  {
    "ID": 6,
    "NAME": "Wolverine (X-Men)"
  },
  {
    "ID": 2,
    "NAME": "Batman"
  },
  {
    "ID": 5,
    "NAME": "Captain America"
  },
  {
    "ID": 3,
    "NAME": "ANT MAN"
  }
]

So, my backend was ready.

The Next step was the integration into my Angular frontend. It was easy to find the right source file for the integration, because the hero-service was encapsulated. The first problem was that my REST service required authentication and the service implementation didn't use authentication. I had the same problem with missing authentication support in Angular 1.4 but found some useful things on stackoverflow. With Angular 4 everything has changed because you code Typescript and the API is different to 1.4, so my old solution didn't work. I tried to find something in the Internet but had a lot of problems because most help was for Angular 2. And I found so many github issues for http authentication and most issues had tons of comments... frustrating and not effective.

I tried to search in the official Angular documentation and found some hints about HttpClient in the online guide. But I didn't know HttpClient because my example was created with HttpModule. Not helpful that the example from the tutorial is different to the official documentation.... or.... I didn't read the details.... or.... I'm a bad developer.... or (and this is a fact) my Javascript/Typescript know-how is not enough. But anyway, it was not a standard task to add authentication to my hero-service.

I'm not 100% sure how I solved the problem, but I guess I read the API doc about http and an information about the new Angular2 HttpModule (means that HttpModule is old and HttpClient is new?). I googled around for sime time and tried a lot of hints, but it was painful. So much documentation about such a simple problem and no concreate solution for the http authentication problem.

Here's my solution:

export class HeroService {
 
  private heroesUrl : string;
       
  private options : RequestOptions;
  private headers : Headers;
       
  constructor(private http: Http)
  {
    this.headers = new Headers();
    this.headers.append('Accept', 'application/json')
    this.headers.append('Content-Type', 'application/json');
    this.headers.append('Authorization', 'Basic ' + btoa("admin:admin"));
         
    this.options = new RequestOptions({ headers: this.headers, withCredentials: true });

    this.heroesUrl = 'http://localhost/services/rest/Heroes/HeroesWorkScreen/data/heroes';
  }

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl, this.options)
                    .toPromise()
                    .then(response => response.json() as Hero[])
                    .catch(this.handleError);
  }

}

I know that username and admin are hard-coded but it's a simple test application without login. It's not really a problem to add a login form if needed and to replace the hard-coded values with variables.

After I solved the authentication problem, and everything was working in my test environment, I tried to continue with tutorial chapter 7 and tried to add new features like Adding new heroes, Deleting heroes. This was really straight forward and worked without problems, even with authentication.

Yuhu. After two days I had a working Angular 4 application which reads data from a REST service and offers CRUD. A simple routing was available and I had custom components, styling and was happy. I thought the next step should be a test deployment. Sounds easy and I thought it couldn't be a problem... but... deployment hell.

Why is Angular deployment so complex?

The documentation has a lot of information about deployment. The simple deployment is really straight-forward because it's enough to copy/paste your development environment. But this is not recommended for production environments because of performance and many other things. So, i tried to deploy a production ready application and not my development environment... And the nightmare started.

I found a super simple deployment guide for Angular 4. First problem: didn't work for my example project because:

Unable to find @angular/cli in devDependencies

Please take the following steps to avoid issues:
"npm install --save-dev @angular/cli@latest"

After installing the missing module, the next problem:

Cannot read property 'config' of null
TypeError: Cannot read property 'config' of null

Also no problem because google found that my project needs an additional config file:

.angular-cli.json

I found this file in my demo Angular application, created from scratch. I tried to edit and adapt the file for my heroes application and the next problem came up:

ENOENT: no such file or directory, stat '...\angular-tour-of-heroes\src\tsconfig.app.json'

No problem, same solution as before (copy/paste/change).

Hurray, the command:

ng build -prod

was successful!

The dist folder contained my production ready application with 6 files! Great.

The deployment to my Tomcat application server was also super easy, because VisionX is able to create war files with a single mouse click. I changed the created war file and put the built Angular files in the root directory and deployed the war file.

No surprise, the application didn't work because some javascript files weren't found. The developer console of Chrome browser was a big help!

So, what should I do?

No... not Google. I read the development guide and found the solution in section Load npm package files from the web (SystemJS).

So, I changed my deployment and replaced node_modules in my index.html with https://unpkg.com/. This solved one problem but the developer console showed more 404 errors. I had to remove following:

<script>System.import('main.js').catch(function(err){ console.error(err); });</script>

and

<link rel="stylesheet" href="styles.css">

I also had to replace the node_modules path in systemjs.config.js with https://unpkg.com/. Oh, and the file was missing in dist folder. I decided to follow the deployment guide and created a file with the name systemjs.config.server.js. If you do this, the index.html needs a changed mapping.

I didn't replace the values by hand. Here's my ant build file:

<project name="AngularJS" default="start.complete">

  <!--
 *****************************************************************
 * information
 *****************************************************************
 -->

  <description>Angular JS deployment</description>

  <!--
 *****************************************************************
 * tasks
 *****************************************************************
 -->

  <target name="start.complete" description="Prepares production files">

    <property name="heroes" location="D:/dev/other/angularjs/angular-tour-of-heroes" />
    <property name="dist"   location="${heroes}/dist" />

    <replace file="${dist}/index.html" encoding="UTF-8">
      <replacetoken><![CDATA[<link rel="stylesheet" href="styles.css">]]></replacetoken>
      <replacevalue><![CDATA[]]></replacevalue>
    </replace>

    <replace file="${dist}/index.html" encoding="UTF-8">
      <replacetoken><![CDATA[<script>System.import('main.js').catch(function(err){ console.error(err); });</script>]]></replacetoken>
      <replacevalue><![CDATA[]]></replacevalue>
    </replace>

    <replace file="${dist}/index.html" encoding="UTF-8">
      <replacetoken><![CDATA[node_modules/]]></replacetoken>
      <replacevalue><![CDATA[https://unpkg.com/]]></replacevalue>
    </replace>
   
    <replace file="${dist}/index.html" encoding="UTF-8">
      <replacetoken><![CDATA[<script src="systemjs.config.js"></script>]]></replacetoken>
      <replacevalue><![CDATA[<script src="systemjs.config.server.js"></script>]]></replacevalue>
    </replace>
   
    <copy file="${heroes}/src/systemjs.config.server.js" tofile="${dist}/systemjs.config.server.js" />
       
  </target>

</project>

With above changes, it was possible to use my application with my tomcat application server. But I had two more problems!

First, my REST service URL was wrong because it was set for my dev environment.
Second, the browser reload of URLs didn't work because the application server returned 404 for e.g. https://cloud.sibvisions.com/heroes/heroes/3

Two more problems, omg.

But clear, the REST service URL can't be the same in the prod environment. I saw that Angular comes with support for environments and thought this would solve my first problem.... No way!
The problem was that the environment integration worked for build time, but not for development. It wasn't possible to import the environment constant in my Typescript files. It wasn't possible beause the file wasn't routed correctly and I didn't find a solution for this problem. The dev server returned 404 for the access to ../environments/environment.js. I tried to find a solution for more than one day, but gave up. I found out that the config files of an Angular application are evil. You have so many options and have absolutely no idea what's right. This is a common problem if you search the internet and it's a good idea to use a pre-created configuration. But the configuration of the heroes application was different to a newly created configuration. So I reverted all my environment changes and decided to follow the official documentation. This code is the clue:

if (!/localhost/.test(document.location.host)) {
  enableProdMode();
}

It doesn't read the value from the environment constant, as recommended here. Not sure if this was recommended in Angular 2 and not for Angular 4, but the support is still built-in. I found so many horrible solutions for the environment problem, but no simple and easy one.

I solved the problem with my heroes-service and static properties. Yep, static field properties. It's that easy!

Here's a snippet from my service:

export class HeroService {
 
  private static heroesUrl : string;
       
  public static initMode(prodMode: boolean): void {
    if (prodMode) {
      enableProdMode();

      HeroService.heroesUrl = 'https://cloud.sibvisions.com/heroes/services/rest/Heroes/HeroesWorkScreen/data/heroes';
    }
    else {
      HeroService.heroesUrl = 'http://localhost/services/rest/Heroes/HeroesWorkScreen/data/heroes';
    }
  }
...

I used the recommended environment detection in my main.ts:

HeroService.initMode(!/localhost/.test(document.location.host));

This solved my environment problem completely. I'm not sure what's the best solution and I would recommend the built-in environment solution, but there should be a working tutorial or example for development and deployment.

Second lesson learned: Deployment is not easy with Angular if you want to make it right. The documentation is not clear and it's complex!!!

Back to my second problem: browser reload!
This is documented here. Clear, isn't it?

So, no solution for my Apache Tomcat installation. Rewrite rules are supported but why so tricky? I won't configure routing in my application and in the application server. This should simply work.
I tried to find a simple solution and created a simple Filter, because too much configuration was not what I want!

My filter:

public void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException
    {
        String sURI = ((HttpServletRequest)pRequest).getRequestURI();
       
        if (StringUtil.isEmpty(FileUtil.getExtension(sURI)))
        {
            RequestDispatcher reqdis = pRequest.getRequestDispatcher(sIndex);
           
            reqdis.forward(pRequest, pResponse);
        }
        else
        {
            pChain.doFilter(pRequest, pResponse);
        }
    }

Configration (web.xml):

<filter>
   <filter-name>IndexHtmlFilter</filter-name>
   <filter-class>demo.angular.IndexHtmlFilter</filter-class>
   
   <init-param>
    <param-name>index</param-name>
    <param-value>/app/index.html</param-value>
   </init-param>
 </filter>
 
 <filter-mapping>
   <filter-name>IndexHtmlFilter</filter-name>
   <url-pattern>/app/*</url-pattern>
 </filter-mapping>

Not my best code, but works.... if you put the application in a seperate folder... e.g. The REST servic call:

//souldn't route to index.html
https://cloud.sibvisions.com/heroes/services/rest/Heroes/HeroesWorkScreen/data/heroes

//should route to index.html
https://cloud.sibvisions.com/heroes/heroes/2

So it was easier to move the application into a sub directory: /app
https://cloud.sibvisions.com/heroes/app/heroes/2

and everything starting with /app/* will be routed to /app/index.html if necessary.

This configuration and the filter solved my second problem and hurray, my application was working without any problems!

Oh, in order to serve my application from the /app directory, I had to set the base href in index.html:

ng build -prod --base-href /heroes/app/

Here's the application: https://cloud.sibvisions.com/heroes/app/
(Please don't delete my default heroes because there's no "restore defaults" implemented)

What's next?

I wasn't happy with default features of tutorial app because I missed an export feature. So I decided to add a Report which contains all my heroes. The REST service was ready in one minute because VisionX has Reporting support. But how to download/save the report?

I had no example and google returned too many answers for Angular 2 and one for Angular 4. I followed the instructions and had many problems because the saveAs method wasn't found... So I had the next big problem!

Every new feature was a big problem for me :)

This feature was horrible because I had absolutely no plan about angular modules. I found several save-file modules and tried to install one by one. No module was working as documented. Not sure if the problem was because of my configuration or because of Angular 4. Finally I found one solution but I had to change my system.config.js manually and add:

'file-saver': 'npm:file-saver'

to the system loader and

'file-saver': {
        format: 'global',
        main: 'FileSaver.js',
        defaultExtension: 'js'
      }

to the packages.

I don't know why the default module installation didn't work but it did not!

After everything worked in my dev environment, I tried the same application in my prod environment and... error.
The FileSaver.js was not found???

Absolutely unclear, but I solved the problem dirty (or not) in my index.html. Simply added

<script src="node_modules/file-saver/FileSaver.min.js"></script>

and it was find in dev and prod environment.

... I thought.

One more problem: Download worked but the content was damaged!

The problem was related to the content-type or maybe the encoding?

Recommended (Solution 1, Solution 2) solution:

this.http.post(HeroService.heroesUrlAction + 'createListReportHeroes', JSON.stringify([null, null]))
                                 .toPromise()
                                 .then(response => {
                                   var blob = new Blob([response._body], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
                                   saveAs(blob, 'report.xlsx');
                                 })
                                 .catch(this.handleError);

This wasn't working because something was missing:

var optCopy = new RequestOptions({ headers: this.headers, withCredentials: true, responseType: ResponseContentType.Blob });+

      this.http.post(HeroService.heroesUrlAction + 'createListReportHeroes', JSON.stringify([null, null]), optCopy)
                                 .toPromise()
                                 .then(response => {
                                   var blob = new Blob([response.blob()], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
                                   saveAs(blob, 'report.xlsx');
                                 })
                                 .catch(this.handleError);

Clear, the ResponseContentType.Blob has to be set!!!
And if you set this type, the response._body throws a warning? Use response.blob() instead!

It took me one hour to remove this warning!

Next?

I'm done!

The whole application development made me 1 year older because of frustration. I know that I'm not an Angular expert and I didn't read everything in the official documentation or guide/tutorial, but the whole development was a torture - sure, the result is great!

Too many different solutions for simple problems: deployment, downloading a file, configuration.

The documentation is awesome but too complex and contains no simple use cases. A simple deployment would be awesome and the problem with environments was horrible. Angular is powerful, no doubt! The configuration and deployment aren't straight forward. Also the mechanism behind the module system was not always working, at least not for me.

I love using custom components and the styling integration is great. The routing configuration is error-prone without an IDE or specific tools. My application didn't use all available features like testing, linting, ...

Finally

The application is available as JVx application with Vaadin UI here.
Login with admin as username and password.

The Angular 4 application.