Publié le 15 Décembre 2015

Comme j'ai perdu pas mal de temps récemment avec la signature de mon app iOS j'ai pensé que ça pourrait servir à d'autres presonnes.

Le problème

Donc voilà j'ai récemment réinitialisé ma keychain et j'ai donc dû régénérer tous mes certificats et profiles iOS. Xcode s'en est chargé pour moi et affiche que tout est OK.

​Seulement voilà impossible de déployer mon app, j'obtient à chaque fois une erreur pour le moins cryptique (merci Apple!).

CSSM_SignData returned: 8001094A
Command /usr/bin/codesign failed with exit code 1

Impossible de savoir d'où vient le problème avec un tel message. On se doute bien que quelque chose ne va pas avec les certificats mais quoi?

Tout semble OK dans Xcode, de même que dans l'Apple Member Center.

Mon hypothèse:

Suite à la reinitialisation de la keychain Xcode a effectué une demande de certificat avec mon ancienne clé privée (j'imagine qu'elle devait être en cache quelque part car elle n'était plus dans ma keychain).

Ensuite au moment de signer mon app, j'obtiens ce magnifique message d'erreur. Vous obtiendrait le même message si vous essayez de déployer votre app depuis un autre ordi (sans exporter votre profil - qui exporte votre clé privée entre autre).

Donc maintenant la solution:

​Il faut arriver à vider le cache d'XCode (fermer Xcode et supprimer ~/Library/Developer/XCode/DerivedData/*). Eventuellement redémarrer l'ordi (je ne suis toujours pas sûr de la procédure à suivre pour vider ce cache).

Ensuite il faut révoquer votre certificat car il est lié à l'ancienne clé. Vous pouvez révoquer votre certificat depuis l'Apple Member Center.

Puis démarrer Xcode qui devrait s'apercevoir que vous n'avez pas de certificat pour signer votre app. En choisissant "Fix Issue" lors du build vous devriez arriver à régler le problème.

Voilà pas mal de temps perdu à cause de messages d'erreurs vraiment très clair!!

Voir les commentaires

Rédigé par Bliz

Publié dans #iOS

Publié le 20 Octobre 2015

J'utilise Gatling pour tester mon application (pour l'instant uniquement un service REST).

J'ai une liste de requêtes JSON dans un fichier et je veux que Gatling envoie chacune de ces requêtes à mon service REST.

Mon fichier contient une requête json par ligne. (ce n'est donc pas un fichier json valide mais chaque ligne est un objet json valide indépendamment).

Le problème est que mon fichier contient plusieurs milliers de lignes et qu'il ne tient pas entièrement en mémoire.

La solution que j'ai trouvé consiste à utiliser un "feeder" sur mesure qui va itérer sur les lignes du fichier est injecté chaque ligne json dans la session, de sorte qu'il sera très facile de former les requêtes HTTP.

Voilà ce que ça donne:

package com.patatos.gatling

import java.util.zip.GZIPInputStream

import io.gatling.core.Predef._
import io.gatling.core.action.builder.ActionBuilder
import io.gatling.core.structure.ChainBuilder
import io.gatling.http.Predef._
import io.gatling.http.action.HttpRequestActionBuilder
import io.gatling.http.request.Body

class MySimulation extends Simulation {
   val httpProtocol = http.baseURL("http://localhost:8080")

   private def sendRequest: HttpRequestActionBuilder = http("SendRequest")
      .put("/myservice")
      .header("Content-Type", "application/json")
      .body(StringBody("${json}"))
      .check(status is 200)
   }

   def samples: Iterator[String] = scala.io.Source.fromInputStream(
      new GZIPInputStream(getClass.getResourceAsStream("/gatling_input.gz"))
   ).getLines

   // iterator qui tourne en boucle sur le fichier d'input
   val feeder = Iterator.continually(samples)
      .flatten
      .map(json => Map("json" -> json))

   //scenario
   val scn = scenario("My Simulation").repeat(100000) {
      feed(feeder).exec(sendRequest)
   }

   setUp(scn.inject(atOnceUsers(4)))
      .protocols(httpProtocol)
      .assertions(global.successfulRequests.percent.is(100))

}

Voir les commentaires

Rédigé par Bliz

