juin
2009
Dans ce triosième volet de la série parlant de l’utilisation d’OSGi côté serveur, je vais présenter comment on peut configurer jetty.
Par défaut, Jetty utilise le port 8080, ce qui peut poser problème quand on a un autre serveur sur ce port.
Pour résoudre celà, on va créer un fragment qui contient les fichiers de configuration de Jetty. J’avais présenté en détail comment céer un fragment dans le billet précédent : « Préparation de l’environnement : configuration de log4j »
Créez donc un fragment en utilisant l’assistant offert par PDE en utilisant « sse.jetty.config » comme nom et en sélectionnant « org.springframework.osgi.jetty.start.osgi » comme host.
Dans ce fragment, créez un dossier « etc » à la racine du projet avec un fichier jetty.xml dedans dont voici le contenu :
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<!-- =============================================================== -->
<!-- Configure the Jetty Server (OSGi specific) -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
<!-- -->
<!-- =============================================================== -->
<Configure id="Server" class="org.mortbay.jetty.Server">
<!-- =========================================================== -->
<!-- Server Thread Pool -->
<!-- =========================================================== -->
<Set name="ThreadPool">
<!-- Default bounded blocking threadpool
-->
<New class="org.mortbay.thread.BoundedThreadPool">
<Set name="minThreads">10</Set>
<Set name="maxThreads">250</Set>
<Set name="lowThreads">25</Set>
</New>
<!-- Optional Java 5 bounded threadpool with job queue
<New class="org.mortbay.thread.concurrent.ThreadPool">
<Set name="corePoolSize">250</Set>
<Set name="maximumPoolSize">250</Set>
</New>
-->
</Set>
<!-- =========================================================== -->
<!-- Set connectors -->
<!-- =========================================================== -->
<!-- One of each type! -->
<!-- =========================================================== -->
<!-- Use this connector for many frequently idle connections
and for threadless continuations.
-->
<Call name="addConnector">
<Arg>
<New class="org.mortbay.jetty.nio.SelectChannelConnector">
<Set name="host"><SystemProperty name="jetty.host" /></Set>
<Set name="port"><SystemProperty name="jetty.port" default="9090"/></Set>
<Set name="maxIdleTime">30000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">9443</Set>
<Set name="lowResourcesConnections">5000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
</New>
</Arg>
</Call>
<!-- Use this connector if NIO is not available.
<Call name="addConnector">
<Arg>
<New class="org.mortbay.jetty.bio.SocketConnector">
<Set name="port">8081</Set>
<Set name="maxIdleTime">50000</Set>
<Set name="lowResourceMaxIdleTime">1500</Set>
</New>
</Arg>
</Call>
-->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- To add a HTTPS SSL listener -->
<!-- see jetty-ssl.xml to add an ssl connector. use -->
<!-- java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- =========================================================== -->
<!-- Set up global session ID manager -->
<!-- =========================================================== -->
<!--
<Set name="sessionIdManager">
<New class="org.mortbay.jetty.servlet.HashSessionIdManager">
<Set name="workerName">node1</Set>
</New>
</Set>
-->
<!-- =========================================================== -->
<!-- Set handler Collection Structure -->
<!-- =========================================================== -->
<Set name="handler">
<New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection">
<Set name="handlers">
<Array type="org.mortbay.jetty.Handler">
<Item>
<New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/>
</Item>
<Item>
<New id="DefaultHandler" class="org.mortbay.jetty.handler.DefaultHandler"/>
</Item>
<Item>
<New id="RequestLog" class="org.mortbay.jetty.handler.RequestLogHandler"/>
</Item>
</Array>
</Set>
</New>
</Set>
<!-- =========================================================== -->
<!-- Configure the context deployer -->
<!-- A context deployer will deploy contexts described in -->
<!-- configuration files discovered in a directory. -->
<!-- The configuration directory can be scanned for hot -->
<!-- deployments at the configured scanInterval. -->
<!-- -->
<!-- This deployer is configured to deploy contexts configured -->
<!-- in the $JETTY_HOME/contexts directory -->
<!-- -->
<!-- =========================================================== -->
<!--
<Call name="addLifeCycle">
<Arg>
<New class="org.mortbay.jetty.deployer.ContextDeployer">
<Set name="contexts"><Ref id="Contexts"/></Set>
<Set name="configurationDir"><SystemProperty name="jetty.home" default="."/>/contexts</Set>
<Set name="scanInterval">1</Set>
</New>
</Arg>
</Call>
-->
<!-- =========================================================== -->
<!-- Configure the webapp deployer. -->
<!-- A webapp deployer will deploy standard webapps discovered -->
<!-- in a directory at startup, without the need for additional -->
<!-- configuration files. It does not support hot deploy or -->
<!-- non standard contexts (see ContextDeployer above). -->
<!-- -->
<!-- This deployer is configured to deploy webapps from the -->
<!-- $JETTY_HOME/webapps directory -->
<!-- -->
<!-- Normally only one type of deployer need be used. -->
<!-- -->
<!-- =========================================================== -->
<!--
<Call name="addLifeCycle">
<Arg>
<New class="org.mortbay.jetty.deployer.WebAppDeployer">
<Set name="contexts"><Ref id="Contexts"/></Set>
<Set name="webAppDir"><SystemProperty name="jetty.home" default="."/>/webapps</Set>
<Set name="parentLoaderPriority">false</Set>
<Set name="extract">true</Set>
<Set name="allowDuplicates">false</Set>
<Set name="defaultsDescriptor"><SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Set>
</New>
</Arg>
</Call>
-->
<!-- =========================================================== -->
<!-- Configure Authentication Realms -->
<!-- Realms may be configured for the entire server here, or -->
<!-- they can be configured for a specific web app in a context -->
<!-- configuration (see $(jetty.home)/contexts/test.xml for an -->
<!-- example). -->
<!-- =========================================================== -->
<!--
<Set name="UserRealms">
<Array type="org.mortbay.jetty.security.UserRealm">
<Item>
<New class="org.mortbay.jetty.security.HashUserRealm">
<Set name="name">Test Realm</Set>
<Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
<Set name="refreshInterval">0</Set>
</New>
</Item>
</Array>
</Set>
-->
<!-- =========================================================== -->
<!-- Configure Request Log -->
<!-- Request logs may be configured for the entire server here, -->
<!-- or they can be configured for a specific web app in a -->
<!-- contexts configuration (see $(jetty.home)/contexts/test.xml -->
<!-- for an example). -->
<!-- =========================================================== -->
<Ref id="RequestLog">
<Set name="requestLog">
<New id="RequestLogImpl" class="org.mortbay.jetty.NCSARequestLog">
<Set name="filename"><SystemProperty name="jetty.logs" default="./logs"/>/yyyy_mm_dd.request.log</Set>
<Set name="filenameDateFormat">yyyy_MM_dd</Set>
<Set name="retainDays">90</Set>
<Set name="append">true</Set>
<Set name="extended">true</Set>
<Set name="logCookies">false</Set>
<Set name="LogTimeZone">GMT</Set>
</New>
</Set>
</Ref>
<New id="ServerLog" class="java.io.PrintStream">
<Arg>
<New class="org.mortbay.util.RolloverFileOutputStream">
<Arg><SystemProperty name="jetty.logs" default="./logs"/>/yyyy_mm_dd.stderrout.log</Arg>
<Arg type="boolean">false</Arg>
<Arg type="int">90</Arg>
<Arg><Call class="java.util.TimeZone" name="getTimeZone"><Arg>GMT</Arg></Call></Arg>
<Get id="ServerLogName" name="datedFilename"/>
</New>
</Arg>
</New>
<!-- <Call class="org.mortbay.log.Log" name="info"><Arg>Redirecting stderr/stdout to <Ref id="ServerLogName"/></Arg></Call>
<Call class="java.lang.System" name="setErr"><Arg><Ref id="ServerLog"/></Arg></Call>
<Call class="java.lang.System" name="setOut"><Arg><Ref id="ServerLog"/></Arg></Call>
-->
<!-- =========================================================== -->
<!-- extra options -->
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">true</Set>
<Set name="gracefulShutdown">1000</Set>
</Configure>
Notez surtout que j’ai utilisé 9090 comme valeur pour le port, au lieu du 8080.
Voilà. Il suffit ensuite d’ajouter ce fragment à votre configuration de lancement pour que Jetty utilise le port 9090.
J’oubliais le blog de ekke qui decrit comment il fabrique, sur une base d’eclipse riena, son application avec EJB3:
http://ekkes-corner.blogspot.com/2008/11/howto-build-osgi-enterprise-server.html
Salut
J’arrive probablement un peu tard dans cette discussion, je viens de découvrir les blogs de developpez.com
@keulkeul SI tu recherches une solution EJB/OSGi intégré, tu pourrais regarder EasyBeans et par transitivité JOnAS.
EasyBeans offre un packaging OSGi et tu peux déployer tes EjbJars comme des bundles OSGi.
Concernant tes questions sur la connectivité, tes EJBs restent accessibles par RMI (si ce sont des @Remote), mais ils sont aussi disponibles en tant que services OSGi pour les autres parties de ton application.
Il existe une application demo fonctionant sur JOnAS qui mixe EJB3, OSGi, WebApp…
http://code.google.com/p/jonas-showcase/
Salut Djo.mos,
Pour information j’ai résolu mon problème. Actuellement j’ai un prototype qui démontre en quelques sortes les points suivants:
– Faire une sorte d’appel RMI entre une application dans un conteneur OSGi et un ensemble d’application qui ne sont pas forcément dans un conteneur OSGi (client Swing, application Web avec GWT, client Swing dans du OSGi, application Eclipse RCP).
– Ajouter dynamiquement de nouveaux POJO sans avoir à redémarrer l’application complète.
Je vais poser tout ça sur « papier » et préparer un billet sur mon blog
Mickael
GF 3 car il est embeddable.
Ceci est une de mes pistes car avant cette nouvelle version il n’y avait pas grand chose comme serveur EJB embarqué. C’est pour cette raison que j’avais choisi OpenEJB.
Mickael
Bonne question : aucune idée, c’est peut etre faisable avec GF 3 car il est embeddable.
Spring DM offre d’office des versions adaptés à OSGi de Jetty et de Tomcat.
« Quand tu dis le conteneur web s’intègre dans equinox, c’est un bundle comme les autres », tu fais comment si tu veux y mettre du Glassfish, Weblogic ?
Mickael
Je ne peux pas te répondre pour Spring Remoting et les EJB 3 car j’ai presque jamais utilisé ces derniers.
En ce qui me concerne, Spring offre tout ce qu’il faut coté IoC, AoP, accès sgbdr, transactions, sécurité, remoting, etc.
Pour ta dernière question, c’est plutot l’inverse : le conteneur web s’intègre dans equinox, c’est un bundle comme les autres
Spring DM offre un mécanisme basé sur le pattern Extender pour découvrir les bundles de type Web et faire le nécessaires pour que le bundle du conteneur web les expose.
Merci encore pour ta réponse très enrichissante. Comme tu peux le voir je suis très intéressé.
De mon côté j’ai des EJB Entity, Stateless que je souhaiterais pouvoir exposer. Malheureusement j’ai quelques soucis pour faire cohabiter OpenEJB et OSGi et je ne peux les utiliser.
Je me dis en lisant tes billets que je pourrais peut être utiliser Spring Remoting pour exposer tout cela ?
D’où ma troisième question ?
Comment s’intègre ton application dans un conteneur Web de type Glassfish, Tomcat, …
Mickael
Oki, en fait les techniques que je décris dans cette série de billets sont tirés d’un projet sur lequel je bosse au boulot.
Je bosse plus précisément sur le serveur métier, qui est full OSGi (tournant sur Equinox). comme tu le décris, c’est un ensemble de bundles qui intéeragissent entre eux (découpés généralement verticalement (un bundle séparé pour chaque couche) et horizontalement (un bundle par domaine)).
après, ce serveur expose les services finaux pour consommation via remoting. L’exposition se fait via des bundles séparés.
Pour le moment, on exporte en utilisant Spring Remoting. On va probablement ajouter d’autres bundles qui exposeront les services via AMF pour des clients flex.
Merci pour ta réponse.
Actuellement tu pars sur une application dont la solution est pure OSGi. Ton application est donc composé d’un ensemble de bundles.
Ma seconde question est la suivante ?
Si tu devais attaquer ton application via un WebService tu ferais comment ?
Mickael
Salut et merci,
Ce qui m’a fait exclure le HttpService d’OSGi Compendium, est le fait qu’il soit limité au Servlet API 2.1, une version archaique et plus du tout adapté au développement web avec les frameworks modernes, telque Wicket par exemple.
Pour le servlet bridge, j’avais fait quelques recherches à ce sujet, et on me l’a déconseillé car il pourrait poser quelques problèmes au déploiement … je crois que la source était attlassian qui ont donné un retour d’expérience sur l’implémentation d’un système de plugins pour JIRA, où ils ont finir par utiliser OSGi.
Remarque qu’en suivant le lien que tu donnes, je me rends compte que les chose ont bien évolué pour Equinox coté serveur, comparé à ce que j’avais vu quand j’avais fait mes recherches y’a plusieurs mois déjà.
Pour revenir à Spring DM, ça supporte la dernière version du Servlet API (les filtres par exemple) et ça permet d’utiliser le web.xml usuel. D’ailleurs, encore une fois, la solution proposé par Spring DM pour gérer les bundles web sera standardisé dans ce qu’on appelle OSGi web container : http://blog.springsource.com/2009/05/27/introduction-to-the-osgi-web-container/
Ajoutes à ça que j’utilisais intensivement Spring dans mes bundles, ce qui fait Spring DM était LA solution dans mon cas.
P.S.: Le prochain billet de cette série présenterait justement comment créer une application Web en utilisant le support Web de Spring DM.
Salut Djo.mos,
Je m’intéresse depuis peu à l’utilisation d’OSGi (via Equinox) dans un environnement EJB (via OpenEJB). L’intérêt pour moi d’utiliser OSGi est de pouvoir ajouter dynamiquement de nouveaux POJOs et de les recharger à « chaud ». Le prototype fonctionne et j’arrive dynamiquement à fournir un nouveau bundle (compilation du POJO, génération du JAR) et de l’intégrer dans le conteneur.
Je suis actuellement à l’étape suivante qui consiste à connecter ce prototype à HTTP. Sous Equinox j’arrive à connecter mon prototype à un serveur Tomcat où à fournir un serveur Jetty (http://www.eclipse.org/equinox/server/)
Ma première question est la suivante (les autres suivront dans de prochains commentaires). Tu parlais dans ton premier post de Spring DM qui offre plus de possibilité côté HTTP. Penses-tu que, ce qui est offert par Equinox ne suffit-il pas (je peux créer des Servlets …) ? En fait, je suis assez hésitant à utiliser Spring DM, je me dis que pour l’instant ça fonctionne.
En tout cas merci pour ces billets très novateurs. Il y a très peu à mon avis d’articles qui traitent vraiment d’utilisation d’OSGi en dehors de son fonctionnement de base.
Mickael