Tumgik
sbglasius · 6 years
Text
Test pollution (part 2)
In my previous post I discussed test pollution in Grails running integration tests when exercising the metaClass of a class instance.
Tumblr media
This post will deal with another thing I noticed in the codebase: Mocking injected dependencies on a bean under test. An example could be a testService that has anotherService injected:
class TestService { AnotherService anotherService String fromAnotherService() { anotherService.anotherMethod() } }
class AnotherService { String anotherMethod() { return "Another Method" } }
Remember the bean wiring is done by Grails.
The integration test could look like this:
@Integration class MockAserviceSpec extends Specification { TestService testService // Autowired by Grails def "test before mocking"() { expect: testService.fromAnotherService() == 'Another Method' } def "stubbing anotherService on testService"() { given: testService.anotherService = Stub(AnotherService) { anotherMethod() >> 'Mocked Method' } expect: testService.fromAnotherService() == 'Mocked Method' } def "test after mocking"() { expect: testService.fromAnotherService() == 'Another Method' } }
The problem is, that when replacing anotherService with a Stub (or Mock or Spy), testService keeps this change in subsequent test cases. In this case, the last test ("test after mocking") will fail, and this is the result:
testService.fromAnotherService() == 'Another Method' | | | | "" false test.TestService@5b087a6b 14 differences (0% similarity) (--------------) (Another Method)
So not only is our test polluted, but but Spock resets the Stub when running the next test case, thus making the result even more unpredictable.
So just like the MockMetaClassHelper I came up with a similar solution to the problem:
trait MockServiceHelper { private Map<object>> replacedServices = [:].withDefault { [] as Set } private log = LoggerFactory.getLogger(this.getClass().name) boolean postponeServiceCleanup = false /** * Replace a service injected instances with another bean and save the variable name * which was touched * @param service * @param variable The variable replaced * @param the replacement instance (mock or another bean) * @return */ Object replaceService(Object service, String variable, Object replacement) { Set variables = replacedServices.get(service) variables << variable service[variable] = replacement log.debug "Replaced ${service.getClass().name}.${variable} with ${replacement.getClass()?.name ?: 'a proxy'}" return replacement } /** * Restore the injected variable with the bean in Application Context. If it was not replaced, nothing happens * @param instance */ void restoreServiceVariable(Object service, String variable) { Set<String> variables = replacedServices.get(service) if (variables.contains(variable)) { internalRestoreServiceBean(service, variable) } } /** * Automatically restores all service variable changes after a test finishes (cleanup) */ @After void cleanupServices() { if (!postponeServiceCleanup) { replacedServices.each { service, variables -> variables.each { variable -> internalRestoreServiceBean(service, variable) } } replacedServices.clear() } } /** * Automatically restores all service variable changes after a test finishes (cleanupSpec) */ @AfterClass void cleanupSpecServices() { if (postponeServiceCleanup) { replacedServices.each { service, variables -> variables.each { variable -> internalRestoreServiceBean(service, variable) } } replacedServices.clear() } } private void internalRestoreServiceBean(Object service, String variable) { Object originalBean = Holders.applicationContext.getBean(variable) // Using that autowireByName is used in Grails service[variable] = originalBean log.debug "Restored ${service.getClass().name}.${variable} with ${originalBean.getClass().name}" } }
Now the specification can be written like this:
@Integration class MockAserviceSpec extends Specification implements MockServiceHelper { TestService testService // Autowired by Grails def "test before mocking"() { expect: testService.fromAnotherService() == 'Another Method' } def "stubbing anotherService on testService"() { given: replaceService(testService,'anotherService', Stub(AnotherService) { anotherMethod() >> 'Mocked Method' }) expect: testService.fromAnotherService() == 'Mocked Method' } def "test after mocking"() { expect: testService.fromAnotherService() == 'Another Method' } }
The only change is, that we let the MockServiceHelper replace anotherService on testService, and with the annotation @After we let the trait cleanup when the test is done, by re-wiring the beans with their original bean reference from applicationContext, thus making the test greeen.
This makes it easier to replace a beans injected dependencies and restore them to their original value when a test has completed.
I hope you find it useful. Please leave a comment in any case.
Stay Groovy!
0 notes
sbglasius · 6 years
Text
Test pollution (part 1)
It’s been quite a while since I’ve blogged, but while my integration-test suite runs, I found time to share this.
Tumblr media
I am on a rather large project, where I'm hired to migrate to Grails 3 (among other things). Here I have seen several things in integration tests that can potentially go wrong and lead to Test pollution. This is sometime seen as randomly failing tests and could happen if test-execution order changes, exposing the pollution. One of the issues could be mocking an injected service metaClass because the test requires a specific return value, and this was the only way to make it happen.
An example could be:
A service:
class TestService { AnotherService anotherService String myProperty String myMethod() { return "My Method" } }
and the test:
class ChangeMetaClassSpec extends Specification { def 'changing the meta class on an instance'() { given: 'a test instance' TestService sut = new TestService(myProperty: 'My Property') expect: 'methods and properties returns expected values' sut.myMethod() == 'My Method' sut.myProperty == 'My Property' when: 'mocking on the instance'' sut.metaClass { myProperty = 'Mocked Property' myMethod = { -> "Mocked Method" } } then: 'meta class values should be returned instead sut.myMethod() == 'Mocked Method' sut.myProperty == 'Mocked Property' } }
In it self not dangerous, when running it as a unit test, but consider this test:
class ChangeServiceMetaClassSpec extends Specification { // This would be like an injected service in an integration test, // as it is shared between invocations of the test. @Shared TestService testService = new TestService(myProperty: 'My Property') def 'changing the meta class on an instance'() { expect: 'methods and properties returns expected values' testService.myMethod() == 'My Method' testService.myProperty == 'My Property' when: 'mocking in a bad way' testService.metaClass { myProperty = 'Mocked Property' myMethod = { -> "Mocked Method" } } then: 'meta class values should be returned instead testService.myMethod() == 'Mocked Method' testService.myProperty == 'Mocked Property' } def 'using the instance after it was mocked'() { expect: 'methods and properties returns expected values' testService.myMethod() == 'My Method' testService.myProperty == 'My Property' } }
So while this looks like the previous test, the big culprit here is mocking the injected service instance (in this case a @Shared instance). This immediately shows when running the second test
This would fail, because the metaClass changes are still there, because changes took place on the shared instance. If metaClass changes were done on the class it self:
when: 'mocking in a better way' TestService.metaClass { myProperty = 'Mocked Property' myMethod = { -> "Mocked Method" } }
and we use Spock's @ConfineMetaClassChanges([TestService]) on the test:
@ConfineMetaClassChanges([TestService]) class ChangeServiceMetaClassSpec extends Specification { .. }
Then one would think that it should work, but it does not (at least not when running it with the @Shared option)
So I came up with a small helper, that solves the problem:
import org.junit.After import org.junit.AfterClass import org.slf4j.LoggerFactory trait MockMetaClassHelper { private Map<object> originalMetaClasses = [:] private log = LoggerFactory.getLogger(this.getClass().name) boolean postponeMetaClassCleanup = false /** * Replace an instance's metaClass with a new and save reference to the original. * A closure can be specified to make changes on the new metaClass. * @param instance * @param modifications * @return */ MetaClass mockMetaClass(Object instance, Closure modifications = null) { if (originalMetaClasses.containsKey(instance) || instance.metaClass instanceof ExpandoMetaClass) { log.info("MetaClass for ${instance.getClass().name} was already mocked, reusing metaClass") return instance.metaClass } originalMetaClasses.put(instance, instance.metaClass) MetaClass temporaryMetaClass = new ExpandoMetaClass(instance.getClass(), false, true) temporaryMetaClass.initialize() instance.metaClass = temporaryMetaClass if (modifications) { instance.metaClass(modifications) } log.debug "Replaced metaClass for ${instance.getClass().name}" return instance.metaClass } /** * Restores metaClass for an instance, if it was changed * @param instance */ void restoreMetaClass(Object instance) { MetaClass originalMetaClass = originalMetaClasses.remove(instance) if (originalMetaClass) { internalRestoreMetaClass(instance, originalMetaClass) } } /** * Automatically restores all metaClass changes after a test finishes (cleanup) */ @After void cleanupMetaClasses() { if (!postponeMetaClassCleanup) { originalMetaClasses.each { instance, originalMetaClass -> internalRestoreMetaClass(instance, originalMetaClass) } originalMetaClasses.clear() } } /** * Automatically restores all metaClass changes after all tests finishes (cleanupSpec) */ @AfterClass void cleanupSpecMetaClasses() { if (postponeMetaClassCleanup) { originalMetaClasses.each { instance, originalMetaClass -> internalRestoreMetaClass(instance, originalMetaClass) } originalMetaClasses.clear() } } private internalRestoreMetaClass(Object instance, MetaClass metaClass) { log.debug "Restored metaClass for ${instance.getClass().name}" instance.metaClass = metaClass } }
This will save the original metaClass and attach a new ExpandoMetaClass which we can work on, and finally it will automatically call cleanupMetaClasses restoring the objects initial metaClass
So basically we register our changes on the metaClass like this:
class ChangeServiceMetaClassSpec extends Specification implements MockMetaClassHelper { ---- def 'changing the meta class on an instance'() { given: .... when: "metaClass is mocked" mockMetaClass(sut) { myProperty = 'Mocked Property' myMethod = { -> "Mocked Method"} } then: ....
and when the test case has completed, it the original metaClass is restored.
the mockMetaClass returns the ExpandoMetaClass and it possible to to either modifications to it, or directly on your class instances metaClass knowing that when the test completes, the original metaClass is restored.
I hope this is helpful and will stop your tests from polluting each other.
Next blog post will be about a similar issue with replacing services with mocks during integration-tests in Grails and of course a solution too.
Stay Groovy!
2 notes · View notes
sbglasius · 8 years
Text
GR8Conf India
I do not normally make conference reviews, mostly because I’m a bit lazy, and because I’d rather tell the organizers straight to the face, what I liked and disliked about their conference.
This time is partly an exception, because I did tell it the organizers in person, but I wanted to tell how impressed I was with GR8Conf India.
About four months ago, the folks from GrailsConf India approached me, since I am co-founder and organizer of the “original” GR8Conf in Copenhagen. They wanted to know if they could use the GR8Conf name to rebrand their conference. I discussed with the GR8Conf EU board, and we felt that they in previous years had proved, that they could handle doing a conference, and so we decided, that a fair usage of the name would be fine, as long as they stayed within the frame of a GR8Conf, using the logo, website, call-for-paper app and so on. 
At the same time, I submitted a couple of talks for the conference, and they were both accepted.
After today, the conference is over. There was well over 200 attendes, and I must say, that they did an excellent job at executing the conference! It was super professional: they recorded all talks, and they even did a live stream from both of the tracks! Everybody was super friendly, and the Groovy and Grails ecosystem appears to be thriving in India.
The organizers from To The New Digital have taken very good care of us, since we arrived. Yesterday they took us to see the Swaminarayan Akshardham Temple (http://akshardham.com/). From a developers perspective you can see it as the worlds largest “open source project” It took 7000 people five years to build it, they volunteered  300 million man hours! Quite impressive!
Tumblr media
It was quite spectacular to visit. Unfortunately we could not bring any electronics to the grounds, so you will have to do with a stock photo from the internet.
Today, after the event was over, To The New Digital invited us to a nice dinner, here  they introduces us to a drinking game called “I am the captain” (I became captain at the first go, which they claimed was very well done).
Finally they gave us a gift for speaking at the conference, a beautiful scarf and four silver cups. 
Thank must go to the GR8Conf India crew from To The New Digital, to the other speakers, and to all attendees for giving great feedback on my talks. 
I hope to be able to come back next year.
PS. Tomorrow our hosts will take us to see the Taj Mahal. I can hardly wait.
1 note · View note
sbglasius · 8 years
Text
Grails 3.x memory settings for run and test
This update is very short, but I hunted quite a while for the right answer.
When you do gradle run or gradle test in a Grails 3 project, the underlying logic will fork a new VM. This VM will get the default memory settings -Xms768m and -Xmx768m which for many projects are insufficient.
But it's actually very simple to give more memory to the forked VM. In build.gradle simply add:
tasks.withType(JavaExec) { jvmArgs '...' }
and set your favorites memory settings as jvmArgs
0 notes
sbglasius · 9 years
Text
I’m joining OCI!
Tumblr media
As of November 1st I will be joining ObjectComputing, Inc (OCI). This is quite exciting for me to join the company behind Grails. I am also excited, because OCI seems very committed to the Groovy and Grails community as a whole.
A little about me: My family and I live in a small village about three hours from Copenhagen. DK. Ever since 8th grade I have had a computer, and even though I have played games on them, programming has always been my passion. In y2k I started working with  Java, and in 2009 I discovered (saw the light in) Groovy and Grails. This was also the year where Guillaume Laforge and I founded GR8Conf and I got to know a lot of people who was dedicated to the community behind these technologies.
My engagement in OCI will be be working on customer projects, and when time permits I will do what I enjoy most: Tell the world how GR8 Groovy and Grails is. I will do this be through conferences like my own GR8Conf EU, and also as a speaker at other conferences. Another area I will be covering is teaching Groovy and Grails to companies and individuals who needs to have their knowledge upgraded. Finally I also hope to engage with people who do not know Grails, so I have a chance to tell them how GR8 I think it is.
I look very much forward to be working with all the bright folks at OCI and to be part of an awesome team.
0 notes
sbglasius · 9 years
Text
Digest Auth with RestClientBuilder
The other day I had to use Digest Auth for a set of rest-services I needed to call. Since I’m already using RestClientBuilder I needed a way to get it working. But finding an example that worked was harder than I though, thus this blog entry for others to use:
I found a few examples on how to configure the RestTemplate, and this is what I came up with:
First make a class that extends: > org.springframework.http.client.HttpComponentsClientHttpRequestFactory
@InheritConstructors public class HttpComponentsClientDigestAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory implements InitializingBean { private static final RND = new SecureRandom() private AuthCache authCache = new BasicAuthCache() private CredentialsProvider provider = new BasicCredentialsProvider(); String url String realm String username String password @Override protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) { HttpClientContext localContext = HttpClientContext.create(); localContext.setAuthCache(authCache) localContext.setCredentialsProvider(provider) return localContext } @Override void afterPropertiesSet() throws Exception { HttpHost host = HttpHost.create(url) DigestScheme digestAuth = new DigestScheme() digestAuth.overrideParamter("realm", realm) digestAuth.overrideParamter("nonce", nonce) authCache.put(host, digestAuth) UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); provider.setCredentials(new AuthScope(host), credentials); } private String getNonce() { byte[] bytes = new byte[16]; RND.nextBytes(bytes); bytes.encodeBase64().toString() } }
With this, all I need to do is configure my RestClientBuilder. I do this is resources.groovy:
beans = { def restAuthConfig = application.config.restService httpClientFactory(HttpComponentsClientDigestAuthHttpRequestFactory) { url = restAuthConfig.baseUrl realm = restAuthConfig.realm username = restAuthConfig.username password = restAuthConfig.password } restTemplate(RestTemplate, ref('httpClientFactory')) restBuilder(RestBuilder, ref('restTemplate')) }
And in my configuration I have
rest { baseUrl = 'http://some.server' realm = "Server defined Realm" username = "service username" password = "server password" }
That's it. Took me a while to get it working, but now that it does, it seems pretty straight forward.
0 notes
sbglasius · 9 years
Text
groovyblogs.org is back
During the past month I have worked on getting the groovyblogs.org website back on track since Glen Smith asked the GR8Conf EU crew to take over hosting and maintenances of the codebase. 
Tumblr media
When I got the codebase it was running Grails 1.2, it used the long deprecated jsecurity plugin, and generally it needed a brush-up on the UI. I decided to migrate to Grails 2.4.x (after all it is representing Groovy and Grails, so they should be at the latest version), replace jsecurity with Spring Security, and replace the UI with Twitter Bootstrap.
My first goal was restoring the feed, so that people using it would have the feed back to normal as soon as possible. That state was established mid September. Since I have worked on getting the lists back, so that the users could actually see the latest blogs on groovyblogs.org and now it has reached a point were it is possible to let previous users log in and update their blogs, and have new users sign up and register blogs.
One thing I had to abandon was the current registred users login credentials, since I had to switch from jsecurity to Spring Security. For that reason, all existing users will have to request a password change.
There might still be quirks/bugs that needs to be fixed, but I feel that it is stable enough to "go live". If you encounter a bug or have a feature request feel free to create an issue in GitHub
I invite existing users to login and update their information (I will actually send out an email to all of them), and I encourage new users to register their Groovy and Grails related blogs.
Thank you for reading through this. Comments are welcomed.
0 notes
sbglasius · 10 years
Text
One year Sabbatical
My family is moving to Berkeley in mid July. My wife will perform research at UC Berkley, my kids (Eva, 11 and Peter, 15) will attend regular school, and I will look for relevant work.
I have many years of experience in the Java world, and know my way around the Groovy ecosystem. I actually know it so well, that I have made a four days training course in Groovy and Grails.
If you want more info on my skills, please check out my resume.
So if your company is searching for an expert in Groovy and Grails - perhaps you should give me a call? 
I would prefer to work in the Berkeley area, or in down town San Fransisco to keep the commute as short as possible. I also know how to work remote, which could also be an option.
And if you know of someone who will sublet their Berkeley home, let us know :-)
0 notes
sbglasius · 10 years
Text
I18nEnums - Translate your Enums in Grails
A while back I did a plugin called EnumMessageSourceResolvable, and all though the name did say something about, what the plugin is supposed to do, the name had always bothered me. At Groovy/Grails eXchange in London last week, I shouted out on Twitter for a better name. It was suggested by https://twitter.com/domurtag - thanks!
Now it's named I18nEnums and it does exactly that - add i18n support to Enums in an easy to use way.
In Grails, when you use a
<g:select name="field" from="${list}">
in your .gsp, Grails will actually go through the trouble of looking up a text for your values, if your value implements the interface MessageSourceResolvable.
If your list is in fact the values from an Enum, then you just have to implement MessageSourceResolvable on your Enum and return the right values for getCodes(), getArguments() and getDefaultMessage(). 
Easy enough if you have one or two Enums in your code-base, but if you have many (like I do), that turns into boilerplate code - and that's where my plugin comes in handy.
All you have to do is annotate your enum class with @I18nEnum 
Here's a short example:
package test @I18nEnum enum MyEnum { ONE, Two, three }
and in your message.properties you would write
test.MyEnum.ONE = 'Value one' test.MyEnum.Two = 'Value two' test.MyEnum.three = 'Value three'
finally in your GSP you would write:
<g:select name="field" from="${test.MyEnum.values()}"/>
which will result in HTML like this
<select name="field"> <option value="ONE">Value one</option> <option value="Two">Value two</option> <option value="three">Value three</option> </select>
Grails will be clever enough to databind the enum values back to fx. a domain object using MyEnum.
If you want t know more, please check out the I18nEnum plugin page.
Suggestions for improvements are welcomed - and so is feedback when you use this plugin.
0 notes
sbglasius · 11 years
Text
Allow CORS on Apache
We're fronting the GR8Conf mobile website (m.gr8conf.eu) with an Apache server, and had some trouble getting it to work.
After digging around the internet I found this article and ended up adding these lines to my Apache configuration for the vhost in question:
<ifModule mod_headers.c> SetEnvIf Origin ^(.*)$ ORIGIN_DOMAIN=$0 Header set Access-Control-Allow-Origin: %{ORIGIN_DOMAIN}e Header set Access-Control-Allow-Methods: POST Header add Access-Control-Allow-Methods: PUT Header add Access-Control-Allow-Methods: GET Header add Access-Control-Allow-Methods: OPTIONS Header add Access-Control-Allow-Methods: PATCH Header set Access-Control-Allow-Credentials: true Header set Access-Control-Allow-Headers: origin Header add Access-Control-Allow-Headers: x-requested-with Header add Access-Control-Allow-Headers: accept Header add Access-Control-Allow-Headers: content-type Header add Access-Control-Request-Headers: accept Header add Access-Control-Request-Headers: origin Header add Access-Control-Request-Headers: x-requested-with Header add Access-Control-Request-Headers: content-type </ifModule>
Remember to run
sudo a2enmod headers 
and restart your Apache
0 notes
sbglasius · 11 years
Video
Javagruppen.dk (The Danish JUG) had the pleasure of presenting Stephen Chin when his his Nighthacking Nordic Tour took him to  Copenhagen on, January 31st 2013, and I was happy to be the host.
We took some time to talk about GroovyFX, the Groovy DSL on top of JavaFX. I had not previously worked with GroovyFX (or say JavaFX) and I was very surprised how easy and terse the code was.
We also talked a bit about the upcoming GR8Conf Europe, how the conference got started, GR8Conf Beer and other GR8 topics. 
All in all a fun night, with lots of new insights. Thank you Stephen for coming to Copenhagen.
0 notes
sbglasius · 11 years
Text
Available for Groovy/Grails assignments
I am available for contract work within a few weeks. Assignments could be Groovy, Grails or Java. I have experience in Groovy, Grails, Java, JEE, jBoss ESB, jBoss jBMP, Spring Framework, jQuery, jQuery mobile, jQuery UI, JMS, Weceem, Android and more.
I can do remote work, with optional onsite two days a week, depending on distance, or assignments within 50 km of my home.
Contracts will be through my employer: Openminds.dk 
Contact me for more information, CV or just a chat. Mobile: +45 4044 9188, Skype: sbglasius
0 notes
sbglasius · 12 years
Text
How to resolve a HTTP redirect in Groovy
Here's just a very small sniplet, where I resolve the real URL from a series of redirects:
def findRealUrl(url) { HttpURLConnection conn = url.openConnection() conn.followRedirects = false conn.requestMethod = 'HEAD' if(conn.responseCode in [301,302]) { if (conn.headerFields.'Location') { return findRealUrl(conn.headerFields.Location.first().toURL()) } else { throw new RuntimeException('Failed to follow redirect') } } return url }
Can also be found here: https://gist.github.com/3899754
0 notes
sbglasius · 12 years
Text
Grails exporting content as zip file
For GR8Conf I had all the invoices in my database as pdf blobs (byte[]) and I wanted to export them all in one zipped file.
I did this very simple action in my controller, which did the job:
def exportInvoices() { def attendees = Attendee.findAllByConfirmedIsNotNullAndInvoiceIsNotNull() response.contentType = 'application/zip' new ZipOutputStream(response.outputStream).withStream { zipOutputStream -> attendees.each { attendee -> def invoice = attendee.invoice zipOutputStream.putNextEntry(new ZipEntry("invoice_${invoice.number}.pdf")) zipOutputStream << invoice.pdf // a byte[] } } }
Just wanted to share my finding. You just gotta love the simplicity of Groovy and Grails
2 notes · View notes
sbglasius · 12 years
Text
Looking for work
Unfortunately, I'm out of work at the end of May (potentially sooner, if new work shows up). This means that I'm available for new assignments or possibly even a fixed position.
I have a solid foundation in Groovy and Grails, and I know how to handle a lot of techologies sourounding a Grails project, like jQuery, jQuery UI, jQueryMobile, JMS, Web-services, Ajax, Apache2 configuration, writing Grails plugins etc.
I have been working with Grails and Groovy professionally the last four years. 
Also I'm proud to be co-founder and organizer of GR8Conf (http://gr8conf.org)
I am used to remote work, if your assignment is not in daily travel distance from my current location.
If you want to learn more, please contact me for an extended CV. 
0 notes
sbglasius · 12 years
Text
Grails reading from the filesystem
This might be trivial, but I search for it again and again, and now I've decided to write it down, for myself to remember it (and for others to find).
I sometimes find myself in need of reading data from my Grails projects filesystem, most of the times when bootstrapping the application.
Inject 'grailsApplication' in your BoosStrap, Controllers, TagLibs and Services: 
def grailsApplication
Place your files in /web-app/WEB-INF/resources and use this line of code to retrieve the directory of your resources:
def resources = grailsApplication.mainContext.getResource('/WEB-INF/resources').file
  This works both in development and production
16 notes · View notes
sbglasius · 12 years
Text
Grails, Weceem and Apache2
I have been using most of my day getting the GR8Conf websites running on the new server.
Let me tell you, it was not trivial, but I managed to get it working (and working GR8 too).
The setup:
A Weceem application running in the context /gr8confcms
A Tomcat 6 server
A MySql server instance (not important here)
An Apache2 server running on a Ubuntu server
The plan:
I want to run all the GR8Conf websites in one Weceem application, using the Weceem spaces (one war)
gr8conf.org should be the default space in Weceem
I want gr8conf.eu and gr8conf.us to be two other spaces in Weceem
The old gr8conf websites from previous years (two wars) should be accessible as 2010.gr8conf.eu and 2011.gr8conf.eu
I want the url's to look as if they do not run under a certain context, but as root.
The admin-part of the application should work as well (the Cookie path should be correct).
I don't want to make changes in the Tomcat server.xml file
I want to be able to deploy via the Tomcat manager interface or directly from Grails using the tomcat target.
After reading numerous blog-posts about configuration of Tomcat and Apache, I found a setup that works. Thats the good part, the bad part is, that it requires quite some configuration on the Apache side. 
Requirements:
Apache webserver with the following modules enabled:
mod_proxy
mod_proxy_html
mod_rewrite
mod_headers
Tomcat 6 (or 7)
Installing those modules should be pretty straight forward using apt-get and a2enmod.
First thing to get working: gr8conf.org
I could not avoid changing the Tomcat server.xml, as I want to use AJP. Uncomment this section in the server.xml
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Now I made a new file in /etc/apache/sites-available called gr8conf.org with this content:
<VirtualHost *:80> ServerName gr8conf.org ServerAlias www.gr8conf.org LogLevel warn ErrorLog /var/log/apache2/gr8conf_org-error.log CustomLog /var/log/apache2/gr8conf_org-access.log combined ProxyRequests Off ProxyPreserveHost On RewriteEngine On <Proxy *> AddDefaultCharset off Order deny,allow Allow from all </Proxy> # redirect request if user is using an alias for this server RewriteCond "%{HTTP_HOST}" "!^gr8conf.org$" RewriteRule "^(.*)$" "http://gr8conf.org$1" [L,R=301] # redirect stray requests to the correct url (*1) RewriteRule "/gr8confcms/(.*)$" "http://gr8conf.org/$1" [L,NE] # edit the location header, to match the correct path (*2) Header edit Location "^http://gr8conf.org/gr8confcms/(.*)$" "http://gr8conf.org/$1" # edit the cookie path to match the correct location, otherwise it will not be stored in the browser Header edit Set-Cookie "^(.*); Path=/gr8confcms$" "$1; Path=/" # the actual proxy ProxyPass / ajp://localhost:8009/gr8confcms/ ProxyPassReverse / ajp://localhost:8009/gr8confcms/ # rewrite links to css and javascript SetOutputFilter DEFLATE;proxy-html;INFLATE ProxyHTMLExtended On ProxyHtmlURLMap /gr8confcms/ / # The doctype is set to Legacy to avoid stripping of iframe links (*3) ProxyHTMLDoctype HTML Legacy </VirtualHost>
I hope the comments speaks pretty much for themself.
The thing that took the most time to figure out, was how to handle Spring Security login, storing the cookie in the right place. I did try to use ProxyPassReverseCookieDomain and ProxyPassReverseCookiePath, but could not get it working. When I started to rewrite the header instead it worked instantly (see *2). 
While working with the Cookie, I also noticed that the URL with the 'jsessionid' was being escaped, so I put the [NE] option in the RewriteRule (see *1) 
The last thing to notice is, that mod_proxy_html would strip information from any iframes (I needed those), and to avoid that, I added the 'Legacy' to ProxyHTMLDoctype.
Finally I add it to the apache configuration:
sudo a2ensite gr8conf.org
Now that I got the first site working, the next was pretty straight forward.
Getting the gr8conf.eu site working:
I made a second file in /etc/apache/sites-available called gr8conf.eu with this content:
<VirtualHost *:80> ServerName gr8conf.eu ServerAlias www.gr8conf.eu LogLevel warn ErrorLog /var/log/apache2/gr8conf_eu-error.log CustomLog /var/log/apache2/gr8conf_eu-access.log combined ProxyRequests Off ProxyPreserveHost On RewriteEngine On AddDefaultCharset off Order deny,allow Allow from all # redirect request if user is using an alias for this server RewriteCond "%{HTTP_HOST}" "!^gr8conf.eu$" RewriteRule "^(.*)$"" "http://gr8conf.eu$1" [L,R=301] # If hitting the root, redirect to the index page RewriteRule "^/$" "http://gr8conf.eu/index" [L] # The url's for uploads are handled by the gr8conf.org configuration RewriteRule "/gr8confcms/uploads/(.*)$" "http://gr8conf.org/uploads/$1" [L] # The admin page is handled by the gr8conf.org configuration (It's not the actual URL....) RewriteRule "/admin" "http://gr8conf.org/admin" [L,R=301] RewriteRule "/gr8confcms/(.*)$" "http://gr8conf.eu/$1" [L,NE] Header edit Location "^http://gr8conf.eu/gr8confcms/(.*)$" "http://gr8conf.eu/$1" Header edit Set-Cookie "^(.*); Path=/gr8confcms$" "$1; Path=/" # The proxy is now pointing to the url of the EU-2012 space in Weceem ProxyPass / ajp://localhost:8009/gr8confcms/content/eu2012/ ProxyPassReverse / ajp://localhost:8009/gr8confcms/content/eu2012/ SetOutputFilter INFLATE;proxy-html;DEFLATE ProxyHTMLExtended On ProxyHtmlURLMap /gr8confcms/content/eu2012/ / ProxyHTMLDoctype XHTML Legacy </VirtualHost>
I have highlighted the major changes compared to the setup for gr8conf.org, no need to elaborate further on the comments.
The configuration for the old sites (2011.gr8conf.eu) follows the same recipe.
I think this pretty much covers it... I'm not an Apache expert, but with with weights and pulleys I managed to get it running smoothly.
If you find any flaws in my setup, or is it possible ot make it simpler, please let me know! 
/Søren
10 notes · View notes