HTML Single Page Layout – Simple, Easy, Fast

You all know the Single Page Layout of HTML pages. It means you have mainly only one page loaded. The page splits up the layout into different sections to get a clear and modern page design. There are a lot of web sites explaining this kind of modern web design and you can find hundreds of templates on different places like onepagelove.com. I also picked up such a design template to build a new site. As always the template is not perfect and you want to tweak it a little bit, to get your own individual style. After I looked into the CSS file and the layout setup, I was shocked. The CSS consisted of more then 2400 lines of code! And each HTML tag was applied with more than 5 style classes in average. For example a simple section within a 2-column layout looked something like this:

...
    <section class="bg-white dark:bg-gray-900">
        <div class="max-w-screen-xl px-4 pb-8 mx-auto lg:pb-16">                
        <h2 class="mb-4 text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">100% Open Source</h2>
            <div class="grid grid-cols-2 gap-8 text-gray-500 sm:gap-12 sm:grid-cols-3 lg:grid-cols-6 dark:text-gray-400">
            .....
         ....
....

So I asked my self: is this complexity needed? You may guess it – my answer is ‘no’. Now let me explain what is needed for a simple single page layout.

The Grid Layout

To design a good-looking website, you need to follow the principle of the golden ratio. This principle was discovered by Leonardo Da Vinci 500 years ago and is nothing new. To make it short: you split up your page into 12 columns and place the elements inside. For example to get an 2/3 ratio you place one block into columns 1-4 and the rest into 5-12.

Often this is archived with defining a lot col-classes for each ratio like this:

.col-1 {width: 8.33%;}
.col-2 {width: 16.66%;}
.col-3 {width: 25%;}
.col-4 {width: 33.33%;}
.col-5 {width: 41.66%;}
.col-6 {width: 50%;}
.col-7 {width: 58.33%;}
.col-8 {width: 66.66%;}
.col-9 {width: 75%;}
.col-10 {width: 83.33%;}
.col-11 {width: 91.66%;}
.col-12 {width: 100%;}

You my see this kind of css classes in many many layouts. But you don’t need this as you can use the grid design rule supported today by all modern browsers.

To use the grid layout you simply define your container class as a grid with 12 columns. The browser overtakes the column layout for you. See the following example:

<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
  max-width: 1280px;
  border: 1px dashed;
  padding: 10px;
  margin-left: auto;
  margin-right: auto;

  display: grid;
  grid-template-columns: repeat(12, minmax(69px, 110px));
}
</style>
</head>
<body>
  <section class="container">
    <div style="grid-column-start: span 4; border: 1px solid green;">
      <p>The left column with some text...</p>
    </div>

    <div style="grid-column-start: span 8; border: 1px solid blue;">
      <p>The right column.....</p>
    </div>
  </section>
</body>
</html>

The important part here is the style class for the section defining a grid with 12 columns

  display: grid;
  grid-template-columns: repeat(12, minmax(64px, 110px));

If you inspect the page with your browsers developer tools, you can see that the section is divided into 12 parts like Leonardo Da Vinci would do it.

So this is quite simple. And I do not use any css classes for my column sections. It is quite enough if you set the grid-column-start directly in the style attribute.

<div style="grid-column-start: span 4;">
      ...
</div>

Get Responsive

Finally you may want to get more responsive. My first example has a minimal width of 768px as each column is defined with a minim width of 64px. In small displays this is not perfect. So lets define a responsive design to cancel out 12 column ration if the display is smaller then 768 px. This can be done with a @meia definition:

@media (max-width: 768px) {
  .container {
     grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  }
}

This code creates a flexible “auto-responsive” grid in which elements are always at least 280 pixels wide. But now, the grid automatically reduces the number of columns as soon as the width falls below 280 pixels.

So we simply changed from 12 columns to responsive design. Thant’s it.

OnlyOffice and WOPI

This blog post includes some tips and tricks how to use WOPI protocol in combination with OnlyOfice.