Publié dans #Scala

Publié le 8 Octobre 2015

Voilà le problème du jour j'ai un Iterator (par exemple qui itère sur les lignes d'un fichier) et j'ai besoin qu'une fois le fichier lu il recommence du début. Une sorte de rolling iterator.

Scala ne fournit pas de méthode directement pour permettre ce type de comportement mais on peut facilement l'implémenter en utilisant:

Iterator.continually[A](f: => A): A

Cette fonction génère un Iterator infini qui appelle f à chaque fois que next() est appelé.

On va donc l'utiliser pour produire un Iterator infini qui fournit un Iterator sur un fichier. On obtient ainsi un Iterator[Iterator[A]] et il ne reste plus qu'à le transformer en Iterator[A] à l'aide de la méthode flatten().

def lines = scala.io.Source.fromFile("monFichier.txt").getLines

def rolling[A](iter: => Iterator[A]): Iterator[A] =
  Iterator.continually(iter).flatten

for ( line <- rolling(lines) ) yield println(line) // runs forever

 

Voir les commentaires

Rédigé par Bliz

Publié dans #Scala

Publié le 25 Septembre 2015

Je viens de tomber sur un comportement du compilateur Scala auquel je ne m'attendais pas et qui peux s'avérer assez pernicieux si le développeur n'a pas conscience de ce fonctionnement.

 

Ceci concerne les pattern matching. Lorsqu'on "pattern match" sur un sealed trait le compilateur peut vérifier que tout les cas sont couvert. Par exemple:

 

sealed trait Record
case class InputRecord(input: String) extends Record
case class OutputRecord(output: String) extends Record

def logRecord(record: Record): Unit = record match {
  case in: InputRecord => println(s"Input record: $in")
  case out: OutputRecord => println(s"Output record: $out")
}

Là pas de problème le pattern match couvre les 2 sous-classes de Record et la compilation passe. Maintenant si j'oublie de traiter un cas:

 

def logRecord(record: Record): Unit = record match {
  case out: OutputRecord => println(s"Output record: $out")
}

Le compilateur le détecte est remonte une erreur car les InputRecord ne sont pas couvert. Super! C'est ce que j'attends du compilateur. Ce qui est plus surprenant maintenant est l'ajout d'une garde (un if quoi):

 

def logRecord(record: Record): Unit = record match {
  case out: OutputRecord if (!out.output.isEmpty) =>
    println(s"Output record: $out")
}

 

On est toujours dans le même cas que précédemment: les InputRecord ne sont pas traités. C'est même pire certains OutputRecord ne sont pas traités. Et pourtant scalac ne remonte aucune erreur, pas même un petit warning.

 

