Liferay 6.1 – Webservices – RemoteInterface

Ich habe einen ganzen Tag damit verschwendet fehlerhafte Tutorials von Liferay zu durchforsten um nach dem X- ten Versuch endlich eine passende Lösung zu finden. Das Geheimnis liegt in dern Stub’s die vom Webservice generiert werden. Um eine korrekte Übergabe der User und Passwort Credentials zu erzielen dürft ihr diese nicht in der URL übergeben. Beispiele von Liferay sind hierbei falsch und alle Anfragen werden mit einem 401 Authentication failed zurückgewiesen. Ich werde euch Anhand eines einfachen Webservices zeigen wie es korrekt funktioniert und Axis konform ist. Wir erstellen zunächst ein Standard Liferay Projekt mit Portlet. Das Portlet könnt ihr genau so gut wieder löschen. Oder blank im Projekt lassen. Anschließend könnt ihr über einen Rechtsklick auf euer Projekt.

Nachdem ihr das gemacht habt erscheint ein kleiner Dialog. Eigentlich selbsterklärend. Nachdem ihr auf „Finish“ gedrückt habt, geht es nun an den Service. Hierbei erstellt er euch automatisch die Entity Foo. Es ist eine Erleichterung falls ihr vorhabt persistent Daten in der Liferay Datenbank unter zu bringen. Ich will euch mit diesem Beispiel lediglich zeigen wie RemoteService in Liferay kommunizieren. Zu diesem Zweck habe ich die Entity gelöscht. Anschließend müsst ihr über den Rechtsklick auf die services.xml eure Klassen builden. Wenn der Ant Prozess durchlaufen ist und euch keine Fehler unterlaufen sind, solltet ihr jetzt eine ganze Stange neuer Klassen im src Directory haben. Darin enthalten sind im package impl folgende zwei Klassen zum einen für die local Services „FooLocalServiceImpl“ zum anderen für die RemoteServices die wir später anzapfen wollen. Das heißt, jegliche funktionalität im Service includieren wir in unserer RemoteKlasse: „FooServiceImpl“. Hier habt ihr ein kleines Biespiel für ein simples Ping Pong Beispiel. Anhand eines Zufallwertes. Wichtig! Ihr müsst jetzt wieder die Services erneut builden um Änderungen wirksam zu machen!

Jetzt gehts an den Hauptteil des Blogeintrags. Das Portlet mit der RemoteClient Funktion. Wie ihr euch schon denken könnt müsst ihr ein neues Liferay Projekt erstellen inkl. MVC Portlet.
Zum einen erstellen wir eine eigene Klasse in Abhängigkeit des MVC-Portlets. Hierzu passen wir einfach unsere portlet.xml des neuen WebService Clients an.

Wenn die neue Klasse erstellt ist, wird sie später bearbeitet. Aber zuerst erstellen wir unsere Webclient. Hierbei klicken wir rechts auf unser Portlet und über New -> Other zum WebserviceClient.

Über weiter kommen wir dazu die ServiceURL einzugeben. Hierbei muss beachtet werden dass wir den PortletName nutzen und über, seit liferay 6.0 /api/axis oder /api/secure/axis die Services aufrufen können.

Jetzt können wir unsere erweiterte MVC Klasse aufbauen.

public class RemoteClient extends MVCPortlet {

	private String host = "localhost";
	private String port = "8180";
	private String userId = "test";
	private String password = "test";
	private String portlet = "FooService-portlet";
	private String method = "Plugin_foo_FooService";

	@Override
	public void doView(RenderRequest renderRequest,
			RenderResponse renderResponse) throws IOException, PortletException {
		super.doView(renderRequest, renderResponse);
	}

