Ce réflexe est malheureusement partagé
par un grand nombre de développeurs ou d'architectes et les
habitudes sont parfois la cause de la complexité inutile de
certaines applications. Nous avons la chance en tant que développeur dans l'écosystème Java de disposer d'une
multitude de librairies pour ne pas réinventer
la poudre à chaque projet, mais cette richesse nous fait parfois
oublié l'essentiel et les bases.
Revenons sur mon besoin : disposer
d'un serveur web permettant de lancer une page web contenant un
formulaire et de récupérer le résultat saisi par l'utilisateur
pour lui afficher un résultat. Je vous propose ci dessous la solution que
j'ai appliquée pour mon besoin basée sur la classe du JDK
com.sun.net.httpserver.HttpServer.
Au niveau rendu visuel la première
page permet de saisir un nom
Au niveau du code j'utilise une classe
permettant de lancer le serveur
public class MyHttpServer {
/**
* Cette méthode permet de lancer notre serveur
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//Creation d'un serveur HTTP
HttpServer server = HttpServer.create(new InetSocketAddress(9998),0);
server.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
byte[] response = null;
int codeRetourHttp = HttpURLConnection.HTTP_OK;
//Dans le cas d'un appel simple j'affiche mon formulaire
try {
if("/hello".equals(httpExchange.getRequestURI().getPath())){
response = Strings.nullToEmpty(
TemplateLoader.getPageFromVelocityTemplate("/hello.html",
HttpParams.getParameters(httpExchange.getRequestURI().getQuery())))
.getBytes();
}
else{
response = Files.readAllBytes(Paths.get(this.getClass().getResource("/index.html").toURI()));
}
}
catch (Exception e) {
response = e.getMessage().getBytes();
codeRetourHttp = HttpURLConnection.HTTP_INTERNAL_ERROR;
return;
}
httpExchange.sendResponseHeaders(codeRetourHttp, response.length);
httpExchange.getResponseBody().write(response);
httpExchange.close();
}
});
//Demarrage du serveur
server.start();
}
}
Cette classe utilise deux classes
utilitaires. La première permet de placer les paramètres passés
dans la query dans une Map.
public class HttpParams {
/**
* Lecture des paramètres qui sont placés dans une map
* @param query
* @return
*/
protected static Map<String,String> getParameters(String query){
Map<String,String> parameters = new HashMap<>();
if(query!=null){
Iterable<String> couples = Splitter.on("&").split(query);
for(String param : couples){
if(!Strings.isNullOrEmpty(param)){
Iterable<String> element = Splitter.on("=").split(param);
Iterator<String> iterator = element.iterator();
parameters.put(iterator.next(), iterator.next());
}
}
}
return parameters;
}
}
La seconde utilise Velocity pour
charger un template de page auquel on passe des paramètres
public class TemplateLoader {
private static VelocityEngine velocityEngine;
/**
* Chargement du template Velocity
* @param nomTemplate
* @param parameters
* @return
*/
protected static String getPageFromVelocityTemplate(String nomTemplate, Map<String, ?> parameters) throws Exception{
Preconditions.checkNotNull(nomTemplate);
if(velocityEngine==null){
initTemplateEngine();
}
Template t = velocityEngine.getTemplate(nomTemplate);
try(StringWriter writer = new StringWriter()){
t.merge(new VelocityContext(parameters), writer);
return writer.toString();
}
}
/**
* Initialisation du moteur de template velocity
* @throws Exception
*/
private static void initTemplateEngine() throws Exception {
Properties props = new Properties();
props.put("resource.loader", "class");
props.put("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
velocityEngine = new VelocityEngine();
velocityEngine.init(props);
}
}
La page HTML d'entrée est celle ci
<!DOCTYPE html>
<html lang="en">
<head>
<link href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css" rel="stylesheet" media="screen"/>
<script src="http://code.jquery.com/jquery-1.9.1.min.js" language="JavaScript"></script>
</head>
<body>
<div class="container">
<h1>Javamind : exemple serveur application</h1>
<p>Cette page statique fait partie de l'article permettant de montrer la mise en place
d'un serveur d'application simplifié</p>
<fieldset>
<div class="control-group">
<label class="control-label" for="inputNom">Votre nom </label>
<div class="controls">
<input type="text" id="inputNom" placeholder="Nom" >
</div>
</div>
<br>
<div class="control-group">
<label class="control-label" for="btnSubmit"> </label>
<div class="controls">
<button class="btn btn-info" id="btnSubmit" onclick="location.href='/hello?name='+$('#inputNom').val()">Envoyer</button>
</div>
</div>
</fieldset>
</div>
</body>
</html>
et le template Velocity est le suivant
<!DOCTYPE html>
<html lang="en">
<head>
<link href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css" rel="stylesheet" media="screen"/>
<script src="http://code.jquery.com/jquery-1.9.1.min.js" language="JavaScript"></script>
</head>
<body>
<div class="container">
<h1>Javamind : exemple serveur application</h1>
<p>Cette page statique fait partie de l'article permettant de montrer la mise en place
d'un serveur d'application simplifié</p>
<p>
<h3>Bonjour $name</h3>
</p>
</div>
</body>
</html>
Cet exemple est vraiment simple mais il a l'avantage de lancer un serveur d'application en moins de 5 secondes et de n'utiliser aucune autre dépendance que le JDK. En conclusion je pense qu'il faut retenir que pour des besoins simples il faut veiller à ne pas créer des solutions trop complexes. Cet exemple pourrait être adapté par exemple pour démarrer un serveur Web exposant des services rest. Pour des appels REST Stateless on a pas forcément besoin de déployer une machine de guerre.
Aucun commentaire:
Enregistrer un commentaire
Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.