OnlyOffice is a powerful collaborative web based document editor. OnlyOffice supports most of the common office document formats for editing Spredsheets, Documents, or Presentations.

WOPI is a standard web protocol for integrating web based document editors into a Web application. It was initially developed by Microsoft and is adapted from different Web Editors like OnlyOffce, Collabora and of course Microsoft Office. OnlyOffice supports the WOPI standard since version 6.4. Information about using WOPI API with OnlyOffice can be found here.

Continue reading “OnlyOffice and WOPI”

Wildfly – Elytron – LDAP SecurityDomains for Active Directory

In one of my last blog posts I explained how to setup a Security Domain in Wildfy Elytron – the new security module. In this blog post I explain how to setup a LDAP security domain for the Active Directory:

The ldap-realm

As explained in my last blog you have to define a security-domain and a security-realm in two separate sections. The following example shows the LDAP configuration to resolve users and roles form an Active Directory. I have reduced the non relevant parts:

       <subsystem xmlns="urn:wildfly:elytron:14.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
            .....
            <security-domains>
                .....                
              	<!-- My LDAP domain   -->
		<security-domain name="mydomain" default-realm="cached-ldap" permission-mapper="default-permission-mapper">
		   <realm name="cached-ldap"/>
		</security-domain>				                
            </security-domains>
            <security-realms>
                .....                
                <!-- my LDAP realm -->
		<ldap-realm name="ldap-realm" dir-context="ldap-connection" direct-verification="true">
			<identity-mapping rdn-identifier="sAMAccountName" use-recursive-search="false" search-base-dn="CN=users,DC=intern,DC=foo,DC=de" >
			  <attribute-mapping>
			   <attribute from="CN" to="Roles" filter="(member={1})" filter-base-dn="CN=users,DC=intern,DC=foo,DC=de"/>
			  </attribute-mapping>
			</identity-mapping>
		</ldap-realm>
		<caching-realm  name="cached-ldap" realm="ldap-realm"/>			    
            </security-realms>
            
            <!-- LDAP Dir Contexts -->
            <dir-contexts>
		<dir-context name="ldap-connection" url="ldap://my-ldap:389" principal="CN=bind_user,CN=users,DC=intern,DC=foo,DC=de">
		     <credential-reference clear-text="YOUR-PASSWORD"/>
		</dir-context>
    	    </dir-contexts>
.....
       

You have to tweak the dir-context and the base-dn in the example above to your LDAP settings. The setup searches for the sAMAccountName as the UserID and searches the roles in the ‘members’ attribute of the user entry.

The cached-ldap

The important part is the ‘cached-ldap‘ security realm. In older versions of Wildfly the ldap realm uses a cache per default. In the new version you need to define a cache by yourself. This is what the cached-ldap is good for. Your security domain points to the cached-ldap and the cached-ldap points to the ldap realm. If you don’t use this, you will see a lot of ldap requests against your directory.

You can also add attributes to setup the default caching like:

<caching-realm name="cached-ldap" realm="ldap-realm" maximum-age="300000" />

Find details here.

Logging

For debugging it is helpful if you change the loglevel for org.wildfly.security. For this you simply add the following logger into the subsystem logging:

        <logger category="org.wildfly.security">
	    <level name="DEBUG"/>
	</logger>

And also set the log level from “INFO” to “DEBUG” for your console handler. This setting will give you more insights of what is happening in the background.

From a server bash you can ‘tail’ the security messages like this:

$ tail -f /opt/jboss/wildfly/standalone/log/server.log  | grep "security"

Role Mapping

In some cases it maybe necessary to map LDAP Group names to specific role names within your application. There for you can use the mappers. See the following example which maps the LDAP Group name ‘imixs_users’ to the application role ‘org.imixs.ACCESSLEVEL.AUTHORACCESS’:

            <security-domains>
               ....
		<security-domain name="imixsrealm" default-realm="cached-ldap" permission-mapper="default-permission-mapper">
		 <realm name="cached-ldap" role-mapper="imixs-user-rolemapper" />
		</security-domain>	                
            </security-domains>
