Lieferservice Hochheim auf ablu.info

Auf der Webseite https://ablu.info können sich Unternehmen aus Hochheim und der Region kostenlos registrieren und Ihre Angebote zum Liefern und/oder Abholen von Waren hinterlegen.

Die Seite richtet sich an Restaurants, Bäckereien, Metzgereien, Buchhandlungen, Apotheken, Bekleidungsgeschäfte und alle anderen Ladengeschäfte, die derzeit nach neuen Wegen suchen.

Sowohl für Unternehmen als auch für Suchende ist die Nutzung der Seiten kostenlos.

Neben dem Online-Angebot wird es auch eine Druckversion geben, die offline verteilt werden kann. So werden auch die Menschen erreicht, die kein Internet haben.

Die Seite ist in zwei Tagen sehr agil entstanden und es kommen noch weitere Funktionen hinzu.

Zunächst wurde dafür ein Prototyp auf einem Whiteboard erstellt und diskutiert. Dieser wurde dann in eine erste Web-Anwendung überführt und nach und nach die ersten Funktionen hinzugefügt.

Am 21.03.2020 um 01:40 Uhr wurden die ersten Beta-Tester eingeladen, die Seite zu prüfen. Nach Implementierung Änderungs- und Erweiterungswünschen konnte die Seite dann um 10:56 Uhr der breiten Öffentlichkeit gezeigt werden. Um 10:58 gab es bereits die erste Anmeldung eines Unternehmens.

SOAP-Fehlermeldung WebServiceException: Could not send Message umgehen

Beim Verbindungsaufbau zu einem WebService, bin ich über ein Problem gestolpert, dass der Zugriff auf den WebService nicht mehr möglich war. Obwohl bei der Generierung des Services (javax.xml.ws.Service) die URL zur WSDL-Datei angegeben wurde und in dieser Datei die Endpoint-URL definiert war, funktionierte der Aufruf jeweils nur einmal nach dem Deployment im jBoss Application Server. Warum? Keine Ahnung. Zunächst wurde die Verbindung wie folgt aufgebaut:

String plainUrl = "http://mywebserivce:9999/call.php?wsdl";
Url url = new Url(plainUrl);
PrintManagerService service = new PrintManagerService(url);	
soap = service.getPrintManagerPort();

In der lokalen Testumgebung (Windows) funktionierte das einwandfrei. Erst im Produktionssystem (Linux; vermutlich spielt das BS hier keine Rolle) kam es zu folgender Fehlermeldung:


javax.xml.ws.WebServiceException: Could not send Message.
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:135)
...
Caused by: java.io.IOException: IOException invoking http://localhost:8080/call.php: HTTP response '404: Not Found'

Der Host wurde offensichtlich von mywebservice:9999 auf localhost:8080 geändert. Im Code konnte ich keine Stelle finden, an dem vom localhost auf Port 8080 die Rede war. Durch Ergänzung von

((BindingProvider)soap).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, plainUrl);

konnte das Problem gelöst werden. Nun wird einmal die URL übergeben und direkt danach der Endpoint ein weiteres Mal gesetzt:

String plainUrl = "http://mywebserivce:9999/call.php?wsdl";
Url url = new Url(plainUrl);
PrintManagerService service = new PrintManagerService(url);	
soap = service.getPrintManagerPort();
((BindingProvider)soap).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, plainUrl);

Verbindungsaufbau zur Plentymarkets SOAP Api Version 110 mit Java

Das folgende Beispiel zeigt die Verwendung der Plentymarkets SOAP Api Version 110. Die SOAP-Klassen wurden mittels dem in Eclipse eingebauten Tool zur Erstellung von Java-Klassen anhand einer WSDL-Datei generiert.

public class PlentyConnector {
	private Logger logger = Logger.getLogger(PlentyConnector.class);
	private String TOKEN = "";
	private int USERID = -1;
	private PlentySoapApiFunctionContainerServiceStub service = null;

// die weiteren Methoden werden separat gezeigt

}

In der folgenden Methoden wird ein Token geladen, sofern dies noch nicht geschehen ist.

   private void createService(String accountUser, String accountPassword, String portAddress) throws Exception {
		try {
			service = new PlentySoapApiFunctionContainerServiceStub(portAddress);
			if (this.TOKEN.equals("") || USERID == -1) {
                                //request a new token
				GetAuthentificationToken req = new GetAuthentificationToken();
				req.setOLogin(new PlentySoapRequest_GetAuthentificationToken());
				req.getOLogin().setUsername(accountUser);
				req.getOLogin().setUserpass(accountPassword);
				GetAuthentificationTokenResponse resp = service.getAuthentificationToken(req);
				this.TOKEN = resp.get_return().getToken();
				this.USERID = resp.get_return().getUserID();
			}
                        //modify the service and set the userid and token as a header element
			SOAPFactory factory = OMAbstractFactory.getSOAP12Factory();
		    SOAPHeaderBlock header = factory.createSOAPHeaderBlock("verifyingToken", null);
		    OMFactory omFactory = OMAbstractFactory.getOMFactory();
		    OMNode userNameNode = omFactory.createOMElement(new QName("UserID"));
		    ((OMElement) userNameNode).setText(""+USERID);
		    header.addChild(userNameNode);
		    OMNode passwordNode = omFactory.createOMElement(new QName("Token"));
		    ((OMElement) passwordNode).setText(TOKEN);
		    header.addChild(passwordNode);
		    service._getServiceClient().addHeader(header);
			System.out.println("token: " + this.TOKEN + " userid: " + this.USERID);
		}
		catch(Exception e) {
			logger.error("Authentification token not loaded correctly!");
			throw e;
		}
	}

