I have this library that I work on from time to time, that I published on Maven Central. A few days ago I released
a new version, and since I always forget what the exact mvn command is, what my GPG passphrase is, etc. I thought it
would be a good opportunity to automate the release process using GitHub Actions.
I came across several outdated tutorials that create settings.xml and import GPG keys manually, but as of Feb. 2021 most
of this is not needed anymore, thanks to recent changes in the setup-java action.
Using the JMS API to do messaging over IBM MQ is rather easy, but writing programs that
perform well can be a bit tricky. In this post, I’m going to share a few tips I’ve gathered here
and there that might help you write faster MQ+JMS applications.
My examples are based on MQ 9 and JMS 2.0 but most of the tips will likely work with
other versions of these products, and some of them can even apply to other message brokers.
So you want to try Ceylon on a new side project, but don’t feel like writing yet another boring command line application? In this article, I will show you how to set up a new Ceylon application that uses jOOQ seamlessly. We will see how easy it is to use Ceylon’s Java interoperability, and how easy it is to customize jOOQ and adapt it to Ceylon’s conventions. The sample application will use an existing database.
Lundi matin, aux environs de 9 h 30. Vous arrivez un peu en retard, mais bon c’était pour la bonne cause, il fallait impérativement finir de merger ces pull requests sur votre projet perso et vous en avez eu jusqu’à 3h du mat’. Même pas le temps d’aller prendre une boisson chaude pour se réveiller, le téléphone sonne : une anomalie ultra-bloquante-qui-va-mettre-en-péril-le-projet-c’est-pas-possible vient d’être ouverte, c’est la catastrophe bla bla bla… Classique, me direz-vous, mais comment trouver facilement l’origine du problème ?
Un billet sur le blog de l’Agilitateur répondant à un billet sur le blog d’Excilys concernant les switchs en java me donne envie de parler un peu de codage en Java. Cet article est une variante du code C# proposé par Oaz en Java.
Je vais vous épargner toute la discussion sur le switch c'est bien / c'est nul, il y a déjà les commentaires enflammés sur le blog d'Excilys pour ça.
Passons directement au code : de la même manière qu'Oaz l'a fait, je dispose d'une interface Function qui définit le contrat que vont suivre les différents cas du switch. Pour que l'on sache que les différentes sous-classes sont bien des cases, on les annote avec un @Case créé pour l'occasion :
On demande au compilateur de garder cette annotation au runtime (pour faire du scanning dynamique), et on précise qu'elle peut s'appliquer à un type (classe ou interface).
On peut alors utiliser l'annotation de cette façon :
publicinterfaceFunction{voiddoYourJob();}@Case(name="bar")publicclassBarFunctionimplementsFunction{publicvoiddoYourJob(){System.out.println("On va manger... des chips ! T'entends ? DES CHIPS !!");}}@Case(name="foo")publicclassFooFunctionimplementsFunction{publicvoiddoYourJob(){System.out.println("Hi, my name is foo!");}}@Case(name="othercase")publicclassOtherCase{publicvoiddoYourJob(){System.out.println("I'm not gonna be called :-(");}}
Reste maintenant le plus dur à faire : scanner un package à la recherche de classes annotées @Case et qui héritent de notre classe de base (ici Function, mais on va essayer de rester génériques).
publicclassSwitch<T>{privateStringpackageName;privateMap<String,T>cases;privateClass<T>parentType;publicSwitch(StringpackageName,Class<T>parentType){this.packageName=packageName;this.parentType=parentType;scanCases();}privatevoidscanCases(){cases=newHashMap<String,T>();try{for(Classclazz:getClasses(packageName)){if(clazz.isAnnotationPresent(Case.class)&&isA(clazz,parentType)){CasetheCase=(Case)clazz.getAnnotation(Case.class);cases.put(theCase.name(),(T)clazz.newInstance());}}}catch(Exceptione){e.printStackTrace();}System.out.println("Scanned "+cases.size()+" cases.");}privatebooleanisA(Classclazz,Class<T>class1){if(clazz.getSuperclass()==class1){returntrue;}for(Classintf:clazz.getInterfaces()){if(intf==class1){returntrue;}}returnfalse;}publicTon(StringcaseName)throwsIllegalArgumentException{if(cases.containsKey(caseName)){returncases.get(caseName);}else{thrownewIllegalArgumentException("The case "+caseName+" does not exist");}}privateList<Class>getClasses(Stringpckgname){...}
J'ai fait appel au generic type introduit en Java5, cela permet d'avoir un switch acceptant n'importe quelle classe de base qu'implémentent les différents cas. J'ai été obligé de repasser cette information dans le constructeur car en Java il n'est pas possible de faire un T.class dans la phase de scan des classes (pour savoir si la classe scannée étend ou implémente T). À la place, on a donc un Class en second paramètre.</p>
La méthode scanCases va parcourir toutes les classes d'un package (grâce à la méthode getClasses() honteusement trouvée sur le forum de Sun Oracle). Pour chaque classe, on regarde si elle est annotée @Case et si elle étend ou implémente T. Si c'est le cas, on en ajoute une instance dans une Map avec le nom du cas comme clé.
Dernière étape : la méthode on() qui renvoie l'instance associée à un cas ou jette une exception s'il n'existe pas.
La glue entre tout ça, c'est un main assez simple :
Notez le dernier appel qui va planter : j'ai bien une classe annotée @Case(name="othercase") mais elle n'implémente pas Function (une ruse de sioux pour égarer le lecteur peu attentif ;-) ).
Conclusion : c'est aussi possible en Java, avec un peu de gymnastique intellectuelle pour arriver à faire ce qu'on souhaite avec les generics :P
Parfois, au cours d'une mission chez un client, on tombe sur du code pas testé, et pas toujours prévu pour être testé non plus. Je vais présenter un cas sur lequel je suis tombé récemment, issu d'un assez gros projet Java EE en cours de développement.
Une classe de services X effectue une certain nombre de traitements. En cas d’erreur, au lieu de jeter une exception métier, un code d’erreur est loggué. Impossible d’utiliser un @Test(expected=...), donc. Fort heureusement, le champ logger dans X est une interface, potentiellement mockable :
Dans tous les cas où nous sommes intéressés, la méthode logError est appelée avec un paramètre Key représentant le code d’erreur. Malheureusement, le logger n’est pas injecté dans un setter, et il est privé. On aimerait tout de même vérifier que l’erreur est bien logguée sous certaines conditions. Solution, un peu de réflexion pour forcer le champ logger à une instance que l’on contrôle :
Et voila, nous pouvons maintenant faire des assertTrue utilisant la méthode wasLogged et la Key nous intéressant ! Notez l’appel à Field#setAccessible(boolean) pour contourner le caractère private du champ logger (que l’on restaure par la suite).
Note : si le logger n’avait pas utilisé d’interface, nous aurions pu nous en sortir en créant une classe fille redéfinissant toutes les méthodes sans appels à super().
Le haut débit c'est bien, mais des fois on a quand même un côté nostalgique du 56k, soit parce qu'on a envie d'embêter les visiteurs de son serveur, soit parce que pour débugger une application (par exemple) on a envie que les données arrivent lentement. Sous linux, il existe la commande tc, pour traffic control, qui permet de contrôler ses connexions réseaux, notamment leur débit...
J'ai donc trouvé un script tout fait permettant de limiter la bande passante d'une interface réseau, en upload ou en download.
#!/bin/bash## tc uses the following units when passed as a parameter.# kbps: Kilobytes per second # mbps: Megabytes per second# kbit: Kilobits per second# mbit: Megabits per second# bps: Bytes per second # Amounts of data can be specified in:# kb or k: Kilobytes# mb or m: Megabytes# mbit: Megabits# kbit: Kilobits# To get the byte figure from bits, divide the number by 8 bit### Name of the traffic control command.TC=/sbin/tc
# The network interface we're planning on limiting bandwidth.IF=eth0 # Interface# Download limit (in mega bits)DNLD=1mbit # DOWNLOAD Limit# Upload limit (in mega bits)UPLD=1mbit # UPLOAD Limit# IP address of the machine we are controllingIP=216.3.128.12 # Host IP# Filter options for limiting the intended interface.U32="$TC filter add dev $IF protocol ip parent 1:0 prio 1 u32"
start(){# We'll use Hierarchical Token Bucket (HTB) to shape bandwidth.# For detailed configuration options, please consult Linux man# page.$TC qdisc add dev $IF root handle 1: htb default 30
$TC class add dev $IF parent 1: classid 1:1 htb rate $DNLD$TC class add dev $IF parent 1: classid 1:2 htb rate $UPLD$U32 match ip dst $IP/32 flowid 1:1
$U32 match ip src $IP/32 flowid 1:2
# The first line creates the root qdisc, and the next two lines# create two child qdisc that are to be used to shape download # and upload bandwidth.## The 4th and 5th line creates the filter to match the interface.# The 'dst' IP address is used to limit download speed, and the # 'src' IP address is used to limit upload speed.}
stop(){# Stop the bandwidth shaping.$TC qdisc del dev $IF root
}
restart(){# Self-explanatory.
stop
sleep 1
start
}
show(){# Display status of traffic control status.$TC-s qdisc ls dev $IF}case"$1"in
start)echo-n"Starting bandwidth shaping: "
start
echo"done";;
stop)echo-n"Stopping bandwidth shaping: "
stop
echo"done";;
restart)echo-n"Restarting bandwidth shaping: "
restart
echo"done";;
show)echo"Bandwidth shaping status for $IF:"
show
echo"";;*)pwd=$(pwd)echo"Usage: tc.bash {start|stop|restart|show}";;esacexit 0
C'est un fait avéré, Netbeans c'est mieux qu'Eclipse. (comment ça, un troll tout poilu ?). Pas plus tard qu'il y a une minute, j'en ai refait l'heureuse expérience. Agacé de voir une série de getters/setters dans ma classe, qui sont triviaux à comprendre mais qui prennent de la place, j'ai voulu utiliser l'editor folding, propre à Netbeans (je crois, du moins).
Même si je ne suis pas trop fan de Deezer pour diverses raisons (interface Flash trop lourde, beaucoup trop de pub, ...), je suis quand même bien content de pouvoir y rechercher une musique que j'ai en tête et que je peux récupérer pour la réécouter par la suite...
En effet, il est de notoriété publique que ce système met en cache la chason en cours d'écoute et la stocke dans un fichier flv nommé /tmp/Flash* sous linux.
À partir de là, il est facile de créer un script shell qui va se charger de récupérer ce ficher, puis d'en extraire la musique en MP3 et de la sauvegarder dans notre dossier de musique favori. Top du top, le script que je vous propose par la suite permet également d'éditer les tags ID3 et de renommer le fichier en conséquence, puis ajoute le morceau à la playlist Amarok en cours :D
symfony 1.2 was just released, and came along with a brand new advent calendar. The first day mentions two ways to set up your web server to host a symfony project. The ugly way seems to be very... ugly and crazy, putting your app directly in your webserver's root folder allows anyone to access all the files. But this security hole can be easily found in 'real' apps, as well as a few others...
This article will try to enumerate some of the mistakes that are often done when a symfony application is put in production environment.
Development environment is still present
These files, ending with _dev.php, are very useful when you are coding: they provide a lot of information on the configuration, the SQL queries that were executed, your PHP version etc. But on the other hand, they should never be accessible to your visitors (I mean, bad ones, you know, those who type in the darkness). They can show your kernel version, the symfony plugins which are installed, the escaping method you are using (or not using...), etc., which can lead to other vulnerabilities.
The project root is the webserver's root folder
This can also give a lot of information to an attacker, especially your database connection settings. Just search for 'web/frontend.php' in your favorite search engine, and you will have a bunch of apps which are potentially in this case. Then you just have to browse config/databases.yml and you get what you want. Just consider that if the application is unsecure, the webserver which is running it is also unsecure, and you can have a remote access to MySQL on port 3306, which will show you a lot of interesting things...
The backend is not protected
Harder to find, but not impossible. Even if this page is not indexed by search engines, it's quite easy to see if a web app is powered by symfony, and the majority of these web apps have a backend which is called 'backend' (if not, just try 'admin' :)). So it's very important to restrict access to this backend, as it can contain sensitive information (user accounts, etc.). Imagine that you didn't make a recent backup of your database, and that someone deletes all its content...
How to fix it?
It's not difficult to avoid these problems. In fact, the solutions are very well explained in the official symfony book, but nonetheless they are often not applied.
Check that *_dev.php files are not uploaded (see the rsync_excludes.txt file for example)
If you can, put the whole project (except the web/ folder) outside the web document root
Protect your backend with sfGuard, or at least a htaccess/htpasswd file :) (and do not user admin/admin or something like this)