Docker + Netbeans + Liferay

Für eine einfache Integration von Docker für die Entwicklung nutze ich Netbeans IDE in Version 8. Die integrierte Docker Unterstützung vereinfacht es, neue Application Server aufzusetzen und für spätere Anwendungen zu verteilen.

Im Vorfeld muss die lokale Docker Installation angepasst werden. Standardmäßig hört Docker auf keinen TCP Socket, das muss customized werden.

Core OS gibt eine einfache Anleitung hierfür vor:

Customizing docker

The Docker systemd unit can be customized by overriding the unit that ships with the default Container Linux settings. Common use-cases for doing this are covered below.

Enable the remote API on a new socket

Create a file called /etc/systemd/system/docker-tcp.socket to make Docker available on a TCP socket on port 2375.

<code><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">Docker Socket for the API</span>

<span class="nn">[Socket]</span>
<span class="py">ListenStream</span><span class="p">=</span><span class="s">2375</span>
<span class="py">BindIPv6Only</span><span class="p">=</span><span class="s">both</span>
<span class="py">Service</span><span class="p">=</span><span class="s">docker.service</span>

<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">sockets.target</span>
</code>

Then enable this new socket:

<code>systemctl <span class="nb">enable </span>docker-tcp.socket
systemctl stop docker
systemctl start docker-tcp.socket
systemctl start docker
</code>

Test that it’s working:

<code>docker -H tcp://127.0.0.1:2375 ps</code>

Quelle: https://coreos.com/os/docs/latest/customizing-docker.html

Netbeans einrichten

Über den Reiter: Services -> Docker könnt ihr mit einem Rechtsklick eine neue Docker Instanz hinzufügen. Hierbei wäre es möglich sogar Remote Systeme anzuschließen, für Teams die auf einer zentralen Installation arbeiten.

Durch die vorhergehende Konfiguration von Docker sollte Successfull beim Test Connection erscheinen.

Docker + Liferay Image

Ich verwende aktuell noch ein Liferay 6.2 Image im Docker Umfeld für ein Projekt.

Daher habe ich auf das bereits erstelle Image von snasello zurück gegriffen.

Das könnt ihr hier https://hub.docker.com/r/snasello/liferay-6.2/

für eure Docker Installation herunterladen.

Liferay in Netbeans

Alle Docker Container werden anschließend im Netbeans gelistet. Ihr könnt den Liferay Container via Rechtsklick „run“ starten. Anschließend wird der Name definiert und das Port- Mapping.

Nachdem ihr „finish“ geklickt habt, wir der Container gestartet. In eurer Console erhaltet ihr dementsprechend den output.

How To Deploy Packages on Liferay Docker Container!

Um Portlets, Themes und Co auf dem Docker Container zu deployen, muss das deployment Verzeichnis lokal gemappt werden. Hierbei könnt ihr folgenden Befehl verwenden:

Laut Docker Dokumentation soll das über den Parameter -v möglich sein.

Quelle: https://docs.docker.com/engine/tutorials/dockervolumes/

Sobald es funktioniert, update ich den Beitrag!

Connect to Docker via Shell

Um den Docker Container zu konfigurieren oder Installationen / Konfigurationen vorzunehmen könnt ihr direkt via Shell auf das System zugreifen.

<code><span class="pln">sudo docker exec </span><span class="pun">-</span><span class="pln">i </span><span class="pun">-</span><span class="pln">t {name-of-docker-container} </span><span class="pun">/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">bash</span></code>

camunda cdi and multiple datasources

Ich durfte mich die letzten Tage mit einem ziemlich fiesen Fehler rum ärgern.

Wir optimieren unsere Bestellungen und vor allem automatisieren wir unseren gesamten Verarbeitungsprozess mit dem Backend.

Der gesamte Bestand und Bestellverfahren wird automatisch mit dem Shop, dem Backend und dem Versender abgeglichen.

Der Prozess zur Bestellübergabe wurde in camunda definiert. Die Service Tasks greifen auf CDI Beans zu. Diese Beans greifen auf JPA Ressourcen zurück.