Die nächste Methode zeigt am Beispiel der Serverzeit, wie weitere Aufrufe erfolgen können:

	private void getServerTime() {
		try {
	    	PlentySoapResponse_GetServerTime resp = service.getServerTime(new GetServerTime()).get_return();
	    	System.out.println("Servertime : "+resp.getTimestamp());
	   }   
	   catch(Exception e) {
		   logger.error("Server time not loaded correctly: " + e.getMessage(), e);
	   }
	}

In der nachfolgenden Methode wird ein Aufruf mit ein paar Testdaten durchgeführt, um die Funktionsweise zu veranschaulichen:

	public void executePlentyCall() throws Exception {
		System.out.println("login");
		createService("MyUserName", "MyPassword", "http://my.plenty.installation.de/soap/version110/");
		getServerTime();
}

Die SOAP-Klassen zum Verbindungsaufbau wurden mit Hilfe des axis2-Tools wsdl2java.bat erstellt.

Auslesen einer HTML-Tabelle mit JavaScript und Auswertung von Input-Feldern

Das folgende Beispiel zeigt wie mit Hilfe von JavaScript-Bordmitteln eine HTML-Tabelle durchlaufen werden kann. Anhand des Beispiels soll darüber hinaus eine Möglichkeit gezeigt werden, Input-Felder in den Tabellen-Zeilen auszulesen und auszuwerten.

Aufbau der HTML-Tabelle

<table id="table_example">
      <tbody>
        <tr>
          <th>Ausgewählt</th>
          <th>Bezeichnung</th>
          <th>Wert 1</th>
          <th>Wert 2</th>
        </tr>
        <tr>
          <td><input name="activated" type="checkbox" checked=""></td>
          <td>Tabellen-Inhalt erste Zeile</td>
          <td><input name="input1" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="2"></td>
          <td><input name="input2" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="1"></td>
        </tr>
        <tr>
          <td><input name="activated" type="checkbox" checked=""></td>
          <td>Tabellen-Inhalt zweite Zeile</td>
          <td><input name="input1" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="2"></td>
          <td><input name="input2" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="1"></td>
        </tr>
      </tbody>
    </table>

In der Tabelle gibt es in jeder Zeile eine Checkbox und zwei Eingabefelder. Eine JavaScript-Funktion soll nun die einzelnen Zeilen durchlaufen und auswerten, ob die Zeile markiert wurde und welche Werte eingetragen sind. Bei den Eingabefeldern sollen nur Integer-Werte erlaubt sein, weshalb der eingegebene Inhalt zunächst überprüft wird.

JavaScript zum Parsen der Input-Inhalte

function parse_int_value(ctrl) {
  ctrl.value = parseInt(ctrl.value) || 0;
}

Das Parsen der Eingabewerte erfolgt beim Durchlaufen der Tabelle erneut.

JavaScript zum Durchlaufen der Tabelle

    function parse() {
     var tblOutputDiv = document.getElementById('tbl_values');
     tblOutputDiv.innerHTML = "";
     var table = document.getElementById('table_example');
     var tmp = "";
	 	 for (var i = 1, row; row = table.rows[i]; i++) {
			//iterate through rows and check whether the row has been selected
			var checked = false;
			var value1 = 0;
			var value2 = 0;
			var inputFields = new Array();
      //get all input fields in the current row
			inputFields = row.getElementsByTagName("INPUT");
			for (var counter = 0, field; field = inputFields[counter]; counter++) {
				if (field.name == "activated") {
					checked = field.checked;
				}
				else if (field.name == "input1") {
					value1 = parseInt(field.value) || 0;
				}
				else if (field.name == "input2") {
					value2 = parseInt(field.value) || 0;
				}
			}
      tmp += 'Ausgewählt: ' + (checked ? "ja" : "nein");
      tmp += ' Wert 1: ' + value1;
      tmp += ' Wert 2: ' + value2;
      tmp += '<br/>';
		}
    tblOutputDiv.innerHTML = tmp;
    }

Zunächst wird das DIV geladen welches zur Ausgabe dient und dessen Inhalt überschrieben. Anschließend erfolgt eine Iteration durch die Zeilen der Tabelle und in jeder Zeile werden die darin enthaltenen Input-Elemente geladen. Diese werden ebenfalls durchlaufen und anhand des Namens werden die Werte in einer temporären Variable zwischengespeichert und später in dem Ausgaben-DIV ausgegeben.