.....
            <mappers>
                .....              
                <regex-role-mapper name="imixs-user-rolemapper" pattern="imixs_user" replacement="org.imixs.ACCESSLEVEL.AUTHORACCESS" keep-non-mapped="true"/>
            </mappers>
....

The mapper is referred in the security-domain. I am using a regex role mapper to replace the role name. You will find more background about this role mapping here.

Eclipse GLSP – Nodes and Moveable Labels

In my last tutorial I gave a short introduction into Eclipse GLSP and how you can get started with your own Domain specific source model. Eclipse GLSP provides a powerful and rich feature set to solve different kind of problems in the modelling area. Today I would like to show a solution to a not so rare problem in graphical modelling.

In BPMN 2.0 and other modelling languages, elements often have labels outside of the element – e.g a label below an symbol. In BPMN labels are also moveable and can be placed at any position in a diagram even if the label is associated with the symbol:

How can this be realised with GLSP? It took me a long time, but the solution is quite simple. Let’s assume you have followed my first tutorial creating your GNode Elements out from your source model. In that case you have a GModelFactory creating the GNode elements.

Continue reading “Eclipse GLSP – Nodes and Moveable Labels”

Build Your Own Modelling Tool with Eclipse GLSP

Eclipse GLSP is a new graphical language server platform allowing you to build powerful and highly adoptable modelling tools. Like many modern modelling frameworks it is based on Node.js and runs in a web browser. But unlike many other modelling tools, Eclipse GLSP takes a much broader approach. It forces the strict separation between the graphic modelling and the underlying model logic. With this concept Eclipse GLSP can not only be integrated in different tooling platforms like Eclipse Theia, Microsoft VS Code or the Eclipse desktop IDE, it also allows any kind of extension and integration within such platforms. On the project homepage you can find a lot of examples and videos demonstrating the rich possibilities.

Continue reading “Build Your Own Modelling Tool with Eclipse GLSP”

Jakarta EE8, EE9, EE9.1. …. What???

Jakarta EE is the new Java Enterprise platform as you’ve probably heard. There is a lot of news about this application development framework and also about the rapid development of the platform. Version 9.1 was released in May last year and version 10 is already in a review process. But what does this mean for my own Java project? Because I was also a bit confused about the different versions, hence my attempt to clarify things.

Continue reading “Jakarta EE8, EE9, EE9.1. …. What???”

Wildfly Running on Docker with Custom Setup

WildFly is a modular & lightweight application server based on the Jakarta EE standard.

You can start a Wildfly Sever with the help of Docker quite simple. To start a blank wildfly server in its standard configuration run:

$ docker run -it quay.io/wildfly/wildfly

Bundle Your Application

If you want to bundle your own application you just need a simple Docker file like this:

