2011-01-19 - standalone soap server en java
Ayant divers projets de plugins java sur une application existante (application “lourde”, sans tomcat ou autre futilité dans ce contexte bien précis), je me penche sur les différentes classes disponibles ici et là, afin de ne pas ré-inventer la roue une nouvelle fois.
jSoapServer
Je viens de “tomber” sur jSoapServer, un ensemble de classes aux fonctionnalités bien allêchantes...
Avant de commencer a coder quoi que ce soit, je vais rapidement tester la chose, via la petite démonstration disponible dans l'archive zip disponible sur le site.
Démo
Après de rapides modifications au script shell fourni (pour adaptation au shell que j'utilise), et rapide vérification de "ce qui va se passer", je lanec donc la démonstration.
La chose s'initialise rapidement, et bind deux ports tcp/8090 pour le webservice en lui-même, et tcp/9080 pour la console d'administration (telnet).
Durant la phase de développement, disposer d'un wsdl automatiquement synchronisé avec le code du webservice s'avère être un atoût non négligeable. jSoapServer dispose de cette fonctionnalité, il en va de même pour la démo.
Configuré sur les ports par défaut, l'accès au wsdl se fait sur http://localhost:8090/test. Parfait, voilà tout ce dont nous avons besoin pour faire un rapide essai depuis un client SOAP écrit dans un autre langage que le java.
Essai depuis un client perl
Pour faire simple (et changer un peu), essayons depuis un petit client perl reposant sur SOAP::Lite
1 #!/usr/bin/perl 2 # 3 # client_test.pl : 4 # 5 use strict; 6 use SOAP::Lite; 7 8 my $wsdl = 'http://localhost:8090/test'; 9 10 my $client = SOAP::Lite->service($wsdl); 11 $client->outputxml(1); # we now want to see full xml response :p 12 13 print $client->testIntAdd(42, 1);
Une éxécution plus tard, notre micro-client perl nous affiche la réponse attendue, en xml, comme demandé :
1 <?xml version="1.0" encoding="UTF-8"?> 2 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 3 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 5 <soapenv:Body> 6 <ns1:testIntAddResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 7 xmlns:ns1="http://jSoapServer.org"> 8 <testIntAddReturn xsi:type="xsd:int">43</testIntAddReturn> 9 </ns1:testIntAddResponse> 10 </soapenv:Body> 11 </soapenv:Envelope>
Sauf erreur de ma part ... 42 + 1 ... donne bien 43 :p
Ensuite...
La prochaine étape va consister en l'écriture d'un petit serveur soap autonome, from scratch, n'utilisant que jSoapServer, via inclusion des archives jar dans notre build.xml (ant).
Ensuite, s'il s'avère que la chose reste légère et souple, exposer différentes méthodes sur nos plugins java sera un jeu d'enfant, et facilitera la communication entre nos applications tierces, et nos plugins.
2011-01-16 - soap webservices et php - nusoap et mode wsdl
Après avoir fait joujou avec Zend_Soap_Server, j'ai cherché
quelque chose de plus léger et me suis penché sur NuSOAP.
Facilitant le développement de webservices, grâce à son
auto-génération de wsdl, différents problêmes n'ont
pas tardé à pointer leur nez :
Une même version de nusoap ne fonctionne tout simplement pas d'une
version de php à une autre ... y compris sur une même
“minor” version ...
Faire fonctionner le serveur (basé sur nusoap donc) en mode wsdl est un
peu pénible également et au final on se vois perdre pas mal de
temps inutilement sur des problêmes qui n'existent pas lorsque l'on
utilise directement l'implémentation php.
Voilà pourquoi - contrairement aux avis bien tranchés de certains - je me contenterais dorénavant d'utiliser l'implémentation php pour ce qui concerne soapServer (retour en arrière non négligeable dans le temps) ...
Voici donc un exemple simplissime de webservice soap en php, en mode wsdl :
Le serveur
On repose ici sur la version module apache de php (php 5.3.5 sous apache
2.2.16 sur la machine concernée).
(je me répête, ceci est une version minimaliste, sans gestion
d'erreur ou presque...)
1 <?php 2 /** 3 * @file ws.php 4 * @brief minimalist sample soap webservice. 5 * @details reachable at http://soap.localhost/ws.php 6 */ 7 8 // defines and registers our custom autoloader: 9 require_once('bootstrap.php'); 10 11 // path to static wsdl (may eventually be set to a full url) : 12 $wsdl = 'wsdl/subscription.wsdl'; 13 14 // only useful while tuning your wsdl file: 15 //ini_set("soap.wsdl_cache_enabled", 0); 16 17 // sample, simple options: 18 $soap_options = array( 19 'soap_version' => SOAP_1_2, 20 'encoding' => 'UTF-8' 21 ); 22 23 // instantiate a server using php implementation, binded to our wsdl : 24 $srv = new soapServer($wsdl, $soap_options); 25 26 // attach a single class containing methods exposed by this webservice: 27 $srv->setClass('My_Ws_Service_Subscription'); // our autoloader knows where to find this 28 29 // handle request and send response to agent: 30 $srv->handle(); 31 32 ?>
Méthodes exposées
Pour faire simple, toutes les méthodes exposées par un même webservice sont issues d'une seule classe ; en voici un exemple:
1 <?php 2 /** 3 * @file classes/My/Ws/Service/Subscription.class.php 4 * @brief methods exposed by webservice from /ws.php 5 */ 6 7 /** 8 * @brief simple sample for ws.php soap webservice. 9 * @details all public methods from this class are exposed by webservice. 10 * @note My_Ws_Service_Base may be used to expose 'shared' methods. 11 */ 12 class My_Ws_Service_Subscription extends My_Ws_Service_Base { 13 14 /** 15 * @brief retrieves some informations on given user subscription. 16 * @param integer $userInternalId our internal user identifier. 17 * @param integer $serviceId identifier for one of our public services. 18 * @return array subscription fields/informations. 19 */ 20 public function getinfo($userInternalId, $serviceId) { 21 $uselessExample = array( 22 'registration_stamp' => 3451191601110150, 23 'expiration_stamp' => 3451191631110150, 24 ); 25 26 return ($uselessExample); 27 } 28 } 29 30 ?>
Le wsdl correspondant
Maintenant que le code du “serveur” est en place, écrivons le fichier wsdl utilisé pour décrire le webservice ; ce fichier sera le seul élément nécéssaire aux consommateurs du webservice pour l'écriture de clients.
1 <?xml version="1.0" encoding="UTF-8"?> 2 <definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 3 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 6 xmlns:tns="http://soap.localhost/soap/subscription" 7 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 8 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 9 xmlns="http://schemas.xmlsoap.org/wsdl/" 10 targetNamespace="http://soap.localhost/"> 11 12 <types> 13 <xsd:schema targetNamespace="http://soap.localhost/"> 14 <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> 15 <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" /> 16 </xsd:schema> 17 </types> 18 19 <!-- our input / output messages : --> 20 <message name="getinfoRequest"> 21 <part name="user_id" type="xsd:integer" /> 22 <part name="service_id" type="xsd:integer" /> 23 </message> 24 <message name="getinfoResponse"> 25 <part name="registration_stamp" type="xsd:integer" /> 26 <part name="expiration_stamp" type="xsd:integer" /> 27 </message> 28 29 <!-- our supported operations (methods) and their i/o parameters : --> 30 <portType name="subscriptionPortType"> 31 <operation name="getinfo"> 32 <input message="tns:getinfoRequest"/> 33 <output message="tns:getinfoResponse"/> 34 </operation> 35 </portType> 36 37 <!-- describes our operations : --> 38 <binding name="subscriptionBinding" type="tns:subscriptionPortType"> 39 <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> 40 <operation name="getinfo"> 41 <soap:operation soapAction="http://soap.localhost/ws.php/getinfo" style="rpc"/> 42 <input><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></input> 43 <output><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></output> 44 </operation> 45 </binding> 46 47 <!-- describe our webservice: --> 48 <service name="subscription"> 49 <port name="subscriptionPort" binding="tns:subscriptionBinding"> 50 <soap:address location="http://soap.localhost/ws.php"/> 51 </port> 52 </service> 53 54 </definitions>
Partie clients
Maintenant que nous sommes supposés avoir un webservice, il est temps de l'appeller :
1 <?php 2 /** 3 * @file test_client.php 4 * @brief simple soapclient for our test webservice. 5 * @note minimalist sample. 6 */ 7 8 // either use local copy of wsdl file, or point it to its url: 9 $wsdl = "http://soap.localhost/wsdl/subscription.wsdl"; 10 11 // create a new soap client using provided wsdl : 12 $client = new SoapClient($wsdl); 13 14 // call 'getinfo' remote method with our two 'integer' parameters: 15 $response = $client->getinfo(23, 42); 16 17 // let's see... 18 var_dump($response); 19 20 ?>
Essayons également depuis un petit client perl:
1 #!/usr/bin/perl 2 3 use strict; 4 use SOAP::Lite; 5 6 my $wsdl = 'http://soap.localhost/wsdl/subscription.wsdl'; 7 8 my $client = SOAP::Lite->service($wsdl); 9 10 $client->outputxml(1); # remove me :p 11 12 # optional HTTP basic auth: 13 sub SOAP::Transport::HTTP::Client::get_basic_credentials { 14 return 'soaplite' => 'authtest'; 15 } 16 17 print $client->getinfo(23, 42);
2011-01-13 - webservice soap en php avec generation automatique du wsdl
Si vous avez déjà écrit un webservice soap, vous savez à quel point la génération du wsdl peut être pénible et fastidieuse, d'autant plus si vous écrivez votre webservice en PHP.
Générer dynamiquement un wsdl correspondant réellement au code exposé peut s'avérer très pratique, notamment durant la phase de développement de vos applications. C'est ce que je vais décrire ici : générer automatiquement et dynamiquement le wsdl correspondant à un webservice.
Dépendances
Le framework Zend propose la fonctionnalité
désirée via Zend_Soap_AutoDiscover.
Le code indiqué ici est écrit avec ZendFramework 1.11.2 sur php
5.3.3
Arborescence
Pour faire compliqué, je ne vais pas utiliser les scripts en ligne de
commande de Zend, pour générer l'arborescence/squelette du
projet, mais la créer manuellement.
(nous utiliserons ~/projects/webservice comme répertoire
racine).
1 mkdir -p ~/projects/webservice 2 cd ~/projects/webservice 3 mkdir -p application/views/scripts/error 4 mkdir application/views/scripts/index 5 mkdir application/controllers 6 mkdir application/configs 7 mkdir public 8 mkdir -p library/Exposed 9 mkdir library/Zend
Voici à quoi doit ressembler votre arborescence :
1 ./application 2 ./application/views 3 ./application/views/scripts 4 ./application/views/scripts/error 5 ./application/views/scripts/index 6 ./application/controllers 7 ./application/configs 8 ./public 9 ./library 10 ./library/Exposed 11 ./library/Zend
librairie Zend locale
Nous installons maintenant localement une copie du répertoire “library/Zend” sous library/Zend/ (méthode “moche”) :
1 mkdir /tmp/zf 2 cd /tmp/zf 3 tar zxvf /tmp/ZendFramework-1.11.2.tar.gz 4 mv ZendFramework-1.11.2/library/Zend/* ~/projects/webservice/Zend/
(le fichier Version.php de Zend doit être directement sous library/Zend/Version.php)
Ré-écriture d'url
Pour que Zend “retrouve ses petits”, nous créons public/.htaccess :
1 # public/.htaccess 2 RewriteEngine On 3 RewriteCond %{REQUEST_FILENAME} -s [OR] 4 RewriteCond %{REQUEST_FILENAME} -l [OR] 5 RewriteCond %{REQUEST_FILENAME} -d 6 RewriteRule ^.*$ - [NC,L] 7 RewriteRule ^.*$ index.php [NC,L]
Point d'entrée
Au tour de public/index.php :
1 <?php 2 /** 3 * @file public/index.php 4 */ 5 6 // Define path to application directory 7 defined('APPLICATION_PATH') 8 || define('APPLICATION_PATH', realpath(dirname(__FILE__) . 9 '/../application')); 10 11 // Define application environment 12 defined('APPLICATION_ENV') 13 || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? 14 getenv('APPLICATION_ENV') : 'production')); 15 16 // Ensure library/ is on include_path 17 set_include_path(implode(PATH_SEPARATOR, array( 18 realpath(APPLICATION_PATH . '/../library'), 19 get_include_path(), 20 ))); 21 22 /** Zend_Application */ 23 require_once 'Zend/Application.php'; 24 25 // Create application, bootstrap, and run 26 $application = new Zend_Application( 27 APPLICATION_ENV, 28 APPLICATION_PATH . '/configs/application.ini' 29 ); 30 31 $application->bootstrap() 32 ->run(); 33 34 ?>
Page d'erreur pour debug
application/views/scripts/error/error.phtml :
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title>Zend Framework Default Application</title> 6 </head> 7 <body> 8 <h1>An error occurred</h1> 9 <h2><?php echo $this->message ?></h2> 10 <?php if (isset($this->exception)): ?> 11 12 <h3>Exception information:</h3> 13 <p> 14 <b>Message:</b> <?php echo $this->exception->getMessage() ?> 15 </p> 16 17 <h3>Stack trace:</h3> 18 <pre><?php echo $this->exception->getTraceAsString() ?></pre> 19 20 <h3>Request Parameters:</h3> 21 <pre><?php echo var_export($this->request->getParams(), true) ?></pre> 22 <?php endif ?> 23 24 </body> 25 </html>
Page par defaut
Pour tout de même afficher quelque chose lors de requêtes “mal placées” - application/views/scripts/index/index.phtml
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 3 <head> 4 <meta http-equiv="content-type" content="text/html;charset=utf-8" /> 5 <title>Nothing here</title> 6 </head> 7 <body> 8 <p>Nothing here</p> 9 </body> 10 </html>
bootstrap zend
au tour du bootstrap : application/Bootstrap.php
1 <?php 2 3 class Bootstrap extends Zend_Application_Bootstrap_Bootstrap 4 { 5 6 7 } 8 9 ?>
Configuration de notre application
Plutôt classique, hormis “rootUrl” et “autoloadernamespaces[]” : application/configs/application.ini
1 [production] 2 phpSettings.display_startup_errors = 0 3 phpSettings.display_errors = 0 4 includePaths.library = APPLICATION_PATH "/../library" 5 bootstrap.path = APPLICATION_PATH "/Bootstrap.php" 6 bootstrap.class = "Bootstrap" 7 ; enable autoloader for library/Zend/ and library/Exposed/ : 8 autoloadernamespaces[] = "Zend" 9 autoloadernamespaces[] = "Exposed" 10 resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" 11 resources.frontController.params.displayExceptions = 0 12 ; rootUrl : uri containing '/public/' directory (for soap wsdl): 13 rootUrl = "/" 14 15 [staging : production] 16 17 [testing : production] 18 phpSettings.display_startup_errors = 1 19 phpSettings.display_errors = 1 20 21 [development : production] 22 phpSettings.display_startup_errors = 1 23 phpSettings.display_errors = 1 24 resources.frontController.params.displayExceptions = 1
Classe exposée
Pour un webservice donné (un controller zend), l'ensemble des méthodes de la classe indiquée dans notre futur controller sera exposé par notre webservice : library/Exposed/Subscription.php
1 <?php 2 3 class Exposed_Subscription { 4 5 /** 6 * Sample description for this exposed method. 7 * 8 * @param integer $sampleNumber some integer. 9 * @param string $sampleString some string. 10 * @return array 11 */ 12 public function testMethod($sampleNumber, $sampleString) { 13 $ret = array( 14 'someReturnedField' => ($sampleNumber + 1), 15 'anotherValue' => "your msg: " . $sampleString 16 ); 17 18 return ($ret); 19 } 20 } 21 22 ?>
notre index controller
Presque vide ... application/controllers/IndexController.php :
1 <?php 2 /** 3 * @file application/controllers/IndexController.php 4 */ 5 6 class IndexController extends Zend_Controller_Action 7 { 8 9 public function init() { 10 11 } 12 13 public function indexAction() { 14 15 } 16 17 } 18 19 ?>
Notre controller d'erreur
Controller par defaut : application/controllers/ErrorController.php
1 <?php 2 /** 3 * @file application/controllers/ErrorController.php 4 */ 5 6 class ErrorController extends Zend_Controller_Action { 7 8 public function errorAction() 9 { 10 $errors = $this->_getParam('error_handler'); 11 12 switch ($errors->type) { 13 case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE: 14 case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER: 15 case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION: 16 // 404 error -- controller or action not found 17 $this->getResponse()->setHttpResponseCode(404); 18 $this->view->message = 'Page not found'; 19 break; 20 default: 21 // application error 22 $this->getResponse()->setHttpResponseCode(500); 23 $this->view->message = 'Application error'; 24 break; 25 } 26 27 // conditionally display exceptions 28 if ($this->getInvokeArg('displayExceptions') == true) { 29 $this->view->exception = $errors->exception; 30 } 31 32 $this->view->request = $errors->request; 33 } 34 35 ?>
Le controller de notre webservice
Ce controller “Subscription” fournira le webservice accessible sous /subscription et son wsdl sous /subscription/wsdl : application/controllers/SubscriptionController.php :
1 <?php 2 /** 3 * @file application/controllers/SubscriptionController.php 4 */ 5 6 class SubscriptionController extends Zend_Controller_Action { 7 protected $_exposedClassName = "Exposed_Subscription"; 8 9 protected $_wsdl; 10 protected $_endpoint; 11 12 /** 13 * sets wsdl and endpoint according to configured 'rootUrl'. 14 * 15 * @see application/configs/application.ini 16 */ 17 public function init() { 18 $bootstrap = $this->getInvokeArg('bootstrap'); 19 $configArray = $bootstrap->getOptions(); 20 $config = new Zend_Config($configArray); 21 22 $scheme = $this->getRequest()->getScheme(); 23 $host = $this->getRequest()->getHttpHost(); 24 $rootUrl = $config->rootUrl ? $config->rootUrl : "/"; 25 $controllerName = $this->getRequest()->getControllerName(); 26 27 $this->_endpoint = $scheme . '://' . $host . $rootUrl . $controllerName; 28 29 $this->_wsdl = $this->_endpoint . '/wsdl'; 30 } 31 32 /** 33 * soap server action. 34 */ 35 public function indexAction() { 36 $options = array( 37 'soap_version' => SOAP_1_2, 38 ); 39 $server = new Zend_Soap_Server($this->_wsdl, $options); 40 41 // exposed class, containing exposed methods: 42 $server->setClass($this->_exposedClassName); 43 44 $server->handle(); 45 exit; 46 } 47 48 /** 49 * action that renders WSDL automatically. 50 */ 51 public function wsdlAction() { 52 $wsdlGenerator = new Zend_Soap_AutoDiscover(); 53 $wsdlGenerator->setClass($this->_exposedClassName); 54 $wsdlGenerator->setUri($this->_endpoint); 55 $wsdlGenerator->handle(); 56 exit; 57 } 58 59 ?>
Configuration du virtualhost apache
Rien de particulier ici : le DocumentRoot pointe sur
~/projects/webservice/public
Nous indiquons également notre éventuel environnement de dev:
SetEnv APPLICATION_ENV development
Essai rapide
A titre d'essai, pointez votre navigateur web vers votre virtualHost, sur
l'uri /subscription/wsdl
Vous devez apercevoir le wsdl complet, avec les paramètres
d'entrée et sortie corrects pour la fonction exposée.
Client initial
La partie utile maintenant : un client de test, nous permettant de vérifier le fonctionnement de notre webservice :
1 <?php 2 /** 3 * @file sampleSoapClient.php 4 */ 5 6 // ensure our Zend/ directory is available for includes: 7 $libDir = dirname(__FILE__) . '/library'; 8 set_include_path(get_include_path() . PATH_SEPARATOR . $libDir); 9 10 require_once('Zend/Loader/Autoloader.php'); 11 $zfLoader = Zend_Loader_Autoloader::getInstance(); 12 13 // wsdl location: 14 $wsdl = 'http://localhost/subscription/wsdl'; 15 16 // soap-related options: 17 $options = array( 18 'soap_version' => SOAP_1_2, 19 ); 20 ini_set("soap.wsdl_cache_enabled", "0"); 21 22 // calling 'test()' method from 'Subscription' webservice: 23 $client = new Zend_Soap_Client($wsdl, $options); 24 $response = $client->testMethod(42, "helloWorld"); 25 26 var_dump($response); 27 28 ?>
2011-01-03 - Android et Linux - installation d'applications
Ne pouvant pas télécharger d'application directement depuis mon mobile a l'heure actuelle, je me suis penché sur la question de l'installation d'applications localement, depuis un fichier apk téléchargé précédemment.
Reconnaitre le mobile
Avant d'envisager un quelconque transfert de fichier, il faut s'assurer que le mobile est bien reconnu (nous ne parlons pas ici de l'usb storage - l'installation d'application sur le mobile depuis la SD card n'étant normalement pas possible).
La “magie” réside ici dans udev, renseigné avec le vendorID correspondant à votre mobile (0bb4 pour mon HTC desire).
Création d'une règle udev :
1 SUBSYSTEM=="usb", ATTR{idVendor}=="0bb4", MODE="0666"
(obtenez votre propre vendorID grace a un lsusb des usbutils.
Un redémarrage de udev plus tard, et l'adb (Android Debug Bridge, contenu dans le répertoire “platform-tools” de votre SDK android) liste votre mobile depuis un ./adb devices.
Installation d'une application
Toujours depuis le répertoire platform-tools de votre SDK android, lancez :
1 ./adb install /path/to/your/application.apk
Note : ceci ne concerne pas les applications que vous développez vous-même sous Eclipse, ce dernier effectuant la procédure lors d'une éxécution ou debug.
2011-01-03 - Portabilite du numero - J+4
Vendredi 31 Décembre 2010 ; bien que n'ayant absolument aucun avis de passage, la page de suivi Chronopost indique que mon HTC desire aurait été acheminé jusqu'à mon immeuble, et serait donc retourné au dépôt Chronopost de Maisons-Alfort.
Après plusieurs coup de fil au numéro de téléphone indiqué par chronopost sur mon répondeur, après que l'on m'ait raccroché au nez passé 10 minutes d'attente, j'ai finalement pu joindre une conseillère Chronopost.
Il s'est averé que si je voulais mon mobile avant 2011, il fallait que
je rende visite à mon colis :-)
Une petite heure de transports en commun plus tard, j'ai finalement pu obtenir
le tant attendu mobile :)
Le colis contient bien tous les éléments, ainsi que le contrat
papier que je me suis empressé de poster.
Le numéro temporaire qui m'est assigné s'active
immédiatement, et permet la réception d'appels ; je dois
attendre la réception de mon contrat par orange, et la date du 7
Janvier pour profiter pleinement de ma ligne (Délai le plus court
choisi lors de l'initiation du transfert de numéro).
2010-12-30 - Dev Android - Etape 1 - Installation environnement
Introduction
Curieux de voir ce qu'offre réellement Android en termes de possibilités de développement, et allant recevoir très prochainement mon HTC desire (sous android), je m'initie progressivement au développement d'applications mobiles sous Android.
Cette série d'articles - que j'espère étoffer régulièrement - va présenter mon expérience personnelle dans le domaine, à commencer par les tout premiers pas : la mise en place de son environnement de développement.
Prérequis
La voie préconisée par la documentation se basant sur l'utilisation de Eclipse, une JVM récente et son JDK sont évidemment nécessaires.
Installation de Eclipse
Mes machines personelles fonctionnant toutes sous différentes distributions GNU/Linux, aucune particularité propre à une autre plateforme ne sera décrite ici.
Afin de ne pas nécessiter de privilèges particuliers, l'environnement de développement sera mis en place sous un compte utilisateur sans privilèges particuliers.
Eclipse, ainsi que le SDK android seront donc installés sous le répertoire “share” sous mon homedir:
1 mkdir -p ~/share/eclipse
Nous téléchargeons la version courante de Eclipse pour le dev
java depuis la page de téléchargements de eclipse.
(Pour ma part, je télécharge
“eclipse-java-helios-SR1-linux-gtk-x86_64.tar.gz” [99.2 MB] )
L'archive ainsi téléchargée contient un répertoire “eclipse”, nous pouvons donc l'extraire directement sous ~/share :
1 cd ~/share 2 tar zxvf /path/to/download/eclipse-java-helios-SR1-linux-gtk-x86_64.tar.gz
Par commodité, je créé un alias me permettant de lancer eclipse où que je sois dans l'arborescence :
1 alias eclipse='cd ~/share/eclipse && ./eclipse'
Installation du SDK android
Téléchargé depuis cette page, nous extrayons le SDK sous ~/share/
1 cd ~/share 2 tar zxvf /path/to/download/android-sdk_r08-linux_86.tgz
Ceci créé un répertoire ~/share/android-sdk-linux_86
Installation du plugin eclipse ADT
- Sous Eclipse, depuis Help / Install new Software...
- Cliquer sur “Add” (ajoùt d'un site de téléchargement)
- Dans ce dialogue “Add repository”, saisir “ADT plugin” comme nom, et https://dl-ssl.google.com/android/eclipse/ comme Location.
- Validez en cliquant sur OK ; eclipse effectue une petite mise à jour.
- Dans la partie centrale du dialogue, cocher la case en face de “Developper Tools” pour sélectionner les différents modules.
- Cliquer sur Next
- Cliquer sur next, accepter la license et cliquer sur finish.
- Redémarrer eclipse lorsque demandé
Composants du SDK android
1 cd ~/share/android-sdk-linux_86/tools 2 ./android
Choisir Available Packages dans la liste de gauche,
puis sous “Android Repository”, cocher les versions sur lesquelles
vous souhaitez développer (Android 2.1 pour mon HTC desire), ainsi que les éventuels exemples,
mais aussi “Android SDK Platform-tools”
Une fois les composants sélectionnés, cliquer sur Install selected, accepter toutes les licences et Install.
Configuration du plugin eclipse ADT
Le SDK Android étant maintenant installé, nous pouvons configurer Eclipse pour l'utiliser :
- sous Eclipse: Window / Preferences...
- sélectionner Android dans la liste de gauche
- sur la ligne “SDK location” cliquer sur “Browse”
- choisir ~/share/android-sdk-linux_86
- Cliquer sur apply : une target apparait, avec la plateforme choisie plus haut.
Création d'un device android
La dernière étape consiste en la création d'un ou
plusieurs devices (par exemple l'emulateur).
Sous eclipse, choisir Window / Android SDK and AVD manager
Cliquer sur New, saisir un nom, indiquer une taille de carte SD a emuler,
choisir la plateforme cible ("Target") et cliquer sur create AVD.
Voila ... l'environnement de développement est maintenant prêt.
2010-12-29 - Portabilite du numero - J+2
Nous voilà Mercredi, 2 jours après avoir lancé le changement d'opérateur.
La page de suivi de commande a évolué aujourd'hui ; ma commande est en cours d'expédition, actuellement au hub chronopost de Chilly, il est fort probable que je reçoive mon HTC desire demain matin :)
2010-12-28 - Portabilite du numero - jour J+1
voulant changer de mobile pour passer de mon vieux Nokia E61 aià un HTC desire, j'ai finalement opté pour un changement d'opérateur mobile, afin de bénéficier d'une "offre promotionnelle" proposant ce téléphone.
Ce lundi 27 décembre, peu avant 20 heures, je me suis donc lancé dans la procédure de changement d'opérateur, avec portabilité du numéro.
Cette phase initiale consiste en deux courtes étapes :
- Obtention du RIO (relevé d'identité opérateur) auprès de l'ancien opérateur.
- Souscription à l'offre du nouvel opérateur
Obtention du RIO
Ce numéro à 12 caractères s'obtient via un serveur vocal, sur simple appel depuis la ligne a migrer (appel au 933 depuis une ligne SFR).
Inscription chez le nouvel opérateur
Procédure très classique, hormis le fait que l'on souhaite
conserver son ancien numéro (et donc saisir notre RIO).
Passé quelques minutes, après saisie de ses informations
personnelles / facturation, la procédure est presque terminée ;
il faudra seulement retourner le contrat au nouvel opérateur, lorsque
ce dernier sera acheminé par voie postale.
Edit: ce Mardi 28 Décembre, en fin de soirée, ma commande vient de passer en statut "Validée" sur la page de suivi de mon nouvel opérateur Orange.
2010-10-05 - gitolite - repositories git et ACLs
gitolite se présente comme le successeur de gitosis, il permet d'assigner simplement des ACL à vos différents repositories git.
Utilisant un compte unique sur le serveur centralisant les repositories git à administrer, gitolite utilise les clefs publiques ssh des développeurs pour autoriser ou non l'accès en lecture et/ou écriture à un ou plusieurs repositories.
La configuration des ACL, ainsi que l'ajoùt de nouvelles clefs publiques se fait intégralement via git.
Exemple d'installation sur une machine debian GNU/Linux :
Installation de gitolite
Un simple apt-get install :
1 root@server# apt-get install gitolite
Compte utilisateur dédié
Bien que debian créé un compte 'gitolite' je créé un compte supplémentaire "git" pour plus de commodités durant les accès distants aux repositories:
1 root@server# adduser git
Clé publique de l'administrateur
exporter la clef publique ssh de l'administrateur gitolite et la nommer "sitaram.pub"
1 gitadmin@station$ scp ~/.ssh/id_rsa.pub root@server:/tmp/sitaram.pub
Crétion de l'environnement gitolite
lancer gl-setup sur le serveur :
1 root@server# su - git 2 git@server# gl-setup /tmp/sitaram.pub
configuration distante
La configuration se fait uniquement via le repo gitolite-admin :
1 dev@station$ cd ; git clone git@server:gitolite-admin
placer les clefs publiques ssh des développeurs autorisés à accéder aux repositories dans keydir/
La configuration des groupes et les repositories auxquels ils ont accès
en lecture/écriture se fait via l'edition de conf/gitolite.conf dans ce
repository cloné.
Au moment du push sur le serveur, les nouveaux repositories sont
créés, les nouvelles clefs ssh autorisées...
activation de gitweb par repository
L'activation se fait automatiquement pour chaque repository disposant d'une description :
1 # locally cloned gitolite-admin repo: 2 # gitolite-admin/conf/gitolite.conf 3 # 4 5 @agroup = auserinsomegroup 6 @somegroupname = auser anotheruser @agroup 7 @rogroup = athirduser 8 9 repo somereponame 10 RW+ = @somegroupname 11 R = @anotherrogroup 12 somereponame "owner full name" = "project description"
Accès distant aux repositories
Les URLs d'accès aux repositories sont de la forme "git@server:reponame"
1 user@station$ git clone git@server:somerepo
push d'un repository existant:
1 user@station$ cd existingrepo 2 user@station$ git push --all git@server:reponame 3 user@station$ git push --tags git@server:reponame
2010-10-03 - Installation de nginx et php fastcgi
Venant de réinstaller une distribution linux avec gestion de paquets binaires sur mon petit laptop (Athlon 1400+), j'ai finalement décidé que apache n'y aura pas son emprise cette fois ci :)
Passé la phase d'installation, je dois avouer que je suis pour l'instant plutôt satisfait de la chose ... Plus besoin de "tricher" ou effectuer plusieurs tentatives pour avoir un véritable virtual host "fallback" (entête Host correspondant à une adresse IP, ou non spécifié), des directives simples exploitant la puissance des expressions régulières, bref ... ça fonctionne vite et bien :)
Voici donc la procédure suivie, pour installer nginx avec php5 en fastcgi, sur une debian fraîchement installée, passée en testing (notamment pour me contenter d'un apt-get install tint2, mais plus globalement pour l'éternel "problême" des versions des paquets debian).
Installation de nginx et php
Je me contente pour l'instant de quelques extensions php, j'ajouterais celles dont j'aurais besoin progressivement.
1 apt-get install nginx php5-cgi php5-mysql php5-sqlite psmisc
Configuration initiale de php5 en vue du passage en fastcgi
Dès l'installation terminée, on active cette option dans /etc/php5/cgi/php.ini :
1 # /etc/php5/cgi/php.ini 2 cgi.fix_pathinfo = 1
Script d'init php5 en fastcgi
Créez /etc/init.d/php-fastcgi :
1 #!/bin/bash 2 # /etc/init.d/php-fastcgi 3 # 4 BIND=127.0.0.1:9000 5 USER=www-data 6 PHP_FCGI_CHILDREN=5 7 PHP_FCGI_MAX_REQUESTS=1000 8 9 PHP_CGI=/usr/bin/php-cgi 10 PHP_CGI_NAME=`basename $PHP_CGI` 11 PHP_CGI_ARGS="- USER=$USER PATH=/usr/bin 12 PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN 13 PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS $PHP_CGI -b $BIND" 14 RETVAL=0 15 16 start() { 17 echo -n "Starting PHP FastCGI: " 18 start-stop-daemon --quiet --start --background --chuid "$USER" --exec /usr/bin/env -- $PHP_CGI_ARGS 19 RETVAL=$? 20 echo "$PHP_CGI_NAME." 21 } 22 23 stop() { 24 echo -n "Stopping PHP FastCGI: " 25 killall -q -w -u $USER $PHP_CGI 26 RETVAL=$? 27 echo "$PHP_CGI_NAME." 28 } 29 30 case "$1" in 31 start) 32 start 33 ;; 34 stop) 35 stop 36 ;; 37 restart) 38 stop 39 start 40 ;; 41 *) 42 echo "Usage: php-fastcgi {start|stop|restart}" 43 exit 1 44 ;; 45 esac 46 exit $RETVAL
On lance php en fastcgi au démarrage :
1 update-rc.d php-fastcgi defaults
Support php dans un vhost
Dans un virtualhost nginx :
1 # /etc/nginx/sites-available/localhost 2 # 3 4 # ... 5 location ~ \.php$ { 6 root /var/www/localhost; 7 fastcgi_pass 127.0.0.1:9000; 8 fastcgi_index index.php; 9 fastcgi_param SCRIPT_FILENAME /var/www/localhost$fastcgi_script_name; 10 include fastcgi_params; 11 } 12 # ...
Test de la config
1 echo "<?php phpinfo(); ?>" > /var/www/localhost/info.php 2 /etc/init.d/php-fastcgi start 3 /etc/init.d/nginx start
un accès à http://localhost/info.php affiche bien les infos php, avec ServerAPI=CGI/FastCGI, et www-data en user.