Je comprends bien que le compilateur ne peut pas analyser mon if mais il pourrait assumer que cela couvre les OutputRecord (tout au plus) et sortir une erreur pour les InputRecord (ou plus simplement afficher un warning comme quoi ce pattern match n'a pu être vérifier).

 

Pour plus d'info il y a un bug ouvert à ce sujet: https://issues.scala-lang.org/browse/SI-7631?jql=labels%20%3D%20pattern-matching

Voir les commentaires

Rédigé par Bliz

Publié dans #Scala

Publié le 8 Septembre 2015

Aujourd'hui je travaille sur une appli emberJS et j'ai un service REST qui retourne une structure JSON dont une des propriétés est du texte HTML.

Le fonctionnement par défaut d'Ember est d'échapper tout le contenu qui est rendu dans les template Handlebars. C'est une bonne chose d'un point de vue sécurité.

Seulement dans mon cas j'ai besoin de rendre ce texte non-échappé. (Attention je précise bien que ce contenu est généré uniquement par les administrateurs du site et non par les utilisateurs ce qui ouvrirait une énorme faille de sécurité).

Pour résoudre ce problème j'ai donc défini une propriété calculée sur mon modèle:

App.FaqEntry = DS.Model.extend({
   question: DS.attr('string'),
   answer: DS.attr('string'),
   htmlAnswer: function () {
      return
this.get('answer').htmlSafe();
   }.property('answer')
});

Ensuite il n'y a plus qu'à utiliser directement la propriété htmlAnswer dans le template:

<div>{{entry.htmlAnswer}}</div>

Voir les commentaires

Rédigé par Bliz

Publié dans #Javascript, #EmberJS

Publié le 8 Septembre 2015

Un des avantages de Playframework est qu'il affiche directement le code problématique dans le navigateur. C'est très pratique lors du développement d'une application mais pas vraiment ce qu'on désire une fois en prod.

Par ailleurs on peut vouloir afficher ses propres pages d'erreurs comme par exemple lors d'une page non trouvée.

En fait il est tout à fait possible de définir les comportements à utiliser en définissant un error handler, dans app/ErrorHandler.scala:

import javax.inject._
import play.api.http.DefaultHttpErrorHandler
import play.api._
import play.api.mvc._
import play.api.mvc.Results._
import play.api.routing.Router
import scala.concurrent._

class ErrorHandler @Inject() (
   env: Environment,
   config: Configuration,
   sourceMapper: OptionalSourceMapper,
   router: Provider[Router]
) extends DefaultHttpErrorHandler(env, config, sourceMapper, router) {

   override def onClientError(request: RequestHeader, statusCode: Int, message: String) = statusCode match {
      // 404 - Page non trouvée - on utilise notre propre page d'erreur
      case 404 => Future.successful(NotFound(views.html.Error.notFound()))
      // sinon on utilise le handler par défaut de Play
      case _ => super.onClientError(request, statusCode, message)
   }
}

Voir les commentaires

Rédigé par Bliz

Publié dans #Scala, #Play

Publié le 1 Septembre 2015

Je n'avais jusqu'ici que très peu utilisé handlebars (à la faveur d'angular) mais ce framework m'a plutôt agréablement surpris par sa simplicité et sa facilité d'extension.

C'est d'ailleurs cette fonctionnalité que nous allons utilisé pour définir notre propre formatage.

Handlebars.registerHelper('percentage', function (number) {
   // oui pas terrible comme formatage mais c'est juste pour l'exemple
   // on retourne par exemple "79.85 %"

   return (Math.round(number * 100) / 100) + " %";
});

Ensuite dans notre template on peut utiliser la "directive" "percentage" que nous venons de définir:

<p>Efficacité: {{percentage efficiency}}</p>

percentage est en fait la directive (helper) que nous venons de définir et efficiency est la variable contenant la valeur (pourcent) de l'efficacité.

Bien sûr il existe des librairies de formatage qui prennent en compte l'internationalisation (http://formatjs.io/handlebars) ce qui n'est pas le cas avec notre méthode ici. En revanche j'aime la simplicité de cette solution.

Voir les commentaires

Rédigé par Bliz

Publié dans #Javascript

Publié le 21 Août 2015

Cette erreur survient lorsque la JVM utilise beaucoup de CPU pour ne vider qu'une petite partie de la mémoire. Par exemple lorsque 98% de CPU ne parvient pas à libérer plus de 2% de mémoire. Dans ce cas la JVM lance ce type d'OutOfMemoryError.

Ceci ne doit se produire que dans de rare cas où un programme java crée énormément d'objets temporaires.

On peut désactiver cette erreur en utilisant l'option suivante:

-XX:-UseGCOverheadLimit

Plus d'info disponible dans cet article: Excessive GC Time and OutOfMemoryError

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Publié le 6 Août 2015

Changer le mot de passe neo4j s'effectue lors de la première connection au serveur (par exemple en ouvrant un navigateur sur http://localhost:7474).

Par défaut le login / mot de passe est neo4j / neo4j.

Bien sûr il faut que l'authentification soit activé dans le fichier neo4j-server.properties:

dbms.security.auth_enabled=true

Une fois changer le mot de passe est stocké sous data/dbms/auth.

Pour changer le mot de passe cela peut s'effectuer depuis la console web avec la commande neo4j suivante:

:server change-password

 

Voir les commentaires

Rédigé par Bliz

Publié dans #neo4j, #database

Publié le 21 Juillet 2015

Tout simplement avec brew:

brew install apache-spark

Ensuite il suffit d'utiliser la commande

spark-shell

qui démarre un cluster Spark en mode standalone ansi que la console Spark (Spark REPL)

 

Voir les commentaires

Rédigé par Bliz

Publié dans #mac