Aufgerufen wird das Skript durch einen Link Tabelle auslesen.

Dieses Beispiel ist bewusst simpel gehalten und soll lediglich das Grundprinzip verdeutlichen. Das gezeigte Beispiel kann hier heruntergeladen bzw. getestet werden: HTML-Tabelle auslesen

Feedback welcome

Hat Ihnen die kurze Übersicht des Auslesens mit Hilfe von Boardmitteln weitergeholfen? Welche weiteren Möglichkeiten gibt es zum Auslesen von Tabellen? Ich freue mich über Kommentare. :-)

REST-Webservice mit PHP ansprechen

REST-Webservice mit PHP ansprechen

In diesem Beitrag geht es um das Ansprechen eines REST-Webservices mittels PHP, wobei der REST-Webservice die Ergebnisse mit Hilfe von JSON zurückliefert. REST steht für Representational State Transfer und stellt eine Möglichkeit zur Implementierung einer Schnittstelle dar. Rückgaben können auf verschiedene Arten erfolgen, z.B. durch XML oder JSON. Das Ansprechen eines SOAP-Webservices ist mit Hilfe der in PHP eingebauten Soap-Klasse möglich (SoapClient). Für das Arbeiten mit einem REST-Webservice kann beispielsweise auf cURL zurückgegriffen werden, was auch Inhalt dieses Beitrags sein wird.

Methode zum Aufruf des Services

public function callAPI($data = false)
{
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_POST, 1);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
	curl_setopt($curl, CURLOPT_URL, $this->serviceUrl);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
	$ret = curl_exec($curl);
	if ($ret === false) {
		$ret = null;
	}
	curl_close($curl);
	return $ret;
}

Zunächst wird mit curl_init(); eine neue cURL-Instanz erzeugt. Post-Daten können eingesetzt werden (curl_setopt($curl, CURLOPT_POSTFIELDS, $data);) und die URL zum Webservice wird gesetzt (curl_setopt($curl, CURLOPT_URL, $this->serviceUrl);). Anschließend wird der Service aufgerufen und der Rückgabewert zurückgegeben ($ret = curl_exec($curl);). Wichtig ist, dass vor der Rückgabe die cURL-Verbindung geschlossen wird (curl_close($curl);).

Methode zum Auslesen der JSON-Rückgabe

Angenommen, in der Rückgabe stecken Benutzerinformationen als JSON-Objekt, kann das Auslesen mit folgende Methode vorgenommen werden:

public function getUserEntityFromInfo($userInfo) {
	$obj = json_decode($userInfo);
	if ($obj && isset($obj->{'username'})) {
		$user = new UserEntity();
		$user->setuserName($obj->{'username'});
		$user->setemail($obj->{'useremail'});
		return $user;
	}
	return null;
}

Dabei wird zunächst das JSON-Objekt anhand des Strings erzeugt ($obj = json_decode($userInfo);). Beinhaltet das Objekt username, so werden username und useremail ausgelesen. Die Klasse UserEntity besteht in diesem Beispiel lediglich aus Getter- und Setter-Methoden.

Die komplette Klasse

class RestExample {
	
	private $serviceUrl = "https://www.myservice.com/servicename";
	
	public function callAPI($data = false)
	{
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_POST, 1);
		curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
		curl_setopt($curl, CURLOPT_URL, $this->serviceUrl);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
		$ret = curl_exec($curl);
		if ($ret === false) {
			$ret = null;
		}
		curl_close($curl);
		return $ret;
	}
	
	public function getUserEntityFromInfo($userInfo) {
		$obj = json_decode($userInfo);
		if ($obj && isset($obj->{'username'})) {
			$user = new UserEntity();
			$user->setuserName($obj->{'username'});
			$user->setemail($obj->{'useremail'});
			return $user;
		}
		return null;
	}
}

Aufruf der Klasse

Das folgende Beispiel zeigt, wie diese Klasse aufgerufen und Post-Werte übergeben werden können.

$restClient = new RestExample();
$curlPostData = array(
		'MyFirstProperty' => 123,
		'MySecondEntry' => "testdata"
);
$ret = $restClient->callAPI($curlPostData);
if ($ret != null) {
	$user = $restClient->getUserEntityFromInfo($ret);
	if ($user != null) {
		echo $user->getuserName();
	}
	else {
		echo "Benutzerinformationen konnten nicht geladen werden.";
	}
}
else {
	echo "Keine Rückgabe nach Webservice-Aufruf erhalten.";
}

Chrome-Extension zum Testen eines REST-Webservices

Zum Schnellen Testen eines REST-Webservices, bietet sich die Google Chrome-Extension Advance REST Client an. Mit dieser Extension können sowohl GET- als auch POST-Requests getestet werden und das Übergeben von Variablen ist möglich.

Anmerkungen

Die hier gezeigten Beispiele sind bewusst recht simpel gehalten. Das Aufrufen eines REST-Webservices wird gezeigt und es ist ersichtlich, wie POST-Daten übergeben werden können. Auch das Erzeugen eines JSON-Objektes wird gezeigt.