	@Override
	public void processAction(ActionRequest actionRequest,
			ActionResponse actionResponse) throws IOException, PortletException {

// ACTION - CALL SERVICE
		try {			
			FooServiceSoapServiceLocator locator = new FooServiceSoapServiceLocator();
			FooServiceSoap service = locator.getPlugin_foo_FooService(_getURL(method, false));
			((Plugin_foo_FooServiceSoapBindingStub) service).setUsername(userId);
			((Plugin_foo_FooServiceSoapBindingStub) service).setPassword(password);

			String match = service.match();
                        System.out.println("Match: "+match);

		} catch (AxisFault e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		super.processAction(actionRequest, actionResponse);
	}

        /**
	 * @param serviceName
	 * @param authenticatedUrl
	 * @return
	 * @throws Exception
	 */
	private URL _getURL(String serviceName, boolean authenticatedUrl) throws Exception {
        String url;
        if(portlet != null) { portlet = "/"+portlet; }
        if (authenticatedUrl) {        	
            url = "http://" + userId + ":" + password + "@"+ host+ ":"+port+portlet+"/api/secure/axis/" + serviceName;
        } else {
            url = "http://" + host+":"+port+portlet+"/api/axis/" + serviceName;
        }
    
        return new URL(url);
    }
}

Wichtig! Für alle die Probleme haben über die _getURL zu kommunizieren, was vor allem Auftritt wenn Server zu Server Verbindungen erstellt werden, muss die AXIS User & Passwort Kombination integriert werden. Hierbei sind diese zwei Zeielen verantwortich:

((Plugin_foo_FooServiceSoapBindingStub) service).setUsername(userId);
((Plugin_foo_FooServiceSoapBindingStub) service).setPassword(password);

Ich freue mich Antworten und hoffentlich konnte ich euch helfen! 😉

Liferay 6.1 – Segmentation fault

Probleme mit Tomcat 7 und Liferay? … Kurz nach dem Start läuft der Server nicht mehr. Die Konsole meldet lediglich Segmentation fault. Kein schöner Fehler und unser Lösungsversuch ist lediglich: JDK Update. Nachdem wir die Java 1.6.24 auf 1.6.34 geupdated haben ist der Fehler verschwunden und der Server läuft wieder stabil.

Liferay – Unable to deploy portlet in Liferay Portal

Problem: Unable to deploy portlet in Liferay Portal Server 6.1.0 CE. Während des deploys erhalte ich den Fehler „war does not support this version of Liferay“ oder “ Add war to the blacklist“.

com.liferay.portal.kernel.deploy.auto.AutoDeployException: sample-struts-portlet-6.1.0.1.war does not support this version of Liferay
 at com.liferay.portal.deploy.auto.PortletAutoDeployer.autoDeploy(PortletAutoDeployer.java:99)
 at com.liferay.portal.deploy.auto.PortletAutoDeployListener.deploy(PortletAutoDeployListener.java:78)
 at com.liferay.portal.kernel.deploy.auto.AutoDeployDir.processFile(AutoDeployDir.java:180)
 at com.liferay.portal.kernel.deploy.auto.AutoDeployDir.scanDirectory(AutoDeployDir.java:222)
 at com.liferay.portal.kernel.deploy.auto.AutoDeployScanner.run(AutoDeployScanner.java:50)
Caused by: com.liferay.portal.kernel.deploy.auto.AutoDeployException: sample-struts-portlet-6.1.0.1.war does not support this version of Liferay
 at com.liferay.portal.tools.deploy.BaseDeployer.deployFile(BaseDeployer.java:746)
 at com.liferay.portal.deploy.auto.PortletAutoDeployer.autoDeploy(PortletAutoDeployer.java:96)
 ... 4 more
06:08:32,312 INFO  [AutoDeployDir:203] Add sample-struts-portlet-6.1.0.1.war to the blacklist 

Mein Problem hierbei war nicht das erstellen des Portlets in einer höheren Version oder einer niedrigeren. Es war lediglich ein Eintrag in einer XML File der gefehlt hat.

Lösung:
Bearbeitet die Datei: „docroot\WEB-INF\liferay-plugin-package.properties“ und fügt folgenden Parameter ein bzw. verändert die Version dementsprechend.

liferay-versions=6.1.0

Jetzt müsst ihr den Server neustarten um das Portlet von der Blacklist zu bekommen. Anschließend sollte es wieder deployed werden können. Wenn das nicht der Fall ist, schaut bitte noch in der anderen File nach: „liferay-plugin-package.xml“

<liferay-versions>
        <liferay-version>6.1.0+</liferay-version>
    </liferay-versions>

Android: setOnClickListener

Aufbauend auf dem ersten Tutorial (Wie fange ich an?) werde ich euch jetzt zeigen wie man seine MainActivity Class richtig befüllt und die erste Aktion über den Button ausführt.

Vorher machen wir noch einen kleinen Ausflug in den Lifecycle einer Android Activity. In der MainActivity werdet ihr ein onCreate(); finden. Diese „Mastermethode“ wird dazu verwendet Buttons und andere Elemente zu initialisieren und evtl. auch mit Leben zu befüllen. Es gibt noch andere Statusse in denen sich die Activity befinden kann. z.B. onView(); Es wird nicht empfohlen in der onView intensive Aufgaben zu initialisieren da sich sonst die ganze Activity in einem stehenden Zustand verwendet. Erst wenn der komplette Prozess abgerufen wäre würde sich die Oberfläche wieder bedienen lassen. Hier noch eine kleine Übersicht des Android Activity Lifecycles.

 

Zurück zu unseren Aktionen die wir auf unseren Button setzen wollen. Per klick soll die nächste Aktivität gestartet werden. Somit wird ein neuer Intent aufgerufen.

Um eine neue Activity aufzurufen müssen wir erstmal eine neue Activity erstellen. Dazu machen wir einen Rechtsklick auf unser Package in dem die MainActivity liegt und erstellen eine neue Klasse, hierbei geben wir als SuperClass Activity an.

Nachdem wir die Events.class erstellt haben benötigen wir noch eine neue LayoutFile für die Events Klasse. Also werden wir das machen. Rechtsklick auf den Ordner layout unter res und schon könnt ihr bei Other auf Android Layout xml File.

Das neue XML File habe ich activity_events.xml getauft. Jetzt könnt ihr gerne meinen Code kopieren. Hierbei wird lediglich eine TextView und eine ListView verwendet.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    

    <TextView
        android:id="@+id/textViewURL"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />
    
    <ListView 
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</LinearLayout>

So, anschließend bearbeiten wir jetzt noch unsere Events.class folgendermaßen:

private static String _URL = "url";

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_events);

	Intent intent = super.getIntent();
	Bundle extras = intent.getExtras();
	String url = (String) extras.get(_URL);

	TextView textViewURL = (TextView) findViewById(R.id.textViewURL);
	textViewURL.setText(url);
}

Hier wird lediglich die neue Activity dargestellt und der übergeben Parameter im TextView wieder ausgegeben.
Um auch die MainActivity dementsprechend anzupassen und eine Aktion auszuführen gibt es jetzt den CodeSnippet für die MainActivity.class

public class MainActivity extends Activity {

	private static String _URL = "url";
	private String url = "http://www.infranken.de/storage/rss/rss/2.0/lokales.xml";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnShowNext = (Button) findViewById(R.id.btnGo);
        btnShowNext.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Intent itn = new Intent(v.getContext(), Events.class);
				itn.putExtra(_URL, url);
				v.getContext().startActivity(itn);
			}
		});

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}

Bevor ihr die App nun starten könnt für einen ersten Testlauf müsstest ihr noch die Manifest File anpassen. Dank dem Sicherheitskonzept von Android, können Activities nicht willkürlich aufgerufen werden.

 

Android: Wie fange ich an?

Ein kleines Tutorial für alle die sich mit Android noch nicht zu sehr auseinander gesetzt haben und einen einfachen Einstig suchen. Wie gehe ich mit einem Button um und wie kann ich die Seite wechseln um neuen Content aufzurufen.

Bevor ich alles in einer Seite reinpack und immer nur gegen neuen Content austausche baue ich lieber neue komplette Seiten auf, inkludiere fertige Elemente und binnen ein paar Minuten läuft die Anwendung super stabil.

Meine alten Tutorials und HowTos haben sich alle auf Android 1.6 – 2.3 bezogen. Jetzt werde ich versuchen euch kleine Starthilfe Tipps für die Versionen 3.0 – 4.1 zu geben. Im Grunde genommen hat sich nicht viel verändert. Es sind nur jede Menge neue Funktionen und Gadget dazu gekommen. Siling Tabviews z.B. ist doch mal richtig cool! Aber back to Basics.

Wir erstellen eine kleine Andorid Applikation, nachdem Bilder mehr sagen als 1000 Worte werde ich versuchen viele Bilder zu verwenden.

Gebt eurer Android App einen guten Namen, den ihr auch weiter verwenden wollt 😉 Ich bin mittlerweile richtig begeistert von dem Werkzeug Version 2.3 zu 4.1 ist ein Meilenstein der sich sehen lässt. Es wird so viel Unterstützung geboten was wir früher umständlich implementieren mussten.

Die Screenshot’s 4 und 5 Zeigen euch den gleichen Step, lediglich die Auswahl ist unterschiedlich. Wollt ihr  vorgefertigtes Layout nutzen um direkt die Kompatibilität mit Tablets zu gewährleisten oder wollt ihr euch frei entfalten und ein blankes Layout verwenden. Ich werde für unser kleines Beispiel das blanke Layout wählen.

Anschließend muss lediglich noch die MainActivity erstellt werden. das geht fix wenn ihr none wählt. Wenn ihr jedoch für Android Versionen jenseits der 11 entwickelt könnt ihr euch sehr schöne Vorlagen, mal wieder zusammen bauen lassen. Da kann man immer etwas experimentieren.

Anschließend läd Android alle notwendigen Daten und fügt sich euch schon zusammen. Es werden alle Ordner erstellt die fürs vernünftige und flotte Entwickeln benötigt werden. Seid froh, das war in den Vorgängerversionen auch nicht so. Da dürfte man stundenlang Dokumentationen wälzen bevor man den richtigen Ordner gefunden hat.

Auf der Linken Seite seht ihr jetzt eure Ordnerstruktur. Hierbei werden im Ordner: r

es/values alle Standardwerte gepflegt. z.b. in der strings.xml alle Werte die später per ID auf Buttons, Texte und alle anderen möglichen Elemente übergeben werden.

Der mittlere Contentbereich in eurer IDE zeigt den ganz normalen Designbereich. Hierbei hat sich auch einiges geändert. Erstellen von Dialogen ist um ein vielfaches erleichtert worden und auch übersichtlicher gestaltet. In meinem kleinen Beispiel habe ich bereits das HelloWorld gegen einen kleinen Standardbutton getauscht. Diesen werde ich in meinem nächsten Kurztutorial mit leben füllen und eine andere Aktivität – setOnClickListener – damit aufrufen.

Primefaces Charts

Kleines HowTo für alle die Chartes von Primefaces benützen möchten. Um es vorweg zu nehmen. Es ist leider nicht möglich eine DateTime X Achse zu nutzen. Wir benützen hierzu um dem ganzen Abhilfe zu schaffen KW/Jahr (Kalenderwoche/Jahr). Es ist natürlich auch möglich Monat/Jahr oder ähnliches zu verwenden.

Primefaces arbeitet hierbei im Backend mit jQuery und einer eigens angefertigten charts.js … Leider hatte ich bisher noch keine Zeit mich genauer mit dieser File auseinander setzen zu können.

Auf der JSF Seite ist es ein Einzeiler. Super von den Primefaces Jungs umgesetzt.

<p:lineChart id="linear" value="#{chartBean.linearModel}" legendPosition="e"  
     title="Linear Chart" minY="0" maxY="10" style="height:300px"/>

Die Java Seite ist etwas umständlicher aber auch hier ist es sehr schlank gehalten.

package org.primefaces.examples.view;  
  
import java.io.Serializable;  
  
import org.primefaces.model.chart.CartesianChartModel;  
import org.primefaces.model.chart.ChartSeries;  
import org.primefaces.model.chart.LineChartSeries;  
  
public class ChartBean implements Serializable {  
  
    private CartesianChartModel categoryModel;  
  
    private CartesianChartModel linearModel;  
  
    public ChartBean() {  
        createCategoryModel();  
        createLinearModel();  
    }  
  
    public CartesianChartModel getCategoryModel() {  
        return categoryModel;  
    }  
  
    public CartesianChartModel getLinearModel() {  
        return linearModel;  
    } 
    private void createLinearModel() {  
        linearModel = new CartesianChartModel();  
  
        LineChartSeries series1 = new LineChartSeries();  
        series1.setLabel("Series 1");  
  
        series1.set(1, 2);  
        series1.set(2, 1);  
        series1.set(3, 3);  
        series1.set(4, 6);  
        series1.set(5, 8);  
  
        LineChartSeries series2 = new LineChartSeries();  
        series2.setLabel("Series 2");  
        series2.setMarkerStyle("diamond");  
  
        series2.set(1, 6);  
        series2.set(2, 3);  
        series2.set(3, 2);  
        series2.set(4, 7);  
        series2.set(5, 9);  
  
        linearModel.addSeries(series1);  
        linearModel.addSeries(series2);  
    }  
}

Ich habe hier zu großen Teilen das Tutorial von Primefaces benützt weil es super einfach beschrieben ist und übersichtlich. Hier gehts zum Orginalbeitrag.