Näytetään tekstit, joissa on tunniste maven. Näytä kaikki tekstit
Näytetään tekstit, joissa on tunniste maven. Näytä kaikki tekstit

sunnuntai 19. elokuuta 2012

Some tomcat7-maven-plugin tricks

Here's some useful tomcat7-maven-plugin tricks that work also with other Maven plugins.

How to set java endorsed dir for tomcat7-maven-plugin

Sometimes you need to use Java endorsed standards override mechanism with Maven plugins. This can be accomplished simply by setting proper MAVEN_OPTS value. For example

export MAVEN_OPTS="-Djava.endorsed.dirs=/some/directory/where/endorsed/jars/are"

How to change tomcat7-maven-plugin classpath order

Very often you need to have the runtime classpath in certain order when running Tomcat or other Maven plugins. I found out by trial and error that the used classpath JAR ordering depends on the order of declared dependencies in pom.xml (most likely this is documented somewhere). So, if you need to get some libraries before others, just move them in the beginning of pom.xml. Of course this does not help, if you have many plugins that need a different order for each plugin.

How to list all loaded classes and their respective JAR files

This is something I had been looking for years and was extremely happy when I finally got to know it. At least Sun Java JRE provides a command line switch for showing all the classes that are loaded into JVM and the source JAR file of those classes. It's not always easy to know what classes are loaded, especially if your dependencies have dependencies to other JAR files that are somehow conflicting (for example different version) with JAR dependencies defined elsewhere. Anyway, to list the classes and their exact source, do the following.

export MAVEN_OPTS="-verbose:class"

The output is something like this.
[Loaded javax.xml.bind.annotation.XmlElement from /home/perttu/software/jdks/jdk1.6.0_31/jre/lib/rt.jar]
[Loaded javax.xml.bind.annotation.XmlElement$DEFAULT from /home/perttu/software/jdks/jdk1.6.0_31/jre/lib/rt.jar]
[Loaded javax.xml.ws.WebFault from /home/perttu/software/jdks/jdk1.6.0_31/jre/lib/rt.jar]

With these tools, any problem related to Tomcat Maven plugin and classpath should be easy to solve.

sunnuntai 20. toukokuuta 2012

Jetty Maven Plugin with hot code replace



I'm often using Jetty Maven plugin to execute my web applications right from the build. It provides an easy and operating system independent way to execute the web app. You only have to check out the source code from version contol, execute Maven build and start Jetty with Maven command. What could be easier way to run your web application in development environment?

I want to point out the following benefits in using Maven Jetty plugin

  • IDE independent way to execute web application. Works on Eclipse, IDEA, NetBeans and whatever
  • Support hot code replace: you can change code inside methods without restarting the whole application
  • Works on Windows, Linux and Mac or any other Java compatible operating system. No need to setup the application server instance
  • Starts fast and it's easy to reload the application to the server after changes

Just add the XML snippet in the end of this post to your pom.xml and execute Maven with mvn jetty:run. As a result, you will get response from your web app in http://localhost:8180/example/. You can run the same goal from your favorite IDE and, thus, you get an IDE independent web app execution!

In case you want to try hot code replace, add the classpaths containing your code inside the extraclasspath-element. Then start the Maven build running Jetty in debug mode and connect to the debugging session with your favorite IDE. The easiest way to achieve this in Eclipse is to run the Maven build in debug mode and then connect to the process with Eclipse debugger. After Eclipse is properly connected to the Maven process running Jetty, all the code changes in method bodies are instantly visible in the running process  (of course the same code has to be modified by Eclipse so that the changes get to the classpath of Jetty).

If you prefer doing things in IDE, at least Eclipse has excellent Jetty plugin called Run Jetty Run (http://code.google.com/p/run-jetty-run/).

<build>
<plugins>
...
<plugin>
    <groupid>org.mortbay.jetty</groupid>
    <artifactid>jetty-maven-plugin</artifactid>
    <version>7.5.2.v20111006</version>
    <configuration>
        <stopport>9966</stopport>
        <stopkey>${project.artifactId}<stopkey>
        <!-- scanning is not used if reload is set to manual -->
        <scanintervalseconds>5</scanintervalseconds>
        <!-- application reloading by pressing enter in the console -->
        <reload>manual</reload>
        <webappconfig>
            <contextpath>/example</contextpath>
            <!-- Changes in these classes will be instantly applied to running Jetty process without restart -->
            <extraclasspath>target/classes;../dependant-project/target/classes;../another-dependant-project/target/classes</extraclasspath>
        </webappconfig>
        <!-- directories whose changes cause automated Jetty context reloading, not used if reload is manual -->
        <scantargets>
            <scantarget>../dependant-project/target/classes</scantarget>
        </scantargets>
        <connectors>
            <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <port>8180</port>
                <maxidletime>60000</maxidletime>
            </connector>
        </connectors>
        <systemproperties>
            <!-- system properties that are used for running Jetty -->
            <systemproperty>
                <name>some.system.property</name>
                <value>somevalue</value>
            </systemproperty>
        </systemproperties>
    </configuration>
    <dependencies>
        <!-- dependencies added to Jetty's classpath -->
        <dependency>
            <groupid>log4j</groupid>
            <artifactid>log4j</artifactid>
            <version>${log4j.version}</version>
            <type>jar</type>
        </dependency>
    </dependencies>
</plugin>
...
</plugins>
</build>

sunnuntai 19. helmikuuta 2012

Sovelluksien konfiguroinnista sananen

Olen projekteissani törmännyt moneen erilaiseen tapaan tehdä sovelluskonfiguraatiota. Konfiguraatiolla tarkoitan tässä sitä, että sama käännöstuote eli (yleensä) binääri saadaan toimimaan eri tavalla konfiguraatiota muokkaamalla ilman tarvetta tehdä uudelleenkäännöstä lähdekoodista. Esimerkiksi tietokannan osoite ja tunnus ovat yleensä konfiguraatiotiedostoon erotettuna, koska testi- ja tuotantoympäristöissä käytetään usein eri tietokantaa.

Mielestäni on parempi viedä konfiguraatiota erillisiin tiedostoihin mieluummin enemmän kuin liian vähän. Joidenkin mielestä kuitenkin erilliset konfiguraatiotiedostot ovat jopa turhia, koska heidän ajatusmallinsa mukaan koodi voidaan aina kääntää ja asentaa uudelleen, kun lähdekoodissa oleviin asetuksiin tehdään muutoksia. Ylenmääräisestä konfiguroitavuudesta on kyllä haittaakin ja parasta on, jos melkein kaikille konfiguroitaville asioille voi antaa järkevän oletusarvon joko suoraan sovelluksen lähdekoodissa tai sitten erillisissä oletusarvoja sisältävissä konfiguraatiotiedostoissa.

Nähdäkseni on olemassa neljä eri tapaa tehdä sovelluksen konfiguraatiota.

  1. Lähdekoodissa olevat vakiot, joiden muuttaminen vaatii lähdekoodin uudelleenkääntämisen
  2. Konfigurointi erillisiin tiedostoihin (Javassa esimerkiksi properties-tiedostot), joiden sisältö kiinnitetään käännösaikaisesti esimerkiksi Maven filttereillä.
  3. Konfiguraatiotiedostot, jotka eivät mene binääripakettien (JAR, WAR, EAR ja vastaavat eri kielissä) sisään ja joita voidaan muutella kääntämisen jälkeen vapaasti. Muutosten käyttöönotto vaatii prosessin uudelleenkäynnistyksen
  4. Prosessin ajoaikainen konfiguraatio eli asetusten muuttaminen ilman prosessin uudelleenkäynnistystä. Tämä tapa on käytössä melkein kaikissa graafisen käyttöliittymän sisältävissä sovelluksissa, mutta erittäin harvoin taustaprosesseissa
Esittämäni konfiguroitavuuden tasot ja niiden järkevä käyttö projektissa riippuu täysin projektin koosta ja tavoitteista.

Jos koodailee itsekseen tai hyvin pienellä ryhmällä sovellusta, jonka ajoympäristö on yksinkertainen ja build-prosessin sekä kehittäjäryhmän hallitsema, voidaan rajoittaa konfiguroitavuus tapaan 1. Tällöin vaatimuksena on se, että kehittäjän on äärimmäisen helppo viedä pienetkin mutokset suoraan tuotantoon ja erillisiä testiympäristöjä ei juurikaan käytetä. On huomattava, että mikäli käytössä on yksikin ympäristö tuotannon lisäksi, se näkyy aina lähdekoodissa. Jokainen tuettava lisäympäristö ja poikkeama konfiguraatiossa vaatii muutoksia lähdekoodiin.

Tapa 2 on käyttökelposuudeltaan hyvin lähellä tapaa 1. Näillä on kuitenkin se merkittävä ero, että uudet ympäristöt tai erilaiset konfiguroinnit eivät enää vaadikaan välttämättä lähdekooditason muutoksia. Toki sovelluksen pitää osata hakea asetukset koodista erillään olevista tiedostoista. Yleensä tavassa 2 erilliset konfiguraatiotiedostot leivotaan binääripakettien sisään ja niiden buildin jälkeinen muuttaminen vaatii pakettien avaamista ja uudelleen paketointia käsipelillä. Tapa 2 soveltuu pieniin projekteihin, joiden ajoympäristö on projektien kehittäjien täydellisessä hallinnassa ja jossa kehittäjän on äärimmäisen helppo viedä pienetkin muutokset suoraan eri testi- ja tuotantoympäristöihin.

Tavassa 3 konfiguraatiomuutokset eivät koskaan vaadi lähdekoodin muutoksia tai binääripaketin uudelleen kasaamista. Kaikki konfiguraatio on erillään varsinaisesta binääristä ja binäärille vain kerrotaan mistä päin sen tulisi käynnistyksen yhteydessä hakea konfiguraationsa. Tämä tapa soveltuu lähes kaikkiin projekteihin eikä ole juurikaan monimutkaisempi toteuttaa kuin tapa 2.

Tapa 4 on käytössä yleensä vain graafisen käyttöliittymän sisältävissä sovelluksissa. Joskus tosin taustaprosessejakaan ei saa niin vaan käynnistellä uudelleen ja niiden konfiguraatiota pitää pystyä muuttamaan ajonaikaisesti. Vaikka tapa 4 kuulostaa parhaalta ja joustavimmalta tavalta sovelluskonfiguraatioon, on sen toteuttaminen kaikelle konfiguraatiolle useimmiten haastavaa ja aivan liian työlästä saavutettuun hyötyyn nähden.

Olen ollut huomaavinani, että Maven filteröintiä käytetään vähän liian innokkaasti sovelluskonfiguraatioon, jossa tavoitteena on tavan 3 joustavuus, mutta sitä yritetään tehdä tavan 2 välinein. Vaikka Mavenin filteröinnillä saadaan helposti tehtyä erilaisia profiilikohtaisia konfiguraatioita, ei filteröintiä voi soveltaa järkevästi kaikelle konfiguraatiolle kuin hyvin suppeissa projekteissa. Esimerkiksi projektissa, jossa varsinaisesta ajoympäristöstä vastaa muu kuin kehitysporukka, täytyisi ajoympäristön ylläpitäjien jatkuvasti kertoa ajoympäristön muutoksista (vaikkapa uusi tietokantasalasana), jotta ne saataisiin sovelluksen konfiguraatioon Maven buildin yhteydessä. Vaikka kehitysporukka vastaisikin kokonaan eri ajoympäristöistä ja asennukset voitaisiin tehdä uudestaan konfiguraatiomuutoksia sisältävän Maven buildin jälkeen, joudutaan joka ajoympäristölle ajamaan erillinen build eri profiililla. Uusi build eri profiililla sisältää riskin siitä, että eri build-tuloksissa on muitakin eroja kuin vain erilaisia filtteröitäviä konfiguraatioita (Mavenin kanssa on helppo mokata tällaisessa erityisesti release-pluginia käyttäessä).


Kun itse lähden tekemään taustaprosessina pyörivää sovellusta (eli suurin osa esimerkiksi selaimella käytettävistä sovelluksista, jotka pyörivät sovelluspalvelimen sisällä), lähden toteutuksessa aina siitä, että konfiguraatioon voi käyttää tapoja 1, 2 ja 3. Jos teen graafisella käyttöliittymällä varustettua sovellusta, otan lähtökohtaisesti tavat 1, 2, 3 ja 4 käyttöön. Yleensä tavan 3 tukeminen on helppo toteuttaa, joten ei ole mitään mieltä jättää sitä pois ja tyytyä vain tapohin 1 ja 2 edes ihan pienimmissä projekteissa. Hyödylliset pienet projektit yleensä kasvavat isommiksi projekteiksi ja tavat 1 ja 2 eivät skaalaudu projektin koon kasvaessa. On kuitenkin huomattava, että mikään esitetyistä tavoista ei yksistään ole yleensä paras tapa. Paras lähestymistapa on tukea ainakin kolmea ensimmäistä tapaa ja tehdä niiden käyttö sovelluskehittäjälle yhtä helpoksi. Näin sovelluskehittäjän on helppo tehdä tekemästään ominaisuudesta halutulla tasolla konfiguroitava.