Es wurden Projektbedingt 2 Resourceprojekte mit verschiedenen JTA- Resources definiert, die aber aufgrund der Architektur auf die gleiche Datenbank zugreifen.

 

Durch die schnelle Verarbeitung des Prozesses kommt  der Application Server durcheinander und Crasht bei der Verarbeitung der Transaktionen.  Einer der Ressourcenmanager lockt die Tabelle und verhindert das weitere verarbeiten der Daten.

Leider ist es mir nicht gelungen eine andere Lösung zu finden als beiden JPA Projekten der gleichen JNDI- Resource zu geben.  🙁

Jemand noch eine andere Idee?

JEE + Microservices, ein neuer IT- Kosmus

Wie man des öfteren ließt werden Micro-Services so etwas wie das nächste größere Ding sein für Moderne IT- Infrastruktur. Weg von großen Anwendungen die viele Daten zentralisiert speichern und nur im eigenen Universum zur Verfügung stellen. Es werden immer mehr Artikel über Micro-Services veröffentlicht. Kleine Datenmengen die sehr schnell zur Verfügung gestellt werden können. Dabei wird die Infrastruktur ziemlich zerrissen – und meiner Meinung nach – eine Art gewollte Unübersichtlichkeit geschaffen.

Da ich mich stetig mit der Entwicklung im JEE Umfeld beschäftige und eigentlich nur über Podcasts, Tutorials und Do-It-Yourself Lerne und meinen Wissensstand erweitere finde ich es nur fair, euch das leben an dieser Stelle etwas einfacher zu machen 😉

Im JEE7 Umfeld und der Verbindung von CDI- Beans ist es sehr einfach JAX-RS Web- Services zu erstellen. Ich werde die Tage ein Tutorial schreiben um euch zeigen zu können, wie einfach man die Kombination von

  • Maven
  • JEE7
  • JAX-RS

funktioniert.

Bis dahin.

PayOne + JEE Application

PayOne ist ziemlich umständlich zu integrieren. Für einen der großen Zahlungsdienstleister haben die Macher von PayOne ganz schön was nachzuholen. Ich persönliche finde PayOne vom Aufbau her ziemlich altmodisch. Man merkt, das dass Webportal des Zahlungsdienstleisters schon etwas in die Jahre gekommen ist. Ebenso der Komfort für die Kunden. Einen großen Pluspunkt hat PayOne. Die Integration in bestehende Systeme. Siehe magento – commerce, shopware, oxid, demandware oder intershop. Dadurch das sie in zahlreichen Großen Unternehmen zum Bestand zählen bietet eine feste Kundenbasis ein regelmäßigen Zahlungsfluss.

Wir sind gerade dabei die Schnittstellen für eine JEE- Anwendung anzubinden. Leider ist mir keine fertige Library im Netz über die Füße gefallen die ich nutzen hätte können. Somit kann ich wieder von vorne anfangen.

PayOne bietet seit einiger Zeit eine „Frontend“ – Lösung an. Hierbei wird über eine dynamische URL ein iFrame inkludiert oder die Seite aufgerufen über der die Zahlung abgewickelt werden kann. Dabei sind mir, im Gegensatz zur Konkurrenz z.B. Adyen wieder einige Nachteile aufgefallen.

  • Es kann lediglich eine Zahlungsart angeboten werden.
  • Die Implementierung ist ziemlich aufwendig da sehr genau Reihenfolgen eingehalten werden müssen.
  • Standard für die Implementierung ist PHP.
  • Relativ langsame Abwicklung des Zahlungsprozesses.

Ich hatte die letze Zeit öfters Gelegenheit einige der Zahlungsdienstleister ausprobieren zu können. Deren Schnittstellen zu testen und wie man darauf reagieren kann. Mir ist aufgefallen das Adyen an dieser Stelle sehr stark ist und mit einfachsten Mechanismen gute Zahlungsmöglichkeiten anbietet.

Unsere Entscheidung ist etwas ungewöhnlich. Wir haben uns trotz der Nachteile für PayOne entschieden. Folgende Gründe haben dafür gesprochen:

  • Unser Sachbearbeiter war stets bemüht alle Informationen die nötig waren zu bekommen
  • Kooperativ mit uns zusammen zu arbeiten
  • Gebühren und Kosten zu minimieren, so das wir als StartUp ebenfalls eine Chance haben
  • Schnelle Reaktion bei Support-Anfragen
  • Unterstützung beim Implementierungsaufwand

Trotz der gestiegenen Aufwände bei der Implementierung sollte es möglich sein in kurzer Zeit eine kleine Library für euch zur Verfügung stellen zu können die die Zahlungsabwicklung mit dem Frontend-Interface abzubilden.

 

Bis dahin

Euer Johannes

WildFly HttpServlet Alternative zu glassfish alternatedocroot_1

Glassfish bietet die Möglichkeit einfach und schnell neue Ordner ins System einzubinden und diese per URL- Pattern anzusprechen.

Die Parameter werden in der: glassfish-web.xml angegeben. Für mehr Details können sie gerne bei Oracle in die Details gehen.

&lt;property name="alternatedocroot_1" value="from=/my.jpg dir=/srv/images/jpg"/&gt;
&lt;property name="alternatedocroot_2" value="from=*.jpg dir=/srv/images/jpg"/&gt;
&lt;property name="alternatedocroot_3" value="from=/jpg/* dir=/src/images"/&gt;

Leider unterstützt jboss seit einigen Versionen eine ähnliche Funktion nicht mehr. Dafür kann per HttpServlet ein Ordner eingebunden werden der ebenfalls per Pattern angesprochen wird.

package de.kuw.jee.kuwbid.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import de.kuw.jee.core.ejb.ParameterBean;
import de.kuw.jee.kuwbid.mb.ApplicationBean;

@WebServlet(urlPatterns = "/images/*", initParams = { 
		@WebInitParam(name = "basePath", value = "/home/jskoeber/server/wildfly/uploads/documents") }
)
public class ImageServlet extends HttpServlet {
	private static final long serialVersionUID = -3522881745170031638L;
	private String basePath = "";
	
	@Inject
	private ApplicationBean app;
	
	@Inject
	private ParameterBean parameter;
    
    public void init() throws ServletException {
        this.basePath = getInitParameter("basePath");
        
        if(!parameter.getParameter("uri.images", app.getMandant()).getWert().isEmpty()) {
        	basePath = parameter.getParameter("uri.images", app.getMandant()).getWert(); 
        }
        
        // Validate base path.
        if (this.basePath == null) {
            throw new ServletException("FileServlet init param 'basePath' is required.");
        } else {
            File path = new File(this.basePath);
            if (!path.exists()) {
                throw new ServletException("FileServlet init param 'basePath' value '" + this.basePath + "' does actually not exist in file system.");
            } else if (!path.isDirectory()) {
                throw new ServletException("FileServlet init param 'basePath' value '" + this.basePath + "' is actually not a directory in file system.");
            } else if (!path.canRead()) {
                throw new ServletException("FileServlet init param 'basePath' value '" + this.basePath + "' is actually not readable in file system.");
            }
        }
    }
    

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String requestedFile = request.getPathInfo();
		
		File file = new File(basePath, URLDecoder.decode(requestedFile, "UTF-8"));
		if (!file.exists()) {
            // Throw 404, redirect to error page may is another selection
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
		
		// write via response's OutputStream
		FileInputStream inputStream = null;

		try {
			inputStream = new FileInputStream(file);
			byte[] buffer = new byte[1024];
			int bytesRead = 0;

			do {
				bytesRead = inputStream.read(buffer, 0, buffer.length);
				response.getOutputStream().write(buffer, 0, bytesRead);
			} while (bytesRead == buffer.length);

			response.getOutputStream().flush();
		} finally {
			if (inputStream != null)
				inputStream.close();
		}
	}
}

Primefaces 5 SelectOneMenu + Hibernate + Wildfly and Entities

Die letzten zwei Tage waren wieder eine Suche nach dem richtigen Weg. Im Normalfall würde sich mein JPA Manager um eine performante Art und Weise kümmern meine Daten zwischen zu lagern oder neu zu laden. Hibernate geht leider eine etwas andere Logik vor und cached nicht lange genug die Entities vor. Das heißt bei einem Neuaufruf über eine andere Bean wird nicht vom Application Server geladene Inhalte übertragen sondern die Abfrage erneut gestartet. Somit wäre ein flushen im Entity Manager nach dem updaten von Inhalten nur optional.

Es ist ziemlich einfach wenn man weiß woran es liegt. Und zwar wird bei Entities eine equals Methode geladen z.b.

package de.kuw.jee.core.verwaltung;

import java.io.Serializable;

import javax.persistence.*;

import java.util.List;
import java.util.logging.Logger;


/**
 * The persistent class for the virtual_domains database table.
 * 
 */
@Entity
@Table(name="virtual_domains")
@NamedQuery(name="VirtualDomain.findAll", query="SELECT v FROM VirtualDomain v")
public class VirtualDomain implements Serializable {
	private static final long serialVersionUID = 1L;
	private static final Logger log = Logger.getLogger(VirtualDomain.class.getName());

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;

	private boolean active;

	private boolean deleted;

	private String directory;

	@Column(name="domain_type")
	private String domainType;

	private String name;

	//bi-directional many-to-one association to SysDomain
	@OneToMany(mappedBy="virtualDomain")
	private List sysDomains;

	//bi-directional many-to-one association to VirtualAlias
	@OneToMany(mappedBy="virtualDomain")
	private List virtualAliases;

	//bi-directional many-to-one association to BillCustomer
	@ManyToOne
	@JoinColumn(name="customer_id")
	private BillCustomer billCustomer;

	//bi-directional many-to-one association to VirtualUser
	@OneToMany(mappedBy="virtualDomain")
	private List virtualUsers;

	public VirtualDomain() {
	}

	public int getId() {
		return this.id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public boolean getActive() {
		return this.active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	public boolean getDeleted() {
		return this.deleted;
	}

	public void setDeleted(boolean deleted) {
		this.deleted = deleted;
	}

	public String getDirectory() {
		return this.directory;
	}

	public void setDirectory(String directory) {
		this.directory = directory;
	}

	public String getDomainType() {
		return this.domainType;
	}

	public void setDomainType(String domainType) {
		this.domainType = domainType;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public List getSysDomains() {
		return this.sysDomains;
	}

	public void setSysDomains(List sysDomains) {
		this.sysDomains = sysDomains;
	}

	public SysDomain addSysDomain(SysDomain sysDomain) {
		getSysDomains().add(sysDomain);
		sysDomain.setVirtualDomain(this);

		return sysDomain;
	}

	public SysDomain removeSysDomain(SysDomain sysDomain) {
		getSysDomains().remove(sysDomain);
		sysDomain.setVirtualDomain(null);

		return sysDomain;
	}

	public List getVirtualAliases() {
		return this.virtualAliases;
	}

	public void setVirtualAliases(List virtualAliases) {
		this.virtualAliases = virtualAliases;
	}

	public VirtualAlias addVirtualAlias(VirtualAlias virtualAlias) {
		getVirtualAliases().add(virtualAlias);
		virtualAlias.setVirtualDomain(this);

		return virtualAlias;
	}

	public VirtualAlias removeVirtualAlias(VirtualAlias virtualAlias) {
		getVirtualAliases().remove(virtualAlias);
		virtualAlias.setVirtualDomain(null);

		return virtualAlias;
	}

	public BillCustomer getBillCustomer() {
		return this.billCustomer;
	}

	public void setBillCustomer(BillCustomer billCustomer) {
		this.billCustomer = billCustomer;
	}

	public List getVirtualUsers() {
		return this.virtualUsers;
	}

	public void setVirtualUsers(List virtualUsers) {
		this.virtualUsers = virtualUsers;
	}

	public VirtualUser addVirtualUser(VirtualUser virtualUser) {
		getVirtualUsers().add(virtualUser);
		virtualUser.setVirtualDomain(this);

		return virtualUser;
	}

	public VirtualUser removeVirtualUser(VirtualUser virtualUser) {
		getVirtualUsers().remove(virtualUser);
		virtualUser.setVirtualDomain(null);

		return virtualUser;
	}

	@Override
	public boolean equals(Object obj) {
		if(obj != null) {
			log.info("Equals action in VirtualDomain for obj: " 
					 + obj.toString());
		}
		return super.equals(obj);
	}
}

Zum testen habe ich die equals Methode rausgezogen um zu sehen was geprüft wird im selectOneMenu von Primefaces. Und wie ich es mir gedacht habe. Wildfly weiß nicht mehr das die Daten in der view schon existieren und werden daher nochmal aus der DB geladen und somit natürlich einen anderen Hash zugeordnet. Um diesen Fehler des: „value not valid“ zu vermeiden müsst ihr die Bean die ViewScoped per CDI Injected wird die Wertetabelle rausziehen z.B.


Der Primefaces

package de.kuw.jee.verwaltung.converter;

import java.util.logging.Logger;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;

import de.kuw.jee.core.verwaltung.VirtualDomain;
import de.kuw.jee.verwaltung.mb.DomainBean;

@FacesConverter("domainConverter")
public class DomainConverter implements Converter {
	private Logger log = Logger.getLogger(DomainConverter.class.getName());
	
	@Inject
	private DomainBean domainBean;
	
	private int id;
	
	@Override
	public Object getAsObject(FacesContext context, UIComponent component, String value) {
		VirtualDomain dom = null;
        id = Integer.valueOf(value);
        if(value != null &amp;&amp; id &gt; 0) {
        	for(VirtualDomain tmp : domainBean.getDomains()) {
        		if(tmp.getId() == id) {
        			dom = tmp;
        		}
        	}
        	
        	log.info("Object is: " + dom.toString() );
        }
        
        return dom;
    }

	@Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
    	String returnVal = "";
    	if(value instanceof VirtualDomain) {
    		 VirtualDomain dom = (VirtualDomain) value;
    		 returnVal = String.valueOf(dom.getId());
    	} else {
    		returnVal = "";
    	}
    	log.finest("GetAsString is: " + returnVal);
        return returnVal;
    }
}

Sobald ihr das beachtet, und aus bestehenden Datasets lest, werdet ihr den Fehler der falschen Hashed und equals Fehler nicht mehr bekommen.

viel Spaß beim ausprobieren.

osRetail – Primefaces 4 Upgrade

Wir sind wieder einen Schritt weiter. osRetail in der finalen Primefaces 4.0 Version. Und als Maven Projekt. osRetail das erste open Source ERP System für den Handel. Wir versuchen das starre Korsett eines fertigen ERP System aufzubrechen. Dem Kunden eine möglichst große Vielfalt an Standardfunktionalität zu bieten und auch kostspielige Funktionen wie der Katalogerstellung frei Haus mitzuliefern.

Vor ca. einem Jahr haben wir uns dazu entschlossen ein großes Refactoring der Software durchzuführen. So haben wir nicht nur die Entwicklungsumgebung von Netbeans auf Eclipse geändert. Wir haben auch IceFaces ausgetauscht und – mit dem leistungsstarken Framework – Primefaces einen weiteren Schritt zu einer moderneren Arbeitoberfläche geebnet.

jee7-standard

Ebenso wurde der Code überarbeitet. Derzeit verwenden wir den JEE7 Standard um den aktuellsten Stand der Enterprise Entwicklung von Java nutzen zu können. Autodeployments über Jenkins erleichtern uns die Arbeit erheblich da wir alle Code Commits und Änderungen auf den Testsystem schnellst möglich dem publishen können.

maven-overview-plugin

Maven unterstützt uns bei der Library Auswahl. Mit einfachsten Mitteln ist es möglich neue oder ältere Bibliotheken auszuwählen und automatisch in der Entwicklung und beim Deployment zu verwenden.

Glassfish 4 bietet uns einen schnellen Application- Server der leicht zu handeln ist. Skalierbarkeit, effizientes Arbeiten und schnelle Deployments standen dabei im Vordergrund. Durch einfache Erweiterbarkeit ist es uns möglich schnell auf Kundenwünsche zu reagieren.