FROM quay.io/wildfly/wildfly:26.0.0.Final
COPY ./target/*.war /opt/jboss/wildfly/standalone/deployments/

This will copy your web application or microservice from a maven project into the Wildfly deployment directory. The applicaton will be be automatically deployed during startup.

Wildfly & Microprofile

Wildfly comes with different configuration profiles (e.g. cluster mode, full version, microprofile). These configuration files are located in the server directory /opt/jboss/wildfly/standalone/configuration

To start your Wildfly container with one of these configurations – for example with the Eclipse Microprofiles you just need to add a parameter to the startup command, specifying the configuration profile:

FROM quay.io/wildfly/wildfly:26.0.0.Final
COPY ./target/*.war /opt/jboss/wildfly/standalone/deployments/
# Run with microprofiles
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c","standalone-microprofile.xml"]

This Dockerfile will now activate the standalone-microprofile.xml configuration during next startup.

Custom Configurations

Of course you can also customize your configuration. For this purpose just copy one of the standalone-*.xml Files form a running container to your host:

$docker cp [CONTAINER_ID]:/opt/jboss/wildfly/standalone/configuration/standalone.xml ./my-standalone.xml

Replace [CONTAINER_ID] with the ID of your running wildfly container.

Next you can edit the standalone.xml file and add your project specific configurations. Finally add you custom file to the container Image via the COPY command in your Dockerfile:

FROM quay.io/wildfly/wildfly:26.0.0.Final
COPY .my-standalone.xml /opt/jboss/wildfly/standalone/configuration/
COPY ./target/*.war /opt/jboss/wildfly/standalone/deployments/
# Run with custom configuration
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c","my-standalone.xml"]

That’s it!

kubectl get nodes error: You must be logged in to the server

Today I got the following error message when trying to run kubectl on my Kubernetes Cluster:

$ kubectl get pods
error: You must be logged in to the server (Unauthorized)

This issue can happen after renewing kubernates certificates and is caused the existing ~/.kube/config to have outdated keys and certificate values in it.

Kubernetes is renewing the certificates automatically and so you need to update your local copy too. You can check the status of your Kubernetes server certificate with:

$ openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text |grep ' Not '
            Not Before: Jan  8 21:13:17 2021 GMT
            Not After : Nov 13 14:46:01 2022 GMT

Running kubectl on a server you can simply renew your .kube/config file with the latest one from your server:

$ cp .kube/config .kube/config_old
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

Is Spring Boot Still State of the Art?

In the following blog post I want to take a closer look at the question if the application framework Spring Boot is still relevant in a modern Java based application development. I will take a critical look against its architectural concept and compare it against the Jakarta EE framework. I am aware of how provocative the question is and that it also attracts incomprehension. Comparing both frameworks I am less concerned about the development concept but more with the question about runtime environments.

Both – Spring Boot and Jakarta EE – are strong and well designed concepts for developing modern Microservices. When I am talking about Jakarta EE and Microservices I always talk also about Eclipse Microprofile which is today the de-facto standard extension for Jakarta EE. Developing a Microservice the concepts of Spring Boot and Jakarta EE are both very similar. The reason is, that a lot of technology of today’s Jakarta EE was inspired by Spring and Spring Boot. The concepts of “Convention over Configuration“, CDI or the intensive usage of annotations were first invited by Spring. And this is proof of the innovative power of Spring and Spring Boot. But I believe that Jakarta EE is today the better choice when looking for a Microservice framework. Why do I come to this conclusion?

Continue reading “Is Spring Boot Still State of the Art?”

Upgrade containerd.io/buster Breaks Kubernetes Master Node

Today I run into a strange situation after upgrading containerd.io on my Kubernetes master node. I am running kubeadm in version 1.21.6 and containerd.io in version 1.4.6. on a Debian Buster server. Everything was fine, until I did a apt upgrade which upgraded containerd.io/buster from version 1.4.6-1 to 1.4.11-1

After this upgrade my master node was no longer working and my Kubernetes cluster could not start. I saw log messages like this ones:

E1113 21:59:33.697509  649 kubelet.go:2291] "Error getting node" err="node \"master-1\" not found"
649 controller.go:144] failed to ensure lease exists, will retry in 7s, error: Get "https://node1-control-plane-endpoint:6443/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/tikal-master-1?timeout=10s": dial tcp 10.0.0.2:6443: connect: connection refused
649 reflector.go:138] k8s.io/client-go/informers/factory.go:134: Failed to watch *v1.Node: failed to list *v1.Node: Get "https://node1-control-plane-endpoint:6443/api/v1/nodes?fieldSelector=metadata.name%3Dmaster-1&limit=500&resourceVersion=0": dial tcp 10.0.0.2:6443: connect: connection refused

The problem seems to be discussed here. My only solution was to downgrade containerd.io back to version 1.4.6 which solved the problem for me:

$ sudo apt-get install containerd.io=1.4.6-1
$ sudo apt-mark hold containerd.io

With the apt-mark I marked containerd.io as a non upgradeable package which avoids automatic upgrades in the future. In general I recommend to mark also kubeadm and kubelet in this way:

$ sudo apt-mark hold containerd kubeadm kubelet kubectl

Upgrades of kubeadm and kubelet should be only done as explained in the Official Upgrade Guide.