[OwnCloud] Einrichten auf Synology NAS

Wer seine Daten (Kalender, Kontaktdaten, Dateien) nicht mehr in die Hand von Firmen geben will, kann versuchen mit OwnCloud seine Bedürfnisse zu befriedigen. Irgendwo muss die OwnCloud-Installation laufen. Bei mir hat sich mein Synology-NAS angeboten. Dort kann man verschiedene Anwendungen nachinstallieren, um das NAS entsprechend an die eigenen Bedürfnisse anpassen zu können. OwnCloud wird leider nicht standardmässig zur Installation angeboten. Kurzes googeln und es fand sich eine Anleitung, die auch funktioniert hat.

Unter http://www.eg-blog.de/?page_id=639 fand sich eine einfache Anleitung. Wenn man OwnCloud einfach nur mal kurz antesten will, reicht diese Anleitung aus. Will man OwnCloud „richtig“ nutzen, sollte man sich noch die erweiterte Anleitung durchlesen: http://www.eg-blog.de/?page_id=639.

Neben dem in der Anleitung aufgeführten Anwendungsrepository http://www.cphub.net haben ich noch das von http://www.synocommunity.com/ eingebunden. Die Anleitung, wie es einzurichten ist, findet sich unter http://www.synocommunity.com/faq.

Unter http://doc.owncloud.org/server/5.0/user_manual/calendars.html#synchronising-calendars-with-caldav findet sich eine Anleitung, wie man an die Kalenderdaten kommt und mit welcher Adresse man es in entsprechenden mobilen Apps einbinden kann. Für das Syncen der Kalenderdaten nehme ich https://play.google.com/store/apps/details?id=org.dmfs.caldav.lib.

Unter http://doc.owncloud.org/server/5.0/user_manual/contacts.html gibt es eine Anleitung, wie man an die Kontaktdaten kommt. Fürs syncen der Daten habe ich https://play.google.com/store/apps/details?id=org.dmfs.carddav.Sync genutzt.

[Mac] Mac wechselt nicht mehr in den Ruhezustand

Ein Freund von mir hatte das Problem, dass sich sein Mac nicht mehr in den Ruhezustand versetzen lies. Er ist immer wieder aus dem Ruhezustand von selbst aufgewacht, obwohl er das nicht machen sollte. Wie sich rausstellte, war das Problem sein Drucker.

Mittels „pmset -g assertions“ im Terminal gab es folgende Informationen:

18.03.12 16:12:36 MEZ   
Assertion status system-wide:
   ChargeInhibit                           0
   PreventUserIdleDisplaySleep             0
   PreventUserIdleSystemSleep              0
   NoRealPowerSources_debug                0
   CPUBoundAssertion                       0
   EnableIdleSleep                         1
   PreventSystemSleep                      1
   DisableInflow                           0
   DisableLowPowerBatteryWarnings          0
   ExternalMedia                           1

Listed by owning process:
  pid 30: [0x0000012c0000001e] ExternalMedia named: "com.apple.powermanagement.externalmediamounted" 
  pid 514: [0x0000012c00000202] PreventSystemSleep named: "org.cups.cupsd"

PreventSystemSleep zeigt an das es knarzt, sollte auf 0 sein. Unten in der Prozessliste sieht man dann, dass cupsd das problem verursacht. Er hat dann bei den Druckeinestellungen nachgeschaut, und irgendwie stand da was das der Drucker online oder schlafend wäre, was er aber nicht war. Das hatte sich dort restartresistent festgesetzt. Er hat es dann gelöscht. Anschliessend wurde der Drucker offline gezeigt und der Ruhezustand ging wieder.

[Android] RESTEasy und Android

Wurde eine REST-Schnittstelle auf Server-Seite mit RESTEasy implementiert, gab es bisher keine wirkliche Möglichkeit, mit RESTEasy den vorhandenen Code auch auf Android-Seite zu nutzen. RESTEasy funktionierte bisher nicht unter Android. Thomas Diesler hat glücklicherweise ein Projekt auf github (https://github.com/tdiesler/resteasy-mobile) gestartet, welches diesen Teil nachimplementiert. Er hat hierzu auch einen Blogpost verfasst.

Nachfolgende die Kurzform der notwendigen Schritte. Ich habe den Code in drei Projekte aufgeteilt:

  • REST-Interface und Transferobjekte
    In diesem Projekt werden die Transferobjekte und das REST-Interface implementiert. Transferobjekte sind die Klassen, die die Daten zwischen Server und Client kapseln. Wie der Name REST-Interface schon vermuten lässt, wird damit die Schnittstelle zwischen Client und Server definiert (aber noch nicht implementiert). Nutzt Maven.
  • REST-Server-Implementierung
    Die Implementierung der Backend-Logik. Benutzt das REST-Interface und die Transferobjekte. Nutzt Maven.
  • REST für Android
    Die Implementierung der Android-Client-Seite. Benutzt das REST-Interface und die Transferobjekte.

Für das REST-Interface und Transferobjekte wird folgende Dependency in der pom.xml eingetragen:

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>1.2.1.GA</version>
</dependency>

Für die REST-Server-Implementierung werden folgende Dependencies in der pom.xml eingetragen:

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>1.2.1.GA</version>
</dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxb-provider</artifactId>
 <version>1.2.1.GA</version>
 </dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>jaxrs-api</artifactId>
 <version>1.2.1.GA</version>
</dependency>

<dependency>
 <groupId>net.sf.scannotation</groupId>
 <artifactId>scannotation</artifactId>
 <version>1.0.2</version>
</dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jackson-provider</artifactId>
 <version>2.3.1.GA</version>
</dependency>

<!-- Reference to REST-Interface-Project -->
<dependency>
 <groupId>de.yellowshoes.rest</groupId>
 <artifactId>restinterface</artifactId>
 <version>0.0.1-SNAPSHOT</version>
</dependency>

Auf Android-Seite ist kein Maven im Einsatz. Folgende Libraries werden im Buildpath mit aufgenommen:

  • Jackson Core ASL 1.6.3
  • Jackson JAXRS 1.6.3
  • Jackson Mapper ASL 1.6.3
  • Jackson XC 1.6.3
  • JAXRS API 2.2.1 GA von RESTEasy
  • RESTEasy Mobile 1.0.0

Nachfolgend ein einfaches Beispiel, wie eine konkrete Implementierungen aussehen kann:

REST-Interface

Zuerst die Transferklasse. Hier ein Objekt Book. Ein simples POJO, mehr nicht.

public class Book
 {
 private String titel;
 private String author;

 public String getTitel()
 {
 return titel;
 }

public void setTitel( String titel )
 {
 this.titel = titel;
 }

public String getAuthor()
 {
 return author;
 }

public void setAuthor( String author )
 {
 this.author = author;
 }
}

Nun das REST-Interface. Die @Path-Annotation definiert jeweils, an welche Url die Klasse/die Methode gebunden wird. Hier wird das komplette Interface an /api gebunden, die Methode (durch die Vererbung) an /api/book. Mit den Annotations @Consumes und @Produces wird festgelegt, was für MediaTypes verarbeitet werden können und was zurückgeschickt werden kann. Hier wird jeweils JSON definiert.

@Path( "/api" )
@Consumes( MediaType.APPLICATION_JSON )
@Produces( MediaType.APPLICATION_JSON )
public interface RestResourceInterface
 {
 @GET
 @Path( "/book" )
 public Book getBook();
 }

REST-Implementierung

Auf Server-Seite wird nur eine Klasse benötigt.

public class RestResource implements RestResourceInterface
 {
 public Book getBook()
  {
  Book book = new Book();
  book.setAuthor( "Hans Müller" );
  book.setTitel( "REST gut" );
  return book;
  }
}

REST-Android

Auch auf Android-Seite muss nicht mehr viel gemacht werden. Hier eine einfache Client-Implementierung. Das wichtigste ist die Methode getClient(), die über die ProxyFactory die Schnittstellen-Implementierung initialisiert. Der Code sollte ansonsten selbsterklärend sein.

public class ResteasyClient implements RestResourceInterface
 {
 RestResourceInterface client = null;

 @Override
 public Book getBook()
  {
  RestResourceInterface client = getClient();
  Book book = client.getBook();
  return book;
  }

 private RestResourceInterface getClient()
  {
  String requestURI = "http://192.168.11.86:8080/restest/";

  if( client == null )
   {
   BasicHttpParams params = new BasicHttpParams();
   HttpProtocolParams.setVersion( params, HttpVersion.HTTP_1_1 );
   HttpProtocolParams.setContentCharset( params, HTTP.DEFAULT_CONTENT_CHARSET );
   HttpProtocolParams.setUseExpectContinue( params, false );
   client = ProxyFactory.create( RestResourceInterface.class, requestURI, new ApacheHttpClient4Executor( params ) );
   }
  return client;
  }
 }

 

G+ API in Java nutzen

Mit diesem Blogpost soll Beschrieben werden, wie man auf der Google+ API mittels Java zugreifen kann. Da die Dokumentation von Google leider nicht so berauschend ist und auch ein wenig verstreut, trage ich hier einfach mal alles zusammen, was ich auf dem Weg zur Benutzung gefunden habe. Grund ist unter anderem, dass wir bei der GTUG RM einen kleinen Hackathon zur G+ API veranstalten.

Vorbereitung

Um mit der Google+ API arbeiten zu können benötigt man folgende Dinge:

API-Key

Zuerst registriert man sich einen API-Key. Das macht man über die Google API-Console. Bei mir sieht es nach der Registrierung dann so aus. Wichtig ist noch, das man sich eine oder mehrere Redirect URIs einträgt, die man später auch nutzt.

 

Libs

Um auf die API zugreifen zu können, benötigt man zwei Libs-Pakete von Google. Zum einen die Google APIs Client Library for Java (Stand 1.5.1 Beta) und die Google+ API (Stand 1.2.5 Beta).

Da ich kein Maven genutzt habe, musste ich die benötigten Libs manuell zusammensuchen.

Unter der Setup-Seite für die Google APIs Client Library for Java finden sich die Informationen, welche Libs grundsätzlich benötigt werden. Die abhängigen Libs (Dependencies) werde ebenfalls aufgeführt. Netterweise liefert Google alle benötigten Libs gleich mit dazu (zu finden im Unterverzeichnis dependencies der Zip-Datei).

Folgende Libs werden benötigt:

Umsetzung

OAuth

Um die G+ API nutzen zu können, braucht man zum Teil eine OAuth-basierte Autorisierung (man bekommt einen sogenannten Access-Token für die weiteren Zugriff auf die APIs) und/oder einen API-Key. Infos dazu finden sich auf der G+ API OAuth-Seite.

Für die Nutzung von OAuth stellt Google Beispielcode bereit.

Mein Beispiel ist als Web-App (für die Google Appengine) ausgelegt.

Code

Autorisierung anfordern

Um die Autorisierung und damit den Access-Token anzufordern benötigt man folgenden Code:

private void doAuthRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException {
   String redirectUrl = getRedirectUrl(req);
   String authorizeUrl = new GoogleAuthorizationRequestUrl(CLIENT_ID, redirectUrl, SCOPE_GOOGLE_PLUS ).build();
   resp.sendRedirect(authorizeUrl);
}

private String getRedirectUrl( HttpServletRequest req ){
   return "http://" + req.getServerName() + ":" + req.getLocalPort() + "/start";
}

Die CLIENT_ID ist die ID als String, die man in der API-Console zugeordnet bekommen hat. SCOPE_GOOGLE_PLUS erhält den sogenannten Scope der verwendeten API als String, in diesem Fall also für die Google+ API. Der passende Scope-Wert findet sich auf der G+ OAuth-Seite und hat den Wert https://www.googleapis.com/auth/plus.me.

Autorisierungscode auslesen

Nach erfolgter Autorisierung springt die OAuth-API zurück zur angegebenen Redirect-URL. Im Erfolgsfall wird ein Code als Parameter in der URL mitgeschickt, den man ausliest und zum Abfragen eines Access-Tokens verwenden muss.

private void doAuthExtract(HttpServletRequest req, HttpServletResponse resp) throws IOException {
   String code = req.getParameter( "code" );
   HttpTransport TRANSPORT = new NetHttpTransport();
   JsonFactory JSON_FACTORY = new JacksonFactory();
   GoogleAuthorizationCodeGrant authRequest = new GoogleAuthorizationCodeGrant(TRANSPORT, JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, code, getRedirectUrl(req));
   authRequest.useBasicAuthorization = false;
   AccessTokenResponse authResponse = authRequest.execute();
   String accessToken = authResponse.accessToken;

   if( session != null ){
      session.setAttribute( SESSION_PARM_ACCESS_TOKEN, accessToken);
      session.setAttribute(SESSION_PARM_REFRESH_TOKEN, authResponse.refreshToken);
      this.accessToken = accessToken;
      this.refreshToken = authResponse.refreshToken;
   }

   resp.sendRedirect( getRedirectUrl(req));
}

Somit haben wir jetzt den Access-Token und können damit auf die G+ API zugreifen.

Zugriff auf G+ API

Ein einfaches Beispiel, ohne den Bezug zur Webanwendung, der Zugriff auf die Personensuche.

Plus plus = new Plus( new NetHttpTransport(), new JacksonFactory() );
plus.setKey( API_KEY );
plus.setOauthToken( accessToken );

Search search = plus.people.search();
search.setQuery(query);
PeopleFeed peopleFeed = search.execute();

if( peopleFeed.isEmpty() == false ){
   List<Person> peopleList = peopleFeed.getItems();

   if( peopleList != null ){
      for (Person person : peopleList) {
         System.out.println( person.getDisplayName() );
      }
   }
}

API_KEY ist der API-Key, der über die Google API-Console erstellt wurde. accessToken ist der über OAuth zugewiesene Access-Token und query ist schliesslich der Suchtext.

Die Plus-Klasse (JavaDoc) ist generell der Ausgangspunkt. Über sie greift man auf die G+ API zu.

Von Seiten Google wird noch ein Starter Projekt bereitgestellt, in dem man sich den Sourcecode als Ausgangspunkt für eigene ENtwicklungen vornehmen kann.

Mein Beispielprojekt findet sich bei Google Code.

[Mac] Maus und Tastatur sharen

Um über eine Tastatur und Maus mehrere Rechner zu steuern, gibt es mittlerweile diverse Softwarelösungen.

Teleport (Mac only)

Einfache und kostenlos zu installierende Lösung. Funktioniert einfach und ist bei mir jetzt im Einsatz.
http://abyssoft.com/software/teleport/

Synergy (Win, Mac, Linux)

Kostenlose Lösung, bei der unter Mac noch eine Konfigurationsdatei editiert werden muss.
http://synergy-foss.org/

ScreenRecyler (Mac, Clients auch Win)

Kommerzielle Lösung.
http://www.screenrecycler.com/

[Android]: Fehler beim Deployment in Eclipse-Console

Bekommt man in der Eclipse-Console folgende Meldungen angezeigt:

[2011-02-14 15:35:18 - ddms]transfer error: Too many open files [2011-02-14 15:35:18 - Device]Error during Sync: Too many open files [2011-02-14 15:35:22 - ddms]ADB rejected shell command (ls -l /): closed

Dann am Besten den adb-Server neu starten:

 ./adb start-server
adb server is out of date.  killing…
* daemon started successfully *

[Android]: Fehler Reporting in eigene Apps einbauen

Seit Android 2.2 wird ein Fehlerreporting „nativ“ unterstützt. Das heisst, der Benutzer kann einen Fehlerreport an den Entwickler schicken lassen, wenn die App abgestürzt ist. Dies geht allerdings nur, wenn die App über den Google Market verbreitet wird. Da wir bei Dailyplaces gerade eine Alpha-Phase für unsere Android-App am Laufen haben und die Version nicht über den Google Market verbreiten wollten, musste eine Alternative her. Zuerst wollte ich eine eigenen Implementierung eines UncaughtExceptionHandlers beginnen, habe mich dann aber noch mal etwas umgeschaut und bin schliesslich auf ACRA gestossen.
Das besondere an ACRA ist, dass man sich nicht um client- oder serverseitige Implementierungen kümmern muss. Die Fehlerreports werden einfach in ein Google Docs-Sheet eingefügt. Wenn man die Lösung mit Google Docs nicht nutzen möchte kann man aber auch gerne einen eigenen Serverdienst dafür umsetzen.
Eine weitere Besonderheit ist in meinen Augen die gute Dokumentation. Das Einbinden in die eigene App hat out-of-the-box funktioniert, da alle Schritte sauber dokumentiert werden.