3 xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:layout="http://www.thymeleaf.org"
5 layout:decorator="templates/layout"
8 <title>juplo - blog - Combining jetty-maven-plugin and wro4j-maven-plugin for Dynamic Reloading of LESS-Resources</title>
10 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
11 <link rel="stylesheet/less" type="text/css" href="../less/base.less"/>
12 <link rel="stylesheet/less" type="text/css" href="../less/screen.less" media="screen"/>
13 <link rel="stylesheet/less" type="text/css" href="../less/print.less" media="print"/>
14 <script src="../js/less-1.7.0.min.js"></script>
18 <ol layout:fragment="breadcrumb">
19 <li class="b"><a class="b" href="../index.html" th:href="@{/index.html}">Home</a></li>
20 <li class="b"><strong class="b">Blog</strong></li>
22 <nav layout:fragment="navigation">
24 <a class="hide" href="#top" title="Show Content">Jump back to the top of the page</a>
25 <h1 class="nav">Navigation</h1>
26 <h2 class="nav menu">Section-Menu</h2>
27 <ul id="menu" class="cf">
28 <li class="m blog"><strong class="m">Blog</strong></li>
29 <li class="m projects"><a href="../projects.html" th:href="@{/projects.html}" class="m">Projects</a></li>
30 <li class="m about"><a href="../about.html" th:href="@{/about.html}" class="m">About</a></li>
33 <article class="main" layout:fragment="maincontent">
34 <article id="post-140" class="post-140 post type-post status-publish format-standard hentry category-jetty category-less category-maven category-wro4j">
35 <div class="entry-header">
36 <h1 class="entry-title">Combining jetty-maven-plugin and wro4j-maven-plugin for Dynamic Reloading of LESS-Resources</h1>
37 <div class="entry-meta">
38 Posted on <a href="http://juplo.de/combining-jetty-maven-plugin-and-wro4j-maven-plugin-for-dynamic-reloading-of-less-resources/" title="12:58" rel="bookmark"><time class="entry-date" datetime="2013-12-06T12:58:17+00:00">December 6, 2013</time></a><span class="byline"> by <span class="author vcard"><a class="url fn n" href="http://juplo.de/author/kai/" title="View all posts by Kai Moritz" rel="author">Kai Moritz</a></span></span>
39 </div><!-- .entry-meta -->
40 </div><!-- .entry-header -->
41 <div class="entry-content">
42 <p>Ever searched for a simple configuration, that lets you use your <a href="http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://wiki.eclipse.org']);" title="See the documentation for mor information">jetty-maven-plugin</a> as you are used to, while working with <a href="http://www.lesscss.org/" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://www.lesscss.org']);" title="See LESS CSS documentation for mor informations">LESS</a> to simplify your stylesheets?</p>
43 <p>You cannot do both, use the <a href="http://www.lesscss.org/#usage" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://www.lesscss.org']);" title="More about the client-side usage of LESS">Client-side mode</a> of LESS to ease development and use the <a href="https://github.com/marceloverdijk/lesscss-maven-plugin" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://github.com']);" title="Homepage of the official LESS CSS maven plugin">lesscss-maven-plugin</a> to automatically compile the LESS-sources into CSS for production. That does not work, because your stylesheets must be linked in different ways if you are switching between the client-side mode – which is best for development – and the pre-compiled mode – which is best for production. For the client-side mode you need something like:</p>
44 <pre class="prettyprint linenums">
46 <link rel="stylesheet/less" type="text/css" href="styles.less" />
47 <script src="less.js" type="text/javascript"></script>
50 <p>While, for the pre-compiled mode, you want to link to your stylesheets as usual, with:</p>
51 <pre class="prettyprint linenums">
53 <link rel="stylesheet/less" type="text/css" href="styles.css" />
56 <p>While looking for a solution to this dilemma, I stumbled accross <a href="https://code.google.com/p/wro4j/" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://code.google.com']);" title="See the documentation of ths wounderfull tool">wro4j</a>. Originally intended, to speed up page-delivery by combining and minimizing multiple resources into one through the use of a servlet-filter, this tool also comes with a maven-plugin, that let you do the same offline, while compiling your webapp.</p>
57 <p>The idea is, to use the <a href="http://code.google.com/p/wro4j/wiki/MavenPlugin" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://code.google.com']);" title="See the documentation of hte wro4j-maven-plugin">wro4j-maven-plugin</a> to compile and combine your LESS-sources into CSS for production and to use the <a href="http://code.google.com/p/wro4j/wiki/Installation" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://code.google.com']);" title="See how to configure the filter">wro4j filter</a>, to dynamically deliver the compiled CSS while developing. This way, you do not have to alter your HTML-code, when switching between development and production, because you always link to the CSS-files.</p>
58 <p>So, lets get dirty!</p>
59 <h2>Step 1: Configure wro4j</h2>
60 <p>First, we configure <strong>wro4j</strong>, like as we want to use it to speed up our page. The details are explained and linked on wro4j’s <a href="http://code.google.com/p/wro4j/wiki/GettingStarted" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://code.google.com']);" title="Visit the Getting-Started-Page">Getting-Started-Page</a>. In short, we just need two files: <strong>wro.xml</strong> and <strong>wro.properties</strong>.</p>
62 <p>wro.xml tells wro4j, which resources should be combined and how the result should be named. I am using the following configuration to generate all LESS-Sources beneath <code>base/</code> into one CSS-file called <code>base.css</code>:</p>
63 <pre class="prettyprint linenums">
65 <groups xmlns="http://www.isdc.ro/wro">
66 <group name="base">
67 <css>/less/base/*.less</css>
71 <p>wro4j looks for <code>/less/base/*.less</code> inside the root of the web-context, which is equal to <code>src/main/webapp</code> in a normal maven-project. There are <a href="http://code.google.com/p/wro4j/wiki/ResourceTypes" onclick="javascript:_gaq.push(['_trackEvent','outbound-article','http://code.google.com']);" title="See the resource locator documentation of wro4j for more details">other ways to specifie the resources</a>, which enable you to store them elswhere. But this approach works best for our goal, because the path is understandable for both: the wro4j servlet-filter, we are configuring now for our development-environment, and the wro4j-maven-plugin, that we will configure later for build-time compilation.</p>
72 <h3>wro.properties</h3>
73 <p>wro.properties in short tells wro4j, how or if it should convert the combined sources and how it should behave. I am using the following configuration to tell wro4j, that it should convert <code>*.less</code>-sources into CSS and do that on <em>every request</em>:</p>
74 <pre class="prettyprint linenums">
75 <code class="properties">
80 <p>You can do a lot more here. There are countless <a href="http://code.google.com/p/wro4j/wiki/ConfigurationOptions" onclick="javascript:_gaq.push(['_trackEvent', 'outbound-article', 'http://code.google.com']);" title="See all configuration options">configuration options</a> to fine-tune the behaviour of wro4j. The <code>disableCache=true</code> is crucial, because we would not see the changes take effect when developing with <strong>jetty-maven-plugin</strong> later on. You can also do much more with your resources here, for example <a href="https://code.google.com/p/wro4j/wiki/AvailableProcessors" onclick="javascript:_gaq.push(['_trackEvent', 'outbound-article', 'http://code.google.com']);" title="See all available processors">minimizing</a>. But for our goal, we are now only intrested in the compilation of our LESS-sources.</p>
81 <h2>Step 2: Configure the wro4j servlet-filter</h2>
82 <p>Configuring the filter in the <strong>web.xml</strong> is easy. It is explained in wro4j’s <a href="https://code.google.com/p/wro4j/wiki/Installation" onclick="javascript:_gaq.push(['_trackEvent', 'outbound-article', 'http://code.google.com']);" title="See the installation instructions for the wro4j servlet-filter">installation-insctuctions</a>. But the trick is, that we do not want to configure that filter for the production-version of our webapp, because we want to compile the resources offline, when the webapp is build. To acchieve this, we can use the <code><overrideDescriptor></code>-Parameter of the <a href="http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin#Configuring_Your_WebApp" onclick="javascript:_gaq.push(['_trackEvent', 'outbound-article', 'http://wiki.eclipse.org']);" title="Read more about the configuration of the jetty-maven-plugin">jetty-maven-plugin</a>.</p>
83 <h2><overrideDescriptor></h2>
84 <p>This parameter lets you specify additional configuration options for the web.xml of your webapp. I am using the following configuration for my jetty-maven-plugin:</p>
85 <pre class="prettyprint linenums">
88 <groupId>org.eclipse.jetty</groupId>
89 <artifactId>jetty-maven-plugin</artifactId>
92 <overrideDescriptor>${project.basedir}/src/test/resources/jetty-web.xml</overrideDescriptor>
94 </configuration>
97 <groupId>ro.isdc.wro4j</groupId>
98 <artifactId>wro4j-core</artifactId>
99 <version>${wro4j.version}</version>
102 <groupId>ro.isdc.wro4j</groupId>
103 <artifactId>wro4j-extensions</artifactId>
104 <version>${wro4j.version}</version>
107 <groupId>javax.servlet</groupId>
108 <artifactId>servlet-api</artifactId>
111 <groupId>org.apache.commons</groupId>
112 <artifactId>commons-lang3</artifactId>
115 <groupId>commons-io</groupId>
116 <artifactId>commons-io</artifactId>
119 <groupId>commons-pool</groupId>
120 <artifactId>commons-pool</artifactId>
123 <groupId>org.springframework</groupId>
124 <artifactId>spring-web</artifactId>
127 <groupId>com.google.code.gson</groupId>
128 <artifactId>gson</artifactId>
131 <groupId>com.google.javascript</groupId>
132 <artifactId>closure-compiler</artifactId>
135 <groupId>com.github.lltyk</groupId>
136 <artifactId>dojo-shrinksafe</artifactId>
139 <groupId>org.jruby</groupId>
140 <artifactId>jruby-core</artifactId>
143 <groupId>org.jruby</groupId>
144 <artifactId>jruby-stdlib</artifactId>
147 <groupId>org.jruby</groupId>
148 <artifactId>jruby-stdlib</artifactId>
151 <groupId>me.n4u.sass</groupId>
152 <artifactId>sass-gems</artifactId>
155 <groupId>nz.co.edmi</groupId>
156 <artifactId>bourbon-gem-jar</artifactId>
159 <groupId>org.codehaus.gmaven.runtime</groupId>
160 <artifactId>gmaven-runtime-1.7</artifactId>
163 <groupId>org.webjars</groupId>
164 <artifactId>jshint</artifactId>
167 <groupId>org.webjars</groupId>
168 <artifactId>less</artifactId>
171 <groupId>org.webjars</groupId>
172 <artifactId>emberjs</artifactId>
175 <groupId>org.webjars</groupId>
176 <artifactId>handlebars</artifactId>
179 <groupId>org.webjars</groupId>
180 <artifactId>coffee-script</artifactId>
183 <groupId>org.webjars</groupId>
184 <artifactId>jslint</artifactId>
187 <groupId>org.webjars</groupId>
188 <artifactId>json2</artifactId>
191 <groupId>org.webjars</groupId>
192 <artifactId>jquery</artifactId>
196 </dependencies>
200 <p>The dependencies to <strong>wro4j-core</strong> and <strong>wro4j-extensions</strong> are needed by jetty, to be able to enable the filter defined below. Unfortunatly, one of the transitive dependencies of <code>wro4j-extensions</code> triggers an uggly error when running the jetty-maven-plugin. Therefore, all unneeded dependencies of <code>wro4j-extensions</code> are excluded, as a workaround for this error/bug.</p>
201 <h2>jetty-web.xml</h2>
202 <p>And my jetty-web.xml looks like this:</p>
203 <pre class="prettyprint linenums">
205 <?xml version="1.0" encoding="UTF-8"?>
206 <web-app xmlns="http://java.sun.com/xml/ns/javaee"
207 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
208 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
211 <filter-name>wro</filter-name>
212 <filter-class>ro.isdc.wro.http.WroFilter</filter-class>
214 <filter-mapping>
215 <filter-name>wro</filter-name>
216 <url-pattern>*.css</url-pattern>
217 </filter-mapping>
221 <p>The filter processes any URI’s that end with <code>.css</code>. This way, the wro4j servlet-filter makes <code>base.css</code> available under any path, because for exampl <code>/base.css</code>, <code>/css/base.css</code> and <code>/foo/bar/base.css</code> all end with <code>.css</code>.</p>
222 <p>This is all, that is needed to develop with dynamically reloadable compiled LESS-resources. Just fire up your browser and browse to <code>/what/you/like/base.css</code>. (But do not forget to put some LESS-files in <code>src/main/webapp/less/base/</code> first!)</p>
223 <h2>Step 3: Install wro4j-maven-plugin</h2>
224 <p>All that is left over to configure now, is the build-process. If you would build and deploy your webapp now, the CSS-file <code>base.css</code> would not be generated and the link to your stylesheet, that already works in our jetty-maven-plugin environment would point to a 404. Hence, we need to set up the <strong>wro4j-maven-plugin</strong>. I am using this configuration:</p>
225 <pre class="prettyprint linenums">
228 <groupId>ro.isdc.wro4j</groupId>
229 <artifactId>wro4j-maven-plugin</artifactId>
230 <version>${wro4j.version}</version>
231 <configuration>
232 <wroManagerFactory>ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory</wroManagerFactory>
233 <cssDestinationFolder>${project.build.directory}/${project.build.finalName}/css/</cssDestinationFolder>
234 </configuration>
237 <phase>package</phase>
239 <goal>run</goal>
246 <p>I connected the <code>run</code>-goal with the <code>package</code>-phase, because the statically compiled CSS-file is needed only in the final war. The <code>ConfigurableWroManagerFactory</code> tells wro4j, that it should look up further configuration options in our <code>wro.properties</code>-file, where we tell wro4j, that it should compile our LESS-resources. The <code><cssDestinationFolder></code>-tag tells wro4j, where it should put the generated CSS-file. You can adjust that to suite your needs.</p>
247 <p>That’s it: now the same CSS-file, which is created on the fly by the wro4j servlet-filter when using <code>mvn jetty:run</code> and, thus, enables dynamic reloading of our LESS-resources, is generated during the build-process by the wro4j-maven-plugin.</p>
248 <h2>Cleanup and further considerations</h2>
249 <h3>lesscss-maven-plugin</h3>
250 <p>If you already compile your LESS-resources with the lesscss-maven-plugin, you can stick with it and skip step 3. But I strongly recommend giving wro4j-maven-plugin a try, because it is a much more powerfull tool, that can speed up your final webapp even more.</p>
251 <h3>Clean up your mess </h3>
252 <p>With a configuration like the above one, your LESS-resources and wro4j-configuration-files will be packed into your production-war. That might be confusing later, because neither wro4j nor LESS is used in the final war. You can add the following to your <code>pom.xml</code> to exclude these files from your war for the sake of clarity:</p>
253 <pre class="prettyprint linenums">
256 <artifactId>maven-war-plugin</artifactId>
257 <configuration>
258 <warSourceExcludes>
261 </warSourceExcludes>
262 </configuration>
266 <h3>What’s next?</h3>
267 <p>We only scrached the surface, of what can be done with wro4j. Based on this configuration, you can easily enable additional features to fine-tune your final build for maximum speed. You really should take a look at the <a href="https://code.google.com/p/wro4j/wiki/AvailableProcessors" onclick="javascript:_gaq.push(['_trackEvent', 'outbound-article', 'http://code.google.com']);" title="Available Processors">list of available Processors</a>!</p>
268 </div><!-- .entry-content -->
269 <footer class="entry-meta">
270 This entry was posted in <a href="http://juplo.de/category/jetty/" title="View all posts in Jetty" rel="category tag">Jetty</a>, <a href="http://juplo.de/category/less/" title="View all posts in less" rel="category tag">less</a>, <a href="http://juplo.de/category/maven/" title="View all posts in Maven" rel="category tag">Maven</a>, <a href="http://juplo.de/category/wro4j/" title="View all posts in wro4j" rel="category tag">wro4j</a>. Bookmark the <a href="http://juplo.de/combining-jetty-maven-plugin-and-wro4j-maven-plugin-for-dynamic-reloading-of-less-resources/" title="Permalink to Combining jetty-maven-plugin and wro4j-maven-plugin for Dynamic Reloading of LESS-Resources" rel="bookmark">permalink</a>.
271 </footer><!-- .entry-meta -->
272 </article><!-- #post-140 -->
273 <!-- You can start editing here. -->
274 <!-- If comments are open, but there are no comments. -->
276 <h3>Leave a Reply</h3>
277 <div id="cancel-comment-reply">
278 <small><a rel="nofollow" id="cancel-comment-reply-link" href="/combining-jetty-maven-plugin-and-wro4j-maven-plugin-for-dynamic-reloading-of-less-resources/#respond" style="display:none;">Click here to cancel reply.</a></small>
280 <form action="http://juplo.de/wp-comments-post.php" method="post" id="commentform">
281 <p>Logged in as <a href="http://juplo.de/wp-admin/profile.php">Kai Moritz</a>. <a href="http://juplo.de/wp-login.php?action=logout&redirect_to=http%3A%2F%2Fjuplo.de%2Fcombining-jetty-maven-plugin-and-wro4j-maven-plugin-for-dynamic-reloading-of-less-resources%2F&_wpnonce=09e5cb501d" title="Log out of this account">Log out »</a></p>
282 <!--<p><small><strong>XHTML:</strong> You can use these tags: <code><a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> </code></small></p>-->
283 <p><textarea name="comment" id="comment" cols="58" rows="10" tabindex="4"></textarea></p>
284 <p><input name="submit" type="submit" id="submit" tabindex="5" value="Submit Comment" />
285 <input type='hidden' name='comment_post_ID' value='140' id='comment_post_ID' />
286 <input type='hidden' name='comment_parent' id='comment_parent' value='0' />
288 <input type="hidden" id="_wp_unfiltered_html_comment_disabled" name="_wp_unfiltered_html_comment_disabled" value="2096655c89" /><script>(function() {
289 if (window === window.parent) {
290 document.getElementById('_wp_unfiltered_html_comment_disabled').name = '_wp_unfiltered_html_comment';
293 <p style="display: none;"><input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="f31e001227" /></p>
297 <aside layout:fragment="marginalcontent">
298 <div id="secondary" class="widget-area" role="complementary">
299 <aside id="archives" class="widget">
300 <h1 class="widget-title">Archives</h1>
302 <li><a href='http://juplo.de/2013/10/' title='October 2013'>October 2013</a></li>
303 <li><a href='http://juplo.de/2013/08/' title='August 2013'>August 2013</a></li>
304 <li><a href='http://juplo.de/2013/01/' title='January 2013'>January 2013</a></li>
305 <li><a href='http://juplo.de/2012/11/' title='November 2012'>November 2012</a></li>
308 <aside id="categories" class="widget">
309 <h1 class="widget-title">Most Used Categories</h1>
311 <li class="cat-item cat-item-4"><a href="http://juplo.de/category/java/" title="View all posts filed under Java">Java</a> (6)</li>
312 <li class="cat-item cat-item-6"><a href="http://juplo.de/category/hibernate/" title="View all posts filed under Hibernate">Hibernate</a> (5)</li>
313 <li class="cat-item cat-item-8"><a href="http://juplo.de/category/maven/" title="View all posts filed under Maven">Maven</a> (5)</li>
314 <li class="cat-item cat-item-9"><a href="http://juplo.de/category/jpa/" title="View all posts filed under JPA">JPA</a> (1)</li>
315 <li class="cat-item cat-item-10"><a href="http://juplo.de/category/appengine/" title="View all posts filed under appengine">appengine</a> (1)</li>
316 <li class="cat-item cat-item-11"><a href="http://juplo.de/category/oauth2/" title="View all posts filed under oauth2">oauth2</a> (1)</li>
319 <aside id="search" class="widget widget_search">
320 <h1 class="widget-title">Search</h1>
321 <form role="search" method="get" id="searchform" class="searchform" action="http://juplo.de/">
323 <label class="screen-reader-text" for="s">Search for:</label>
324 <input type="text" value="" name="s" id="s" />
325 <input type="submit" id="searchsubmit" value="Search" />
329 </div><!-- #secondary .widget-area -->
330 <div id="tertiary" class="widget-area" role="supplementary">
331 </div><!-- #tertiary .widget-area -->