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

Neues Jahr, neues Glück

Ich habe es nicht mehr geschafft letztes Jahr den ein oder anderen Beitrag zu verfassen. Dafür noch an alle die meinen Blog seit Jahr und Tag verfolgen.

!!! SORRY !!!

Ich freue mich dennoch darüber das immer wieder neue Besucher meinen Block finden und auch lesen. Es wird die nächste Zeit mal wieder das ein oder andere Tutorial geben. Speziell im Bereich Android versuche ich euch wieder das ein oder andere zu zeigen. Die neuen Benutzeroberfläche für das wohl meist verbreitete mobile Betriebssystem weltweit ist ziemlich cool, übersichtlich und scheint relativ intuitiv bedienbar zu sein.

Ich freue mich das ich endlich mal wieder geschafft habe ein paar Zeilen zu schreiben. Bis die Tage. Jo. 😉

Kurz in eigener Sache und zum Sport

Hey zusammen, wie der ein oder andere mittlerweile mitbekommen hat bin ich aktiver Crossfitter, Nein, ich mache das nicht weil ich meine Urinstinkte wecken lassen möchte oder weil es mir gefällt mich täglich anbrüllen zu lassen. Auch finde ich Aussagen wie Weichei eher für unangepasst in Boxen.

Es geht um Teamgeist, seine Leistung zu verbessern ein besseres Körpergefühl für sich zu entwickeln und vor allem dem eigenen Schweinehund den Kampf anzusagen.

Games2013_WomenZigZagSprintDaher bin auch ich nicht vollkommen unvoreingenommen, aber ich finde es gut als Allrounder, der mittlerweile über die Jahre sehr viele verschiedene Sportarten (Fussball, Handball, Kickboxen, All-Style-Karate, Mountainbiken, Skifahren, Inlinern, klassiches Fitnessstudio, Laufen, Hindernislaufen etc.) ausgeübt hat, sagen zu können das es fast keine bessere Alternative gibt sowohl Herz und Kreislauf zu festigen, seine Muskulatur zu stabilisieren und das unter Aufsicht!

1899473_1443735389195655_1895774167_o

Letzes Jahr hat mich ein kleiner Bandscheibenvorfall erwischt, leider sitze ich auch den ganzen Tag zu meist gegen 10 -12 Stunden vor dem Rechner und Tippe unverständliche Zeichen zu tausenden in den Flimmerkasten. Wenn die Tippse vorüber ist kann es auch noch dazu kommen das diverse Onlinegames und Shooter den Abend füllen können wenn man mal wieder nichts anderes zu tun hat. Quasi weiter sitzend. Ich liebe es aber auch an die frische Luft zu kommen. Mountenbiken, Laufen, Skifahren, Bouldern, Inlinern, Paintballspielen und was einem sonst noch einfällt. Meistens habe ich auch den Drang dazu es etwas zu übertreiben und somit – meine Crossfiter Kollegen der Box Kulmbach können das bezeugen – artet es ab und an mal in dem einen oder anderen Unfall aus. 😉

Aber ich lenke zu sehr vom Thema ab. Ich habe heute durch Zufall einen Blogeintrag vom Stern gelesen. Alexandra Kraft, scheinbar (ich spekuliere) selbst nie in einer Crossfit Box teilgenommen da sie über Preise in New York schwadruliert die 100 Dollar oder mehr kosten können. Abwertend über – als Drillsergeant betitelte – Trainer oder gar Weicheiermentalität. Für meine Verhältnisse finde ich diesen Artikel ziemlich belustigend, Crossfit kommt viel härter rüber als es ist. Natürlich kann es sein das es mal lauter wird, aber das dient der Motivation – wie es auch im Blogeintrag von Alexandra richtig, aber auch abwertend erkannt wurde. Um die letzen Reserven den Schweinehund aus seinem Häuschen zu jagen fehlt einem selbst ab und an die Überwindung und da kommt der Teamgeist der Crossfitter heraus. Anfeuern, nicht locker lassen und seine Mitstreiter so lange unter die Arme kitzeln bis die letze Übung, die letze Wiederholung vollendet wurde. Gehört dazu, egal bei welcher Sportart. Ich kann nicht im Wettkampf ab der Hälfte aufhören und gehen. Ich werde auch bei einem Turnier vermeiden vorzeitig auszuscheiden oder im Fall eines Läufers einen Marathon nicht nach 10 Kilometern abbrechen. Im Fall von Crossfit ist jedes WOD sein eigener kleiner Wettkampf, in meiner Leistungsgruppe. Kann ich keine Liegestütze mache ich sie auf den Knien. Kann ich keine Klimmzüge, gehe ich an die Ringe und skaliere mir mein Gewicht so zu Recht das ich es schaffe mein WOD durchzuhalten. Natürlich sollte das Ganze auch anstrengend sein, immerhin will man keine 2Stunden dafür in Kauf nehmen.

c2e0e164d9124a2a2e6551b895635ea0-1684ced9cba38c8aea3c3773fabd59ba

Wenn ich dagegen Abschnitte wie den folgenden Lese, weiß ich wirklich nicht ob sich Alexandra schon ernsthaft mit der Materie beschäftigt hat.

Heute dagegen ist Fitness harte Arbeit. Der Spaß ist vorbei. Es geht um maximalen Muskelaufbau in kürzeste Zeit. Doppelt gut, wenn das alle noch in die Mittagspause passt. Es gilt: Kein Schmerz, kein Gewinn.

 

Das Prinzip ist einfach und schmerzhaft. Durch systematische Überbelastung soll maximaler Trainingserfolg erzielt werden. Ob das gesund für Muskeln, Herz und Kreislauf ist, hat noch keiner vernünftig belegt. Die Zahl der Anhänger wächst aber trotzdem dramatisch. Das wird auch nicht davon gebremst, dass zahlreiche Kardiologen vor der Überlastung, vor allem für Ungeübte, warnen.

 

Lustig ist das längst nicht mehr. Wer nicht durchhält, gilt als Weichei. Und wer es lieber entspannt mag, als Zeitverschwender. Wenn ich theoretisch in sieben Minuten fit werden kann, warum soll ich dann eine halbe Stunde meiner wertvollen Zeit dafür hergeben? Der perfekte Sport für ein Land wie die USA, in dem es für Arbeitnehmer keinen gesetzlichen Urlaub gibt.

Der Schlusssatz verrät alles 😉

Und umso öfter ich dabei zuschaue, umso sicherer bin ich, dass Cross Fit nichts für mich ist.

Alle Sportarten haben ihre Eigenheiten, alles macht unterschiedlich Spaß und fördert, fordert seinen Körper. Aber man sollte nicht so herablassend über eine Sportart schreiben die man selbst nur aus der Ferne beäugt.

JSPWiki in glassfish

Ich habe vor kurzem einen Application- Server von glassfish 4 auf 4.1 geupdatet. Problem, Jspwiki weigert sich Umlaute korrekt dar zu stellen.

Hat schon mal jemand ähnliche Probleme mit dem aktuellen glassfish und eine Lösung parat?

Meine Lösung derzeit ist ein aktueller tomcat 8 mit einer jdk 7. Leider wird von jspwiki kein Java 8 voll unterstützt.

Für Tipps wäre ich dankbar.

PS: Konfiguriert ist das gesamte System mit UTF-8. Sowohl in custom.properties als auch auf dem application server. Auch das local File encoding steht auf UTF-8. Das gesamte JSPWiki wird falsch dargestellt. Sowohl die Inhalte als auch die Seiten.

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.

<property name="alternatedocroot_1" value="from=/my.jpg dir=/srv/images/jpg"/>
<property name="alternatedocroot_2" value="from=*.jpg dir=/srv/images/jpg"/>
<property name="alternatedocroot_3" value="from=/jpg/* dir=/src/images"/>

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();
		}
	}
}

Varnish Cache für Webserver

Varnish Installieren

$ sudo apt-get install varnish

Varnish Konfigurieren

Die Standardkonfigurationen von Varnish findet ihr unter /etc/default/varnish und /etc/varnish/default.vcl

Für den Cache wird in der /etc/default/varnish ein daemon konfiguriert der nach der Installation per sudo bereits vorkonfiguriert ist. Ihr könnt zur Sicherheit noch einmal nachsehen.

DAEMON_OPTS="-a :6081  
             -T localhost:6082  
             -f /etc/varnish/default.vcl  
             -S /etc/varnish/secret  
             -s malloc,256m"

Der Port des daemon wird nach der Konfiguration von 6081 auf 80 geändert.

In der /etc/varnish/default.vcl wird der listener definiert auf den varnish hört.

backend default {
    .host = "127.0.0.1"; 
    .port = "80"; 
}

Für eine detailierte Information und ein paar Erklärunge könnt ihr gerne unter: Sitepoint – Getting Started with Varschnish nachsehen und auch diese Verweise weiterarbeiten.

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 && id > 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.