<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4466305307772589754</id><updated>2017-01-19T10:47:33.393+01:00</updated><category term="Grails"/><category term="IT-Consulting"/><category term="ElasticSearch"/><category term="Konferenz"/><category term="exensio"/><category term="Portal"/><category term="Groovy"/><category term="claretportal"/><category term="User Interface"/><category term="Mobile"/><category term="e20"/><category term="Informationsportal"/><category term="Testing"/><category term="JavaScript"/><category term="solr"/><category term="BigData"/><category term="HTML5"/><category term="Business Intelligence"/><category term="Excel"/><category term="Open-Source"/><category term="Selenium"/><category term="Usability"/><category term="socialmedia"/><category term="Testautomatisierung"/><category term="java"/><category term="Apache Webserver"/><category term="Browsertest"/><category term="D3"/><category term="Enterprise Search"/><category term="Geb"/><category term="IoT"/><category term="Logstash"/><category term="Sensoren"/><category term="Springboot"/><category term="Statistical Data"/><category term="Tomcat"/><category term="sharepoint"/><category term="Kapow"/><category term="Kibana"/><category term="Spock"/><category term="socbiz"/><category term="Kofax"/><category term="Lucene"/><category term="MySQL"/><category term="Python"/><category term="RaspberryPi"/><category term="WebLogic Portal"/><category term="WebLogic Server"/><category term="pharma"/><category term="Enterprise Applikationen"/><category term="Jira"/><category term="Responsive Design"/><category term="Spring"/><category term="Wissensmanagement"/><category term="barcamp"/><category term="jquery mobile"/><category term="BI"/><category term="Confluence"/><category term="Content"/><category term="GORM"/><category term="Knowledgemanagement"/><category term="Migration"/><category term="Nagios"/><category term="Play framework"/><category term="REST"/><category term="Responsive Web Design"/><category term="Rickshaw"/><category term="Web Applikation"/><category term="rwd"/><category term="AS3"/><category term="BPMN"/><category term="CMS"/><category term="EPK"/><category term="Flash"/><category term="Integration"/><category term="Liferay"/><category term="Monitoring"/><category term="NVD3.js"/><category term="PIM"/><category term="Projektplanung"/><category term="Qlik"/><category term="SNMP"/><category term="Scrapy"/><category term="Security"/><category term="Sense"/><category term="Virtualisierung"/><category term="Web Scraping"/><category term="Wiki"/><category term="agile"/><category term="bcka"/><category term="cloud"/><category term="excel2web"/><category term="ggx"/><category term="iPad"/><category term="shipit-day"/><category term="vaadin"/><category term="web"/><category term="ADF"/><category term="API"/><category term="ActionScript"/><category term="Anforderungsanalyse"/><category term="Anforderungsmanagement"/><category term="Apache Wink"/><category term="Apps"/><category term="Aufgabenplanung"/><category term="Auswahlberatung"/><category term="Automatisierter Build"/><category term="Batch"/><category term="Beratung"/><category term="BlackBerry"/><category term="Build Prozess"/><category term="CKEditor"/><category term="Canvas"/><category term="Compass Search"/><category term="DMS"/><category term="DPWK"/><category term="DWH"/><category term="EJB 3.0"/><category term="ESB"/><category term="ETL"/><category term="EclipseLink"/><category term="Enterprise Application Integration"/><category term="Enterprise Service Bus"/><category term="Evaluation"/><category term="FacetView"/><category term="Fail2ban"/><category term="Fixtures"/><category term="Flex"/><category term="Geolocation"/><category term="Glosse"/><category term="Grid"/><category term="Grok"/><category term="Groovy Migration"/><category term="Hibernate"/><category term="Informationsmanagement"/><category term="JPA"/><category term="JRockit"/><category term="Jetty"/><category term="Kettle"/><category term="Kontraktlogistik"/><category term="Lancom"/><category term="Layout"/><category term="Leaflet"/><category term="Lizenzen"/><category term="LocalConnection"/><category term="Logistik"/><category term="Map"/><category term="Mapbox"/><category term="Mocha"/><category term="Node.js"/><category term="OS X"/><category term="Offshoring"/><category term="Pharmamarketing"/><category term="Pharmavertrieb"/><category term="Piwik"/><category term="Propagation"/><category term="Prozessanalyse"/><category term="Prozessmodellierung"/><category term="R"/><category term="Releaseplanung"/><category term="Requirements Engineering"/><category term="Requirements Management"/><category term="Router"/><category term="Ruby"/><category term="SAP EP"/><category term="SVG"/><category term="Sauce Labs"/><category term="ScanCode toolkit"/><category term="SeInterpreter"/><category term="Sensor"/><category term="Snapsvg"/><category term="Spezifikation"/><category term="Subversion"/><category term="Taxonomie"/><category term="Tweepy"/><category term="WebDAV"/><category term="Webtest"/><category term="Wissensdatenbank"/><category term="akka"/><category term="bcs5"/><category term="bedcon"/><category term="digital workplace"/><category term="industrie40"/><category term="ionic"/><category term="jax"/><category term="km"/><category term="olap"/><category term="projektinfo"/><category term="soa"/><category term="web20"/><title type='text'>exensio it blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.exensio.de/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/-/Grails'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/search/label/Grails'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/-/Grails/-/Grails?start-index=26&amp;max-results=25'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>56</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-3650104092735786688</id><published>2016-10-13T11:20:00.000+02:00</published><updated>2016-10-13T11:20:43.999+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Spock"/><category scheme="http://www.blogger.com/atom/ns#" term="Testautomatisierung"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><title type='text'>Grails, good to know: Working with the Build Test Data Plugin</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://4.bp.blogspot.com/-70G4zkoHwbU/V_9OhAuAxGI/AAAAAAAAA1M/aH6RySbJuHonnU19gRL0ZUqkhpBS98dEwCLcB/s1600/Grails_logo_2009_2010.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://4.bp.blogspot.com/-70G4zkoHwbU/V_9OhAuAxGI/AAAAAAAAA1M/aH6RySbJuHonnU19gRL0ZUqkhpBS98dEwCLcB/s200/Grails_logo_2009_2010.jpg&quot; width=&quot;200&quot; height=&quot;56&quot; /&gt;&lt;/a&gt;&lt;/div&gt;There are several choices for creating test data when working with &lt;a href=&quot;https://grails.org/&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt;. Beside the manual creation of domain objects it is possible to use one of the existing plugins. Before Grails 3.0 one of the good choices was the &lt;a href=&quot;https://grails.org/plugin/fixtures&quot; target=&quot;_blank&quot;&gt;Fixture Plugin&lt;/a&gt; for specifying domain test data. This plugin is not ported to newer Grails versions and therefore no longer an option. Another good choice is the &lt;a href=&quot;https://github.com/longwa/build-test-data/wiki&quot; target=&quot;_blank&quot;&gt;Build Test Data Plugin&lt;/a&gt; which is in the focus of this post. &lt;br /&gt;&lt;br /&gt;After referencing the plugin via the build.gradle file, new domain objects can be created easily by calling the &lt;i&gt;build()&lt;/i&gt; method on a domain class.  The following listing demonstrates the usage by creating two new entities of the type project. All required attributes of the entities are populated automatically or can be alternatively specified explicitly as shown for the second entity. &lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/244b8ad330802b640847c67d873f1fab.js&quot;&gt;&lt;/script&gt; The &lt;a href=&quot;https://github.com/longwa/build-test-data/wiki&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt; provides more information on the general usage of the plugin. The following sections provide two best practices on using some less known features of the Build Test Data Plugin. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Testing REST APIs&lt;/h2&gt;&lt;br /&gt;Nowadays many Grails applications provide REST APIs that have also run through the automatic test cycles.  Assuming in our case that an existing project from an external system should be imported via REST, it is necessary for a test to construct a post request that contains all necessary attributes and especially an ID. When calling the standard &lt;i&gt;build()&lt;/i&gt; method on the project entity and then converting it to JSON, the object will already be saved in the database before sending it via REST to the application. This can be avoided by calling the &lt;i&gt;buildWithoutSave()&lt;/i&gt; method instead of the &lt;i&gt;build()&lt;/i&gt; method. An example for the usage is shown in the following code snippet:  &lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/edd59f4e46f57df256d86ad27e73aed7.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;&lt;h2&gt;Creation of custom identities&lt;/h2&gt;&lt;br /&gt;The ID of the entity is available after creating a new entity via the &lt;i&gt;build()&lt;/i&gt; method, when using the standard settings for domain classes. Depending on the requirements it might be necessary to use custom IDs for the database that are assigned manually. This can be specified in the mapping block of a domain class, as shown below: &lt;script src=&quot;https://gist.github.com/exblog/eba5acec99785ed0c30441306a5cb095.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;To avoid the manual assignment of IDs in test cases it is possible to enhance the &lt;a href=&quot;https://github.com/longwa/build-test-data/wiki/TestDataConfig&quot; target=&quot;_blank&quot;&gt;TestDataConfig&lt;/a&gt; file. This file of the BuildTestData Plugin can be used to define static or dynamic values for the attributes of entities when creating test data.  In our case we assume that UUIDs are assigned as IDs to the domain classes. We want to make sure that an artificial ascending UUID is assigned to every created entity. This can be done by defining a closure that generates the assignment code of an ID for each domain class which is shown in the following listing. &lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/8115f18323d7cbf640d833ebe3d91076.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;A simple test for verifying the creation of project entities and its correct UUID assignment can look like shown below. &lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/a84601785abd9e8067c07eadc56ec091.js&quot;&gt;&lt;/script&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/3650104092735786688/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/10/grails-good-to-know-working-with-build.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/3650104092735786688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/3650104092735786688'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/10/grails-good-to-know-working-with-build.html' title='Grails, good to know: Working with the Build Test Data Plugin'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://4.bp.blogspot.com/-70G4zkoHwbU/V_9OhAuAxGI/AAAAAAAAA1M/aH6RySbJuHonnU19gRL0ZUqkhpBS98dEwCLcB/s72-c/Grails_logo_2009_2010.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-7653698935958895781</id><published>2016-10-05T10:26:00.000+02:00</published><updated>2016-10-05T10:26:48.079+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Geb"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Migration"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><category scheme="http://www.blogger.com/atom/ns#" term="Webtest"/><title type='text'>Migration von Grails 1.3.7 nach 3.1.9 - Testing - Teil 3</title><content type='html'>Dies ist der dritte Beitrag über die Migration einer Grails Applikation. Im &lt;a href=&quot;http://blog.exensio.de/2016/08/migration-grails-137-nach-319.html&quot;&gt;ersten Teil&lt;/a&gt;&amp;nbsp;habe ich mich mit allgemeinen Schwierigkeiten und Lösungen befasst und im &lt;a href=&quot;http://blog.exensio.de/2016/08/migration-grails-137-nach-319-erstellen.html&quot;&gt;Zweiten Teil&lt;/a&gt;&amp;nbsp;eine selbstentwickelte Lösung zum Erstellen von Testdaten vorgestellt. In diesem Beitrag werde ich speziell die Tests beleuchten.&lt;br /&gt;&lt;h4&gt;Testarten&lt;/h4&gt;In der existierenden Applikation gibt es eine übersichtliche Anzahl von Unit-Tests. Der größte Teil der Integrationstests besteht aus Controller- Integrationstests und nur wenigen Service- Integrationstests. Als funktionale Tests wurden historisch bedingt die Frameworks&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Geb&lt;/span&gt; und &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Webtest&lt;/span&gt;&amp;nbsp;eingesetzt.&lt;br /&gt;In früheren Versionen konnte das Testing Framework &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Spock&lt;/span&gt; über ein Plugin eingebunden werden, während es in neueren Versionen direkt in Grails integriert ist. Auch Geb muss nicht mehr als Plugin eingebunden werden sondern wird direkt unterstützt. Webtest ist ein Plugin das zum automatischen Testen von Web-Applikationen implementiert wurde, jedoch nicht mehr weiter entwickelt wird und deshalb nicht mehr verwendet werden kann.&lt;br /&gt;&lt;h4&gt;Unit Tests&lt;/h4&gt;Da schon in der ursprünglichen Version das Test Framework &lt;span style=&quot;font-family: inherit;&quot;&gt;Spock&lt;/span&gt; verwendet wurde, konnten die Unit Tests ohne große Schwierigkeiten migriert werden. Hauptsächlich sind Funktionsaufrufe überflüssig, die durch Annotationen ersetzt wurden. &lt;br /&gt;&lt;h4&gt;Integrationstests&lt;/h4&gt;Es wird empfohlen Controller möglichst als Unit Tests zu realisieren und Services mit Integrationstests zu testen. Sämtliche Versuche, die bestehenden Controller-Integrationstests zu migrieren, blieben erfolglos. Man kann ein Objekt eines Controllers erstellen, jedoch war es nicht möglich, diesen mit allen Abhängigkeiten zu initialisieren. Schwierigkeiten hierbei waren die &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Hibernate-Session&lt;/span&gt; und &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;HTTP-Sessions&lt;/span&gt;, die nicht korrekt funktionierten, so dass bspw. ein Service keine Daten der Datenbank abfragen oder die HTTP- Session bearbeiten&amp;nbsp;konnte.&lt;br /&gt;&lt;div&gt;Wir haben uns dazu entschlossen, den Empfehlungen zu folgen und die Tests zu ändern. Das bedeutet für die Controller neue Unit Tests zu schreiben und für die Services Integrationstests. Ich glaube, dass durch diesen Schritt eine höhere Robustheit gegenüber kommenden neueren Versionen geschaffen wird, da jetzt ausschließlich native Bordmittel von Grails verwendet werden.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Beim Migrieren der Service-Integrationstests macht sich vor allem Grails&#39; Umstellung auf Spring- Boot bemerkbar, das seit der Version 3.0 verwendet wird &lt;a href=&quot;http://docs.grails.org/3.0.0/guide/introduction.html#whatsNew&quot; target=&quot;_blank&quot;&gt;[1]&lt;/a&gt;. Denn ab jetzt wird die &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;setupSpec&lt;/span&gt; Funktion von Spock, der ersten Testklasse, vor dem Start der Applikation ausgeführt, was deren Nutzen stark einschränkt. Beispielsweise ist das Anlegen oder Laden von Daten aus der Datenbank, die zum Testen verwendet werden sollen, nicht mehr möglich, denn sämtliche Hibernate- Funktionen sind nicht verfügbar. Weiter wird der &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;where&lt;/span&gt; Block, der in Spock verwendet werden kann, vor dem Ausführen der &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;setup&lt;/span&gt;- Methode ausgewertet. Somit sollten dort statische Werte verwendet werden, wie &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;String&lt;/span&gt;, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;int&lt;/span&gt; oder &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;boolean&lt;/span&gt;. Es ist möglich, eine ähnliche Funktionsweise wie die setupSpec Funktion nachzubilden.&lt;br /&gt;Dazu wird eine statische boolean Variable &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;initialized = false&lt;/span&gt; angelegt. In der setup-Funktion wird zusätzlich zu dem Code, der vor jeder Testmethode ausgeführt werden soll, eine if- Condition verwendet um Code zu kapseln, der nur ein einziges mal zu Beginn ausgewertet werden soll. Hierzu wird mit &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;!initialized&lt;/span&gt; überprüft, ob dieser Teil bereits einmal durchlaufen wurde. &amp;nbsp;Am Ende der if- condition wird die Variable &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;initialized&lt;/span&gt; auf &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;true&lt;/span&gt; gesetzt und somit wird dieser Codeteil nicht noch einmal ausgeführt. Das nachfolgende Codebeispiel veranschaulicht das Prinzip.&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/fd79bb656253987ec8cbd00d557b3407.js&quot;&gt;&lt;/script&gt; &lt;br /&gt;&lt;h4&gt; Funktionale Tests&lt;/h4&gt;Da in der ursprünglichen Version ebenfalls schon Geb verwendet wurde, können diese Tests ohne größere Anstrengungen migriert werden. Auch hier sind nur vereinzelt kleinere Änderungen durchzuführen. Zum Beispiel müssen alle Testklassen mit der Annotation &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@Integration&lt;/span&gt; markiert werden, oder der &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;contextPath&lt;/span&gt; muss bei der Url Definition der &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;pages&lt;/span&gt;&amp;nbsp;erweitert werden, da Grails diesen nicht mehr standardmäßig auf den Namen der Applikation setzt &lt;a href=&quot;http://docs.grails.org/3.1.9/guide/testing.html#functionalTesting&quot; target=&quot;_blank&quot;&gt;[2]&lt;/a&gt;.&lt;br /&gt;&lt;div&gt;Das dem Grails Webtest Plugin zu Grunde liegende Projekt&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Canoo Webtest&lt;/span&gt;&amp;nbsp;wird nicht mehr entwickelt und somit ist auch das Grails Plugin nicht mehr aktuell und funktionsfähig. &lt;a href=&quot;https://github.com/canoo/webtest&quot; target=&quot;_blank&quot;&gt;[3]&lt;/a&gt; &lt;a href=&quot;https://grails.org/plugin/webtest&quot; target=&quot;_blank&quot;&gt;[4]&lt;/a&gt; Es gibt somit keine Alternative außer der Neu-Implementierung dieser Tests mit Geb. Ich habe eine - wenn auch sehr umständliche - Möglichkeit gefunden, die alten Webtests gegen die migrierte Applikation auszuführen. Der Vorteil hierbei ist, dass die neue Applikationen während der Migration auf die richtige Funktionalität geprüft werden kann. Somit hat man die Gewissheit, dass sie wie gewünscht funktioniert, auch wenn noch keine umfangreichen neu implementierten Tests vorhanden sind.&lt;/div&gt;&lt;div&gt;Dazu lässt sich in der Konfigurationsdatei &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;webtest.properties&lt;/span&gt;&amp;nbsp;mit den folgenden drei Zeilen einstellen, welcher Server getestet werden soll. Da dadurch die alte Applikation gestartet wird, muss darauf geachtet werden, dass sie mit einem anderen Port wie die Neue startet, da es sonst zu Fehlern kommt.&lt;/div&gt;&lt;script src=&quot;https://gist.github.com/exblog/b98787e9ac90baa816d8762669bbf902.js&quot;&gt;&lt;/script&gt; &lt;br /&gt;&lt;h4&gt;Fazit&lt;/h4&gt;Die Migration der Tests ist in unserem Fall der größte Teil Umstellung auf Grails 3.1.9. Da es in früheren Versionen noch mehrere Test-Möglichkeiten gab und auch Grails sich stark weiter entwickelt hat, ist es leider nicht immer möglich, alte Tests zu migrieren - ein großer Teil der Tests muss komplett neu geschrieben werden.&lt;br /&gt;Ich hoffe ich kann mit dieser Blog-Artikel-Reihe über die Migration einer großen Grails Applikation einigen Interessierten einige Arbeit ersparen, indem sie auf die aufgezeigten Lösungen zurückgreifen.&lt;br /&gt;&lt;h4&gt;Links&lt;/h4&gt;&lt;div&gt;[1]&amp;nbsp;&lt;a href=&quot;http://docs.grails.org/3.0.0/guide/introduction.html#whatsNew&quot; target=&quot;_blank&quot;&gt;Neuerungen seit Grails 3&lt;/a&gt;&lt;/div&gt;&lt;div&gt;[2]&amp;nbsp;&lt;a href=&quot;http://docs.grails.org/3.1.9/guide/testing.html#functionalTesting&quot; target=&quot;_blank&quot;&gt;Funktionale Tests in Grails 3.1.9&lt;/a&gt;&lt;/div&gt;&lt;div&gt;[3]&amp;nbsp;&lt;a href=&quot;https://github.com/canoo/webtest&quot; target=&quot;_blank&quot;&gt;Canoo Webtest Sourcecode auf GitHub&lt;/a&gt;&lt;/div&gt;&lt;div&gt;[4]&amp;nbsp;&lt;a href=&quot;https://grails.org/plugin/webtest&quot; target=&quot;_blank&quot;&gt;Grails Webtest Plugin&lt;/a&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/7653698935958895781/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/10/migration-von-grails-137-nach-319.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/7653698935958895781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/7653698935958895781'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/10/migration-von-grails-137-nach-319.html' title='Migration von Grails 1.3.7 nach 3.1.9 - Testing - Teil 3'/><author><name>Sebastian Jäger</name><uri>https://plus.google.com/114000193552020861532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-yY0lpW8_nzw/AAAAAAAAAAI/AAAAAAAAABE/ueiT5IHPLFY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-6958112712635532517</id><published>2016-09-27T06:30:00.000+02:00</published><updated>2016-09-27T06:30:27.580+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Fixtures"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Migration"/><title type='text'>Migration von Grails 1.3.7 nach 3.1.9 - Erstellen von Testdaten - Teil 2</title><content type='html'>Wie schon im &lt;a href=&quot;http://blog.exensio.de/2016/08/migration-grails-137-nach-319.html&quot;&gt;ersten Teil&lt;/a&gt; dieser Reihe beschrieben, stand ich während der Migration einer Applikation der Version 1.3.7 auf 3.1.9 vor dem Problem, dass das Plugin&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Fixtures&lt;/span&gt;, welches zum Erstellen und&amp;nbsp;Einspielen von Testdaten verwendet wurde, nicht mehr weiterentwickelt wird. Hier will ich nun darstellen, welche Anforderungen wir an eine neue Lösung hatten, welche Möglichkeiten sich boten und was schließlich dazu führte, eine eigene Lösung zu implementieren. Abschließend werde ich das Prinzip meiner Lösung näher erläutern und Codebeispiele geben.&lt;br /&gt;&lt;h4&gt;Anforderungen&lt;/h4&gt;Eine besondere Funktionalität des &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Fixtures-&lt;/span&gt;Plugins ist die Möglichkeit, den einzelnen Testdaten-Instanzen einen eindeutigen Namen zu geben und diese in Test Cases zu referenzieren. Da Testdaten vorhanden waren, die natürlich wieder verwendet werden sollten, ist es zusätzlich von Vorteil, wenn die Daten möglichst ähnlich dargestellt werden. Damit ist es möglich mit Hilfe der Suchen und Ersetzen Funktion der IDE wiederkehrende Muster zu finden und diese an das neue Format anzupassen. Eine weitere Anforderung war die Robustheit gegenüber Änderungen. Zum Beispiel sollte es keine großen Anpassung der Fixtures nach sich ziehen, wenn einem Attribut der Constraint &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;bindable:false&lt;/span&gt; hinzugefügt wird. Diese Änderung würde bei der Erstellung einer Instanz erzwingen, dass dieses Attribut nicht innerhalb einer Map übergeben wird, sondern einzeln gesetzt werden muss.&lt;br /&gt;&lt;h4&gt;Alternativen&lt;/h4&gt;Das Grails-Plugin &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Build-Test-Data&lt;/span&gt; wurde für die Erstellung von Testdaten implementiert. Es analysiert automatisch Constraints einer Domainklasse und erstellt valide Instanzen. Möglich wäre es, die schon vorhandenen Daten zu verwenden und daraus Instanzen zu erstellen - jedoch nicht, ohne eine eigene Erweiterung die Daten zu speichern und wieder zu verwenden. &lt;br /&gt;&lt;br /&gt;Da ohne eine eigene Implementierung keine Möglichkeit gegeben war, kam die Idee auf, die einzelnen Instanzen manuell per Konstruktor-Aufruf zu erstellen. Hierzu müsste jedoch die Syntax der Test-Daten stark geändert werden. Auch könnte es zu Problemen führen, wenn es Änderungen an den Constraints gibt. Wird, wie oben beschrieben, zu einem Attribut der Constraint &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;bindable:false&lt;/span&gt; hinzugefügt, müssten alle Instanzen der Domainklasse angepasst werden.&lt;br /&gt;&lt;br /&gt;Da keine der Lösungen zufriedenstellend verwendet werden konnte, habe ich mich dazu entschlossen, eine eigene Lösung zu implementieren, um Test-Daten zu erstellen. Mit der eigenen Implementierung ist es wieder möglich, die Testdaten in Test Cases zu referenzieren.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;span style=&quot;font-kerning: none;&quot;&gt;Implementierung der eigenen Logik&lt;/span&gt;&lt;/h3&gt;Es soll die Möglichkeit geboten werden, Daten zu definieren, die automatisch gespeichert werden. Für komplexere Beziehungen soll es möglich sein, separate Daten zu beschreiben, die zunächst erstellt und nicht explizit gespeichert werden sollen, da diese von anderen Daten abhängen und dort ein kaskadierendes Speichern notwendig ist. Abschließend kann eine Funktion implementiert sein,  in der beliebige Operationen für Modifikationen ausführbar sind.&lt;br /&gt;&lt;h4&gt;Data-Klasse&lt;/h4&gt;Da alle Klassen einem bestimmten Aufbau folgen müssen, um eine korrekte Funktionsweise zu gewährleisten, habe ich eine abstrakte Oberklasse implementiert, von der geerbt wird. Diese besitzt folgende leere Funktionen, die dazu verwendet werden um zum einen die Daten auszulesen und zum andern ggf. spezielle Beziehungen herzustellen:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;getPreData&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;:&lt;/span&gt;&amp;nbsp;Diese Methode muss überschrieben werden und&amp;nbsp;&lt;/span&gt;eine Map mit Daten zurückgeben.&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;getData:&lt;/span&gt;&amp;nbsp;Diese Methode muss ebenso&amp;nbsp;eine Map der Daten zurück geben.&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;post:&lt;/span&gt;&amp;nbsp;Diese Methode kann überschrieben werden. &lt;/li&gt;&lt;/ul&gt;Beide &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;get&lt;/span&gt;&amp;nbsp;Methoden müssen eine Map zurückgeben, in der die Daten enthalten sind. Als Schlüssel wird jeweils der Name verwendet, unter dem die Testdaten Instanz referenziert werden kann, und als Wert eine Map, die die Attribute und deren Werte enthält. Zusätzlich muss der Schlüssel &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;domainName&lt;/span&gt; enthalten sein, der die Domainklasse definiert. Da der Aufbau der neuen Daten ähnlich dem der Fixtures ist lassen sich diese schnell passend migrieren. Wie nachfolgend zu sehen müssen vor allem die Klassen sowie Methoden-Deklarationen erweitert werden.&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/9414aa2b90073a79a3cdf4a56387411a.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/f4467f017b34e7c92eabf1624fb1365b.js&quot;&gt;&lt;/script&gt; Sollen Beziehungen zwischen Instanzen hergestellt werden, realisiert man diese mit Closures. Dabei wird, wie oben zu sehen eine Referenz auf die gewünschte Instanz zurückgeben. Da die Closure erst beim Erstellen der Daten ausgewertet wird, muss die Reihenfolge beachtet werden, in der man Daten erstellt. Bei Beziehungen, die mit &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;belongsTo&lt;/span&gt;&amp;nbsp;markiert sind, greift ein kaskadierendes Speichern, das die abhängige Instanz mitspeichert. Hierfür wurde die Methode &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;getPre&lt;/span&gt;&amp;nbsp;geschaffen, die es ermöglicht, Daten zu erstellen, die nicht explizit gespeichert werden, aber nach dem Erstellen der Hauptdaten  auf Persistenz überprüft werden. In der Methode &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;post&lt;/span&gt;&amp;nbsp;kann man beliebige Operationen ausführen. In der migrierten Applikation sind in wenigen Domainklassen Hilfsfunktionen implementiert um Beziehungen zwischen Instanzen herzustellen. Dabei wird nicht nur die Referenz gespeichert sondern auch Attributwerte an verschiedensten Stellen in der Datenbank geändert. Es bietet sich an dies, wie in dem nachfolgenden Codeausschnitt zu sehen, in der &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;post&lt;/span&gt; Methode zu verwenden.&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/cabe5ece614924bf4f04a68716ae4d64.js&quot;&gt;&lt;/script&gt; &lt;br /&gt;&lt;h4&gt;DataLoader-Klasse&lt;/h4&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;DataLoader&lt;/span&gt;&amp;nbsp;ist eine abstrakte Klasse, die die Map &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;data&lt;/span&gt;&amp;nbsp;enthält, in der alle erstellten Instanzen gespeichert werden. Mit der Funktion &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;loadData&lt;/span&gt;Class&lt;/span&gt; ist es möglich, alle Daten, die in der übergebenen Klasse definiert sind, zu erstellen und speichern. Tritt ein Fehler in einer der Daten Instanzen auf, wird eine &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Exception&lt;/span&gt; geworfen. Zuerst wird ein Objekt der übergebenen Klasse erstellt und die &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;preData&lt;/span&gt;&amp;nbsp;sowie &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;data&lt;/span&gt;&amp;nbsp;ausgelesen. Sind &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;preData&lt;/span&gt;&amp;nbsp;vorhanden, werden diese vor den Hauptdaten erstellt, anschließend gespeichert und die &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;preData&lt;/span&gt;&amp;nbsp;auf Persistenz geprüft. Zum Schluss wird die Funktion &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;post&lt;/span&gt;&amp;nbsp;ausgeführt. Im folgenden Sequenzdiagramm ist der Ablauf bildlich darstellt:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://4.bp.blogspot.com/-4ELZ4Bq8c0w/V8aGHNQ1vsI/AAAAAAAAACg/mpkOxuVzG3AvRB5yngC-0H3rkFqmmBeDgCLcB/s1600/Unbenannt.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://4.bp.blogspot.com/-4ELZ4Bq8c0w/V8aGHNQ1vsI/AAAAAAAAACg/mpkOxuVzG3AvRB5yngC-0H3rkFqmmBeDgCLcB/s320/Unbenannt.PNG&quot; width=&quot;244&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Eine Exception wird erzeugt, wenn&lt;br /&gt;&lt;ol&gt;&lt;li&gt;ein Name zur Referenzierung der Daten doppelt verwendet wird&lt;/li&gt;&lt;li&gt;keine Beziehung (&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;null&lt;/span&gt;) hergestellt werden kann&lt;/li&gt;&lt;li&gt;das Speichern einer Instanz nicht möglich ist&lt;/li&gt;&lt;li&gt;ein &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;preData&lt;/span&gt;-Aufruf nicht gespeichert wurde&lt;/li&gt;&lt;/ol&gt;Der folgende Code- Block enthält einen ersten Entwurf der Logik, wie das Erstellen der Daten realisiert werden kann.&lt;/div&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/8f438a24d7ffcfc82d339c33ce396bbe.js&quot;&gt;&lt;/script&gt; &lt;br /&gt;&lt;h4&gt;Fazit&lt;/h4&gt;Diesd Implementierung funktioniert in dem migrierten Projekt sehr gut, wenngleich noch einige Verbesserungen möglich sind. Zum einen könnte die Performance verbessert werden und zum anderen könnten die Implementierung dahingehend verbessert werden, dass eine Fehlverwendung vermieden wird.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/6958112712635532517/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/09/migration-von-grails-137-nach-319_27.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/6958112712635532517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/6958112712635532517'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/09/migration-von-grails-137-nach-319_27.html' title='Migration von Grails 1.3.7 nach 3.1.9 - Erstellen von Testdaten - Teil 2'/><author><name>Sebastian Jäger</name><uri>https://plus.google.com/114000193552020861532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-yY0lpW8_nzw/AAAAAAAAAAI/AAAAAAAAABE/ueiT5IHPLFY/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://4.bp.blogspot.com/-4ELZ4Bq8c0w/V8aGHNQ1vsI/AAAAAAAAACg/mpkOxuVzG3AvRB5yngC-0H3rkFqmmBeDgCLcB/s72-c/Unbenannt.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-5597955995855683656</id><published>2016-09-20T06:00:00.000+02:00</published><updated>2016-09-20T10:32:35.412+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy Migration"/><title type='text'>Migration von Grails 1.3.7 nach 3.1.9 - Probleme und deren Behebung - Teil 1</title><content type='html'>In den letzten Monaten hat es zu meinen Hauptaufgaben gezählt, eine große Business-Applikation von Grails 1.3.7 auf die Version 3 zu migrieren. In diesem Blog Post will ich auf allgemeine Schwierigkeiten und Lösungen eingehen, die in unserem Fall aufgetreten sind.&lt;br /&gt;In den folgenden Posts werde ich zum einen auf die selbst entwickelte Lösung zum Erstellen von Testdaten eingehen und zum anderen die Probleme bei der Migration der einzelnen Testarten erläutern.&lt;br /&gt;&lt;br /&gt;Für die Migration wurde in folgender Reihenfolge vorgegangen:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Neues Projekt mit der gewünschten Grails Version erstellen und konfigurieren&lt;/li&gt;&lt;li&gt;Einbinden aller Plugins in der neusten verfügbaren Version, die verwendet werden sollen&lt;/li&gt;&lt;li&gt;Migrieren der Domain-/ Java-/ Groovy-Klassen, sowie TagLibs und i18n- Ressourcen&lt;/li&gt;&lt;li&gt;Einspielen von Testdaten&lt;/li&gt;&lt;li&gt;Migrieren der Services&lt;/li&gt;&lt;li&gt;Migrieren der Controller&lt;/li&gt;&lt;li&gt;Migrieren der Views und alle zugehörigen Ressourcen wie JavaScript und CSS Dateien&lt;/li&gt;&lt;li&gt;Migrieren der Testdateien&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Nach jedem Schritt wurden automatisierte Tests, die die schon migrierten Teile der Applikation testen, ebenfalls migriert, um möglichst früh Fehler zu erkennen. Wie oben beschrieben werde ich im dritten Teil dieser Serie genauer darauf eingehen.&lt;/div&gt;&lt;div&gt;&lt;h4&gt;Hibernate naming_strategy&lt;/h4&gt;&lt;/div&gt;Nachdem die Domainklassen sowie sämtliche Hilfsklassen migriert waren, ist durch das Starten mit einer bestehenden Datenbank, aufgefallen, dass das automatisch erstellte Datenbankschema nach dem Starten der Applikation sich von dem Ursprünglichen hinsichtlich der Tabellennamen unterscheidet. Das Anpassen der &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;hibernate.naming_strategy&lt;/span&gt; hat für mich das Problem gelöst. In der&amp;nbsp;Hibernate-Dokumentation für das Interface&amp;nbsp;&lt;a href=&quot;https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/cfg/NamingStrategy.html&quot; target=&quot;_blank&quot;&gt;NamingStrategy&lt;/a&gt;&amp;nbsp;können implementierte Strategien gefunden werden, die Hibernate von Haus aus mit bringt. Das Definieren eigener Strategien ist jedoch ebenso möglich. &lt;a href=&quot;http://docs.grails.org/3.1.9/guide/single.html#customNamingStrategy&quot; target=&quot;_blank&quot;&gt;[1]&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Actions von Controllern&lt;/h4&gt;Treten bei Controllern Fehler auf, dass Annotationen nicht erlaubt seien, liegt das daran, dass Actions seit Grails 2 als Methoden implementiert werden sollen. Bei Applikationen der Version 1.x war es üblich diese als Closures zu programmieren. &lt;a href=&quot;http://docs.grails.org/2.0.0/guide/introduction.html#webFeatures&quot; target=&quot;_blank&quot;&gt;[2]&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Transaktionen in Services&lt;/h4&gt;Transaktionen ist der wesentliche Punkt, der sich für Services verändert hat. Bis zur Version 2.3 von Grails wurden die Spring Transaktionen verwendet, die durch das statische Attribut &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;transactional&lt;/span&gt; aktiviert oder deaktiviert wurden. Empfohlen wird, diese komplett durch die seither neuen, Grails Transaktionen zu ersetzen, die über Annotationen verwendet werden. Alle Services ohne jegliche Annotationen benutzen standardmäßig Transaktionen.&lt;br /&gt;&lt;div&gt;Vorsicht ist geboten, wenn der Modus einzelner Methoden vom default Wert &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;read-write&lt;/span&gt;&amp;nbsp;&lt;/span&gt;beispielsweise zu &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;readOnly&lt;/span&gt;&amp;nbsp;geändert werden soll, denn dann muss die Service Klasse ebenfalls als &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@Transactional&lt;/span&gt;&amp;nbsp;gekennzeichnet werden. Sind nur einzelne Methoden und nicht die Klasse gekennzeichnet, werden nur für diese Methoden Transaktionen verwendet, alle anderen jedoch nicht. &lt;a href=&quot;http://docs.grails.org/3.1.9/guide/services.html#declarativeTransactions&quot; target=&quot;_blank&quot;&gt;[3]&lt;/a&gt;&lt;/div&gt;&lt;h4&gt;Resource- Plugin durch Asset- Pipeline ersetzen&lt;/h4&gt;Seit der Version 2.0 von Grails wurde das bisher verwendete &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;resource&lt;/span&gt;&amp;nbsp;Plugin durch &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;asset-pipeline&lt;/span&gt;&amp;nbsp;ersetzt. Dadurch muss das Einbinden der JavaScript und CSS Dateien angepasst werden. In der Datei &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;UIResources.groovy&lt;/span&gt;&amp;nbsp;wurden bisher sogenannte Module definiert. Mit dem Tag &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;lt;r:require modules=“common”/&amp;gt;&lt;/span&gt;&amp;nbsp;wurden diese Dateien in dem entsprechenden View eingebettet. Um ein ähnliches Verhalten nachzubilden, habe ich für jedes Modul eine neue JavaScript und eine CSS Datei erstellt, die die zugehörigen Dateien definiert. In den folgenden Ausschnitten ist dies beispielhaft dargestellt.&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/a865c546865f92091152200389e8e8bb.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;https://gist.github.com/exblog/c185388cfff1016b7da3172535c72572.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;Die verwendeten Pfade der Ressourcen sind relativ, ausgehend von der Dateistruktur&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;grails-app/assets/javascript/&lt;/span&gt;&amp;nbsp;oder &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;grails-app/assets/stylesheets/&lt;/span&gt;. Wichtig ist hierbei auf die Reihenfolge zu achten, denn ansonsten könnte es vorkommen, dass JavaScript eine Funktion verwenden will, die noch nicht definiert wurde, was zu Fehlern führt. Um die neu erstellten &quot;Module&quot; zu nutzen, werden nun zwei Aufrufe benötigt: &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;lt;asset:javascript src=“commonModule.js”/&amp;gt;&lt;/span&gt;&amp;nbsp;und &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;lt;asset:stylesheet src=“commonModule.css”/&amp;gt;&lt;/span&gt;.&lt;br /&gt;&lt;h4&gt;Deploying&lt;/h4&gt;Abschließend gehe ich noch auf ein Problem ein, das schwer zu entdecken ist. Ein, mit Hilfe des Gradle Task &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;bootRepackag&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;e&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;erstelltes&lt;/span&gt;&amp;nbsp;Standalone War-File, hat zufällig, entweder unter Windows oder, was öfter auftrat, unter Linux, anstelle der erwarteten Login Seite eine Liste aller Controller angezeigt. Verursacht wurde dieses Problem dadurch, dass in unserer Applikation das URL- Mapping, speziell der Root- Endpunkt (&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;“/“&lt;/span&gt;), angepasst wurde. Dadurch kann es auftreten, dass ein Plugin, das auch eine &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;UrlMapping.groovy&lt;/span&gt; Datei besitzt, die eigene Änderung überschreibt. Hier war noch der Standardwert enthalten, der auf eine fehlerhafte View verwiesen hat. Mit einem Workaround lässt sich dies umgehen. Dazu wird ein Interceptor implementiert, der aktiv werden soll, wenn eine Anfrage an &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;“/“ &lt;/span&gt;gesendet wird und dann manuell auf den gewollten Controller und dessen action umleitet. Hier ist ein Beispiel, das eine mögliche Implementierung des Interceptors darstellt.&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/673492f66c59458dcedb1e8a0f11ce44.js&quot;&gt;&lt;/script&gt; &lt;br /&gt;&lt;div&gt;&lt;h4&gt;Fazit&lt;/h4&gt;&lt;/div&gt;&lt;div&gt;Es wurden einige Probleme und deren Möglichkeiten zur Behebung bei einer Grails-Migration vorgestellt. Eine Migration von älteren Versionen ist also nicht unmöglich, aber auch nicht an allen Stellen einfach. Oft sind es nur Kleinigkeiten, die sich geändert haben und meist durch eine Anpassung der Konfigurationen behoben werden können. Problematisch ist eher das Finden der Ursache zu den auftretenden Fehlern.&lt;br /&gt;&lt;h4&gt;Links&lt;/h4&gt;[1]&amp;nbsp;&lt;a href=&quot;http://docs.grails.org/3.1.9/guide/single.html#customNamingStrategy&quot; target=&quot;_blank&quot;&gt;Hibernate Naming Strategy ändern - Grails 3.1.9 Guide&lt;/a&gt;&lt;br /&gt;[2]&amp;nbsp;&lt;a href=&quot;http://docs.grails.org/2.0.0/guide/introduction.html#webFeatures&quot; target=&quot;_blank&quot;&gt;Änderungen seit Grails 2.0&lt;/a&gt;&lt;br /&gt;[3]&amp;nbsp;&lt;a href=&quot;http://docs.grails.org/3.1.9/guide/services.html#declarativeTransactions&quot; target=&quot;_blank&quot;&gt;Transaktionen in Grails 3.1.9&lt;/a&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/5597955995855683656/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/09/migration-von-grails-137-nach-319.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/5597955995855683656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/5597955995855683656'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/09/migration-von-grails-137-nach-319.html' title='Migration von Grails 1.3.7 nach 3.1.9 - Probleme und deren Behebung - Teil 1'/><author><name>Sebastian Jäger</name><uri>https://plus.google.com/114000193552020861532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-yY0lpW8_nzw/AAAAAAAAAAI/AAAAAAAAABE/ueiT5IHPLFY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-301040322319877315</id><published>2016-07-01T11:02:00.000+02:00</published><updated>2016-08-26T10:48:34.962+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="Lizenzen"/><category scheme="http://www.blogger.com/atom/ns#" term="Open-Source"/><category scheme="http://www.blogger.com/atom/ns#" term="ScanCode toolkit"/><title type='text'>Lizenzanalyse mit dem Open Source Tool ScanCode toolkit</title><content type='html'>In diesem Blogpost möchte ich auf das Tool &lt;a href=&quot;https://github.com/nexB/scancode-toolkit&quot; target=&quot;_blank&quot;&gt;ScanCode toolkit&lt;/a&gt; eingehen und zeigen wie man damit überprüfen kann welche Lizenzen die abhängigen Jar-Dateien eines Java-Projektes verwenden. Diese Information kann relevant sein, wenn man prüfen will ob daraus bspw. rechtliche Probleme resultieren können. Im &lt;a href=&quot;https://www.bitkom.org/Publikationen/2016/Leitfaden/Open-Source-Software-20/Bitkom-Leitfaden-Open-Source-Software-20.pdf&quot; target=&quot;_blank&quot;&gt;Bitkom-Leitfaden zu Open-Source-Software 2.0&lt;/a&gt; wird diese Thematik aufgegriffen und behandelt.&lt;br /&gt;Für dieses Beispiel wurde eine Grails- Applikation zur Hand genommen, welche etwas mehr als 150 externe Jar- Dateien einbindet, von denen die Lizenzen ermittelt werden sollen.&lt;br /&gt;&lt;br /&gt;Damit ScanCode toolkit Dateien überprüfen kann, müssen sie in entpackter Form vorliegen. Die Funktion zum Entpacken wird von Haus aus mitgeliefert, jedoch muss das Entpacken manuell gestartet werden. Wechselt man mit einem Terminal in das Verzeichnis des Tools kann mit folgendem Befehl das rekursive Entpacken aller Dateien in dem Zielverzeichnis gestartet werden:&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/b5e5aa3446735321fb02663808b4c583.js&quot;&gt;&lt;/script&gt; Der Pfad zum Zielverzeichnis ist relativ. Alle entpackten Dateien werden in demselben Verzeichnis mit dem Postfix &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;i&gt;„-extract“&lt;/i&gt;&lt;/span&gt; gespeichert. Liegen alle Dateien entpackt vor, kann man den Scan starten. &lt;br /&gt;Dies geschieht mit dem folgenden Befehl:&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/exblog/a4b74070872b388012386403d1f597d4.js&quot;&gt;&lt;/script&gt; Auch hier werden relative Pfade erwartet. Verwendet man die Option &lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;„-- format“&lt;/span&gt;&lt;/i&gt;kann die Art der Ausgabe bestimmt werden, dazu später mehr.&lt;br /&gt;&lt;br /&gt;Die Funktionsweise von ScanCode toolkit ist einfach, es durchsucht alle Dateien, in dem Zielverzeichnis, gegen einen bestehenden und erweiterbaren Datensatz von Lizenzen. Nach dem gleichen Prinzip ist es auch möglich Copyright- Statements zu finden, dies wurde jedoch nicht getestet und wird hier nicht weiter erläutert. &lt;br /&gt;Der Nachteil hierbei ist, dass dadurch Jar- Dateien mehrfach aufgelistet werden. Dies hat mehrere Gründe, zum einen kann es sein, dass diese unter einem dualen Lizenzsystem angeboten werden. Das bedeutet es werden mehrere Lizenzen aufgelistet, unter welcher die Software verwendet wird bleibt dem Benutzer überlassen. Zum anderen ist es möglich, dass ein Plugin seinerseits weiteren Code einbindet und dessen Lizenz, beispielsweise in einem Kommentar deutlich macht. In extremen Fällen verwendet auch dieser ein duales Lizenzsystem.&lt;br /&gt;&lt;br /&gt;Man hat die Wahl zwischen 3 verschiedenen Visualisierungsmöglichkeiten. Die Ausgabe als JSON- Datei, einer statisch HTML- Seite, welche eine Tabelle der Daten enthält oder eine HTML- Applikation die das Filtern, Sortieren und Navigieren über die Verzeichnisstruktur erlaubt.&lt;br /&gt;&lt;br /&gt;In Abbildung 1 ist eine beispielhafte Darstellung eines Scanergebnisses  als HTML- App dargestellt. Navigiert man im linken Bereich durch die  Verzeichnisstruktur werden die visualisierten Daten im rechten Bereich  entsprechend angepasst. Sind in dieser Gesamtansicht nun unerwünschte  Lizenzen aufgelistet wird die Stärke dieser HTML- Applikation deutlich. &lt;br /&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-62neP2kUouY/V2ztlNOgA4I/AAAAAAAAACM/nq-e7odD8UYDHKsNKwBZOAEmjM41qcaUgCK4B/s1600/Uebersicht_Ergebnis_schmal.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;340&quot; src=&quot;https://3.bp.blogspot.com/-62neP2kUouY/V2ztlNOgA4I/AAAAAAAAACM/nq-e7odD8UYDHKsNKwBZOAEmjM41qcaUgCK4B/s400/Uebersicht_Ergebnis_schmal.PNG&quot; title=&quot;Abbildung 1&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Abbildung 1&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-sZpaa1cevFE/V2zX5hwb-hI/AAAAAAAAABY/rPCEBEjZf3EAT8463wX5BasLB2YtJq2EACK4B/s1600/Uebersicht_Ergebnis_schmal.PNG&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-J1I2Bx3ANTo/V2zX8xV07jI/AAAAAAAAABg/MptlSx_z3h8j19zdosAvmhdN8tEmmfYhACK4B/s1600/Uebersicht_Scan_Ergebnis.PNG&quot; imageanchor=&quot;1&quot;&gt;&lt;/a&gt;&lt;br /&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt; &lt;o:OfficeDocumentSettings&gt;  &lt;o:RelyOnVML/&gt;  &lt;o:AllowPNG/&gt; &lt;/o:OfficeDocumentSettings&gt;&lt;/xml&gt;&lt;![endif]--&gt;&lt;br /&gt;Wechselt man in den Reiter &lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;„License &amp;amp; Copyright Details“&lt;/span&gt;&lt;/i&gt;wird eine Liste angezeigt die Dateien enthält in denen Lizenzen gefunden wurden. Sortiert man diese, wie in Abbildung 2, nach der Spalte Info kann man gezielt die Namen, der Jar- Files, in der Spalte Path ablesen, in denen Stichworte gefunden wurden, die in Verbindung mit den unerwünschten Lizenzen stehen. Das Verwenden der Suchfunktion, oben rechts, kann hierbei eine Hilfe sein.&lt;br /&gt;&lt;br /&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt; &lt;o:OfficeDocumentSettings&gt;  &lt;o:RelyOnVML/&gt;  &lt;o:AllowPNG/&gt; &lt;/o:OfficeDocumentSettings&gt;&lt;/xml&gt;&lt;![endif]--&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-RUOe8Mygypo/V2zhgRHdqNI/AAAAAAAAAB8/L7a7ctxCpskuzymTPCcuCsoRTbwJss0IgCK4B/s1600/Reiter_Lizenzen_schmal.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;290&quot; src=&quot;https://1.bp.blogspot.com/-RUOe8Mygypo/V2zhgRHdqNI/AAAAAAAAAB8/L7a7ctxCpskuzymTPCcuCsoRTbwJss0IgCK4B/s400/Reiter_Lizenzen_schmal.PNG&quot; title=&quot;Abbildung 2&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Abbildung 2&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt; &lt;w:WordDocument&gt;  &lt;w:View&gt;Normal&lt;/w:View&gt;  &lt;w:Zoom&gt;0&lt;/w:Zoom&gt;  &lt;w:TrackMoves/&gt;  &lt;w:TrackFormatting/&gt;  &lt;w:HyphenationZone&gt;21&lt;/w:HyphenationZone&gt;  &lt;w:PunctuationKerning/&gt;  &lt;w:ValidateAgainstSchemas/&gt;  &lt;w:SaveIfXMLInvalid&gt;false&lt;/w:SaveIfXMLInvalid&gt;  &lt;w:IgnoreMixedContent&gt;false&lt;/w:IgnoreMixedContent&gt;  &lt;w:AlwaysShowPlaceholderText&gt;false&lt;/w:AlwaysShowPlaceholderText&gt;  &lt;w:DoNotPromoteQF/&gt;  &lt;w:LidThemeOther&gt;DE&lt;/w:LidThemeOther&gt;  &lt;w:LidThemeAsian&gt;X-NONE&lt;/w:LidThemeAsian&gt;  &lt;w:LidThemeComplexScript&gt;X-NONE&lt;/w:LidThemeComplexScript&gt;  &lt;w:Compatibility&gt;   &lt;w:BreakWrappedTables/&gt;   &lt;w:SnapToGridInCell/&gt;   &lt;w:WrapTextWithPunct/&gt;   &lt;w:UseAsianBreakRules/&gt;   &lt;w:DontGrowAutofit/&gt;   &lt;w:SplitPgBreakAndParaMark/&gt;   &lt;w:EnableOpenTypeKerning/&gt;   &lt;w:DontFlipMirrorIndents/&gt;   &lt;w:OverrideTableStyleHps/&gt;  &lt;/w:Compatibility&gt;  &lt;m:mathPr&gt;   &lt;m:mathFont m:val=&quot;Cambria Math&quot;/&gt;   &lt;m:brkBin m:val=&quot;before&quot;/&gt;   &lt;m:brkBinSub m:val=&quot;&amp;#45;-&quot;/&gt;   &lt;m:smallFrac m:val=&quot;off&quot;/&gt;   &lt;m:dispDef/&gt;   &lt;m:lMargin m:val=&quot;0&quot;/&gt;   &lt;m:rMargin m:val=&quot;0&quot;/&gt;   &lt;m:defJc m:val=&quot;centerGroup&quot;/&gt;   &lt;m:wrapIndent m:val=&quot;1440&quot;/&gt;   &lt;m:intLim m:val=&quot;subSup&quot;/&gt;   &lt;m:naryLim m:val=&quot;undOvr&quot;/&gt;  &lt;/m:mathPr&gt;&lt;/w:WordDocument&gt;&lt;/xml&gt;&lt;![endif]--&gt;&lt;!--[if gte mso 9]&gt;&lt;xml&gt; &lt;w:LatentStyles DefLockedState=&quot;false&quot; DefUnhideWhenUsed=&quot;true&quot;   DefSemiHidden=&quot;true&quot; DefQFormat=&quot;false&quot; DefPriority=&quot;99&quot;   LatentStyleCount=&quot;267&quot;&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;0&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Normal&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;heading 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 7&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 8&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;9&quot; QFormat=&quot;true&quot; Name=&quot;heading 9&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 7&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 8&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; Name=&quot;toc 9&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;35&quot; QFormat=&quot;true&quot; Name=&quot;caption&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;10&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Title&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;1&quot; Name=&quot;Default Paragraph Font&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;11&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Subtitle&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;22&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Strong&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;20&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Emphasis&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;59&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Table Grid&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; UnhideWhenUsed=&quot;false&quot; Name=&quot;Placeholder Text&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;1&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;No Spacing&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; UnhideWhenUsed=&quot;false&quot; Name=&quot;Revision&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;34&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;List Paragraph&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;29&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Quote&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;30&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Intense Quote&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3 Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid Accent 1&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3 Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid Accent 2&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3 Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid Accent 3&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3 Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid Accent 4&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3 Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid Accent 5&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;60&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Shading Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;61&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light List Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;62&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Light Grid Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;63&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 1 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;64&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Shading 2 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;65&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 1 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;66&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium List 2 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;67&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 1 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;68&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 2 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;69&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Medium Grid 3 Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;70&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Dark List Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;71&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Shading Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;72&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful List Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;73&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; Name=&quot;Colorful Grid Accent 6&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;19&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Subtle Emphasis&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;21&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Intense Emphasis&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;31&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Subtle Reference&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;32&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Intense Reference&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;33&quot; SemiHidden=&quot;false&quot;    UnhideWhenUsed=&quot;false&quot; QFormat=&quot;true&quot; Name=&quot;Book Title&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;37&quot; Name=&quot;Bibliography&quot;/&gt;  &lt;w:LsdException Locked=&quot;false&quot; Priority=&quot;39&quot; QFormat=&quot;true&quot; Name=&quot;TOC Heading&quot;/&gt; &lt;/w:LatentStyles&gt;&lt;/xml&gt;&lt;![endif]--&gt;&lt;!--[if gte mso 10]&gt;&lt;style&gt; /* Style Definitions */  table.MsoNormalTable  {mso-style-name:&quot;Normale Tabelle&quot;;  mso-tstyle-rowband-size:0;  mso-tstyle-colband-size:0;  mso-style-noshow:yes;  mso-style-priority:99;  mso-style-parent:&quot;&quot;;  mso-padding-alt:0cm 5.4pt 0cm 5.4pt;  mso-para-margin-top:0cm;  mso-para-margin-right:0cm;  mso-para-margin-bottom:10.0pt;  mso-para-margin-left:0cm;  line-height:115%;  mso-pagination:widow-orphan;  font-size:11.0pt;  font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;  mso-ascii-font-family:Calibri;  mso-ascii-theme-font:minor-latin;  mso-hansi-font-family:Calibri;  mso-hansi-theme-font:minor-latin;  mso-fareast-language:EN-US;} &lt;/style&gt;&lt;![endif]--&gt; &lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;Es ist sinnvoll die Plugins, bei denen ein unerwünschte Lizenz gefunden wurde, einzelnen auf deren Lizenz zu überprüfen. Denn, wie oben beschrieben, ist es möglich und meiner Erfahrung nach meistens der Fall, dass es unter einer Multilizenz steht oder Code einbindet der unter einer Mehrfachlizenzierung steht, was der Grund ist warum diese unerwünschte Lizenz angezeigt und in Verbindung mit der Jar- Datei  aufgelistet wird.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Fazit&lt;span style=&quot;font-weight: normal;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;Das Tool ersetzt nicht das Evaluieren der Lizenzen. Die Entscheidung unter welche Lizenz eine Jar-Datei fallen darf, oder besser unter welche sie nicht fallen darf, damit es verwendet wird, muss noch immer manuell getroffen werden. Aber um einen schnellen Überblick zu bekommen welche Lizenzen verwendet werden und ob evtl. unerwünschte vorhanden sind ist es hervorragend geeignet.&lt;/span&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;In diesem Fall sind, von den anfänglich gut 150 Jar- Dateien, sieben in Verbindung mit unerwünschten Lizenzen aufgetaucht. Nach einer weiteren Untersuchung, bspw. auf der Hersteller Homepage, konnten auch deren Lizenzen schnell überprüft und für gut befunden werden.&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/301040322319877315/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/07/lizenzanalyse-mit-dem-open-source-tool.html#comment-form' title='2 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/301040322319877315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/301040322319877315'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/07/lizenzanalyse-mit-dem-open-source-tool.html' title='Lizenzanalyse mit dem Open Source Tool ScanCode toolkit'/><author><name>Sebastian Jäger</name><uri>https://plus.google.com/114000193552020861532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-yY0lpW8_nzw/AAAAAAAAAAI/AAAAAAAAABE/ueiT5IHPLFY/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://3.bp.blogspot.com/-62neP2kUouY/V2ztlNOgA4I/AAAAAAAAACM/nq-e7odD8UYDHKsNKwBZOAEmjM41qcaUgCK4B/s72-c/Uebersicht_Ergebnis_schmal.PNG" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-1789920567893173604</id><published>2016-06-22T15:25:00.001+02:00</published><updated>2016-06-23T10:50:28.363+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="solr"/><title type='text'>HTML als HTML in Grails anzeigen</title><content type='html'>HTML als HTML in Grails anzeigen ist natürlich keine Rocket-Science. Aber da ich aktuell eine Weile suchen musste, möchte ich hier kurz mein Wissen teilen :-)&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;In Solr&amp;nbsp;Highlighting bekommt man die markierten Texte automatisch mit &amp;lt;em&amp;gt; bzw. &amp;lt;/em&amp;gt; markiert. Und eben diese wollte ich in eine GSP in Grails anzeigen.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Eigentlich sollte folgendes funktionieren:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;${doc.content}&lt;/i&gt; wird automatisch encoded, wenn in Config.groovy&amp;nbsp;&lt;i&gt;grails.views.default.codec=&quot;html&quot;&lt;/i&gt; gesetzt ist, müsste man auf &lt;i&gt;&quot;none&quot;&lt;/i&gt; setzen. Man kann dies auch auf nur einer Seite mittels&amp;nbsp;&lt;i&gt;&amp;lt;%@page defaultCodec=&quot;none&quot; %&amp;gt; &lt;/i&gt;setzen.&lt;/li&gt;&lt;li&gt;Oder man benutzt die JSP Notation,&amp;nbsp;&lt;i&gt;&amp;lt;%=doc.content%&amp;gt; &lt;/i&gt;benutzen&lt;i&gt;.&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Hat beides nicht funktioniert. In Grails 2.5.4 hat schließlich nur folgendes funktioniert:&amp;nbsp;&lt;i&gt;${raw(doc.content)}&lt;/i&gt;.&lt;/div&gt; Happy Coding!</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/1789920567893173604/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/06/html-als-html-in-grails-anzeigen.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/1789920567893173604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/1789920567893173604'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/06/html-als-html-in-grails-anzeigen.html' title='HTML als HTML in Grails anzeigen'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-4685219215683861290</id><published>2016-06-06T07:17:00.000+02:00</published><updated>2016-06-06T11:51:33.252+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Konferenz"/><title type='text'>Rückblick auf die GR8Conf in Kopenhagen</title><content type='html'>&lt;a href=&quot;https://3.bp.blogspot.com/-TqbBIkpjGjU/V1Sbvo2fb_I/AAAAAAAAAz4/e0ZYo6brXLk212sMjNxeQTaC--U4LPUYgCLcB/s1600/DSC_0111.JPG&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://3.bp.blogspot.com/-TqbBIkpjGjU/V1Sbvo2fb_I/AAAAAAAAAz4/e0ZYo6brXLk212sMjNxeQTaC--U4LPUYgCLcB/s200/DSC_0111.JPG&quot; width=&quot;98&quot; /&gt;&lt;/a&gt;Dieses Jahr durfte ich drei Tage auf der gr8conf zu Gast sein und mein Wissen hinsichtlich des Groovy- und Grails-Ökosystems erweitern. Um die Open-Source Aktivitäten rund um diese Technologien zu fördern waren wir von der Firma &lt;a href=&quot;http://www.exensio.de&quot; target=&quot;_blank&quot;&gt;exensio&lt;/a&gt; auch einer der Sponsoren dieser Konferenz.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Tag 1&lt;/h2&gt;Über den gesamten ersten Tag wurden jeweils 2 parallele Workshops angeboten. &lt;br /&gt;Ich hatte zunächst den Workshop zu gradle besucht, der viele Best Practices zum Build-System geboten hat.  &lt;br /&gt;Mein zweiter Workshop befasste sich mit Grails und AngularJS. Mit dem AngularJS-Profile wird eine Integration des Javascript-Frameworks innerhalb von Grails ermöglicht. Durch die direkte Integration können allerdings die beiden Komponenten nicht mehr einfach voneinander physikalisch getrennt werden, sprich der Javascript-Code wird auf einem anderen Server wie die Grails-Code installiert.  &lt;br /&gt;Am Abend fand der traditionelle Hackergarten statt, dessen Ziel es ist innerhalb eines Abends etwas zur Open-Source-Community beizutragen. Ich arbeitete mit anderen Teilnehmern an der Aktualisierung des Elasticsearch-Plugins für Grails. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Tag 2&lt;/h2&gt;Mit der Keynote von Ken Kousen startete der zweite Tag. Er ging insbesondere auf den aktuellen Status von Groovy ein. Die Ankündigung von gradle vor wenigen Wochen, dass zukünftig neben Groovy die Programmiersprache Kotlin unterstützt wird, sorgte für einiges an Diskussionen in der Groovy-Community. Aus seiner Sicht sollte sich Groovy davon  aber nicht beunruhigen lassen, da Groovy weiterhin von gradle unterstützt wird und außerdem in vielen anderen Systemen, wie bspw. Jenkins, verwendet wird. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://4.bp.blogspot.com/-FhFwgEo068c/V1ScLI20w6I/AAAAAAAAA0Q/IxPqha1QmBcdMtuqlKGQBMQ1KjXNZ_wRQCLcB/s1600/DSC_0119.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://4.bp.blogspot.com/-FhFwgEo068c/V1ScLI20w6I/AAAAAAAAA0Q/IxPqha1QmBcdMtuqlKGQBMQ1KjXNZ_wRQCLcB/s320/DSC_0119.JPG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://2.bp.blogspot.com/-xuztTn8-EqU/V1ScLDnVcJI/AAAAAAAAA0U/uQqNkrCizhU1YReObbAcHUb3LebqkyoSQCLcB/s1600/DSC_0132.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://2.bp.blogspot.com/-xuztTn8-EqU/V1ScLDnVcJI/AAAAAAAAA0U/uQqNkrCizhU1YReObbAcHUb3LebqkyoSQCLcB/s320/DSC_0132.JPG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Den interessantesten Vortrag an diesem Tag hielt aus meiner Sicht Michael Ploed zum Thema Reactive Applikations mit Grails 3. Er zeigte auf, wie einfach die Modularisierung von Grails-Applikationen mit Hilfe des in Grails 3 integrierten Reactor-Frameworks erfolgen kann.  &lt;br /&gt;In der Präsentation »Monitoring and Metrics with Grails 3« stellte Jeff Scott Brown das neue dropwizard-metric Plugin vor. Hiermit können zukünftig elegant Laufzeiten und die Anzahl von Methodenaufrufen gemessen werden. &lt;br /&gt;Das Thema »Dockerize you Grails App« war meine Abschluss-Session an diesem Tag. Hier wurde auf Docker Basics eingegangen und aufgezeigt, wie einfach Grails-Applikationen darauf aufsetzen können. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Tag 3&lt;/h2&gt;Am dritten Tag standen für mich zwei Sessions zum Thema Plugins auf dem Programm. Neben einem generellen Überblick im Vortrag »Mastering Grails 3 plugins« ging der zweite Vortrag auf die Migrationsmöglichkeiten von Plugins von der Version 2 auf 3 ein. &lt;br /&gt;Am spannendsten waren die Präsentationen von Graeme Rocher zur weiteren Entwicklung von Grails. Hier ist insbesondere das mit Grails 3.2 kommende Thema RxGORM, das nicht blockierende Datenbankzugriffe bietet sehr interessant.  &lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://1.bp.blogspot.com/-qyVgsbWqT_E/V1Sb_8pn0bI/AAAAAAAAA0A/6ZTxyv_lhxISYcoMPcQRwxbVKLcX0JjpQCLcB/s1600/DSC_0108.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://1.bp.blogspot.com/-qyVgsbWqT_E/V1Sb_8pn0bI/AAAAAAAAA0A/6ZTxyv_lhxISYcoMPcQRwxbVKLcX0JjpQCLcB/s320/DSC_0108.JPG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://4.bp.blogspot.com/-wwR9oYvQz58/V1ScAcpMA9I/AAAAAAAAA0I/q3a2mPp27akn5iHs7j-PlOcp0lh1-e3EwCLcB/s1600/DSC_0134.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://4.bp.blogspot.com/-wwR9oYvQz58/V1ScAcpMA9I/AAAAAAAAA0I/q3a2mPp27akn5iHs7j-PlOcp0lh1-e3EwCLcB/s320/DSC_0134.JPG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;Fazit&lt;/h2&gt;Neben vielen wissenswerten Informationen zu den verschiedenen Groovy- und Grails-Technologien ist Kopenhagen nicht nur wegen der gr8conf eine Reise wert. Die Stadt ist wunderschön und bietet im Vergleich zu den meisten Städten in Deutschland vielfältige und interessante Gebäudearchitekturen.</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/4685219215683861290/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/06/ruckblick-zur-gr8conf-in-kopenhagen.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/4685219215683861290'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/4685219215683861290'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/06/ruckblick-zur-gr8conf-in-kopenhagen.html' title='Rückblick auf die GR8Conf in Kopenhagen'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://3.bp.blogspot.com/-TqbBIkpjGjU/V1Sbvo2fb_I/AAAAAAAAAz4/e0ZYo6brXLk212sMjNxeQTaC--U4LPUYgCLcB/s72-c/DSC_0111.JPG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-2433516057854031656</id><published>2016-04-15T16:40:00.000+02:00</published><updated>2016-04-15T16:56:35.183+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Responsive Design"/><title type='text'>Latest jQuery with Grails 2 and the resources plugin</title><content type='html'>We just updated the front end of an older &lt;a href=&quot;https://grails.org/&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt; 2.1.5 application to &lt;a href=&quot;http://getbootstrap.com/&quot; target=&quot;_blank&quot;&gt;Bootstrap&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The application uses the &lt;a href=&quot;http://grails.org/plugin/resources&quot; target=&quot;_blank&quot;&gt;resources plugin&lt;/a&gt;&amp;nbsp;and due to some other dependencies the &lt;a href=&quot;https://grails.org/plugin/jquery&quot; target=&quot;_blank&quot;&gt;jQuery plugin&lt;/a&gt; is loaded. The jQuery plugin ships with jQuery 1.11.1 and was not updated since 2014. The latest jQuery version as of now is 2.2.3. So how can we update the javascript library without fiddling with dependency excludes or other hacks?&lt;br /&gt;&lt;br /&gt;The resources plugin lets you override single resource files that are loaded in a plugin. The only thing you need is the id of the resource. In this case we have the following definition in the Grails jQuery plugin: &lt;script src=&quot;https://gist.github.com/exblog/2faa1f49165a7dc483997db7fc480ff3.js&quot;&gt;&lt;/script&gt;The id for the javascript file is &#39;js&#39;. The new jQuery version was put in &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;b&gt;web-app/lib/jquery-2.2.3&lt;/b&gt;&lt;/span&gt;. With the following override in the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;b&gt;ApplicationResources.goorvy &lt;/b&gt;&lt;/span&gt;configuration the latest jQuery version is used everywhere: &lt;script src=&quot;https://gist.github.com/exblog/c6cef691ca11f152246b7b3245dda2ab.js&quot;&gt;&lt;/script&gt;This is a simple and fast way to override any javascript or css file that is provided by a Grails plugin. The &lt;a href=&quot;http://grails-plugins.github.io/grails-resources/guide/5.%20Overriding%20resources.html&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;&amp;nbsp;of the resources plugin has some more informations about overrides.</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/2433516057854031656/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/04/latest-jquery-with-grails-2-and.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2433516057854031656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2433516057854031656'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/04/latest-jquery-with-grails-2-and.html' title='Latest jQuery with Grails 2 and the resources plugin'/><author><name>Florian Mutter</name><uri>https://plus.google.com/114456387242978355770</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-uAwDR5U04Gw/AAAAAAAAAAI/AAAAAAAAAKE/LuH7qZeNzfo/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-1666826866194558616</id><published>2016-02-16T09:08:00.000+01:00</published><updated>2016-02-16T09:08:31.419+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="exensio"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="shipit-day"/><title type='text'>ShipIt Day 2016: iBeacon Tracking Server</title><content type='html'>Für unser Ship-It Day Projekt &quot;Mit iBeacons Arbeitszeit erfassen&quot; haben wir eine App für Smartphones und eine Server-Applikation entwickelt. Nachdem wir &lt;a href=&quot;http://blog.exensio.de/2016/02/shipit-day-2016-ibeacon-tracking-app.html&quot; target=&quot;_blank&quot;&gt;im ersten Artikel&lt;/a&gt; das Projekt und die mobile App vorgestellt haben, geht es in diesem Artikel um die Server-Applikation.&lt;br /&gt;&lt;br /&gt;In der Server-Applikation werden alle Daten gespeichert, aggregiert und verwaltet. Die App wurde mit&amp;nbsp;&lt;a href=&quot;https://grails.org/&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt;&amp;nbsp;3.1.1 und&amp;nbsp;&lt;a href=&quot;http://v4-alpha.getbootstrap.com/&quot; target=&quot;_blank&quot;&gt;Bootstrap 4&lt;/a&gt;&amp;nbsp;(Alpha) umgesetzt.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;h3&gt;Die Funktionalitäten&lt;/h3&gt;Auf der Startseite wird angezeigt, welche Mitarbeiter gerade im Büro sind und wann sie angekommen sind. Im unteren Teil werden die Mitarbeiter angezeigt, die nicht im Büro sind, und wann sie das Büro verlassen haben.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-2bMhxS8cTbI/Vr4AEBlCKVI/AAAAAAAAAOA/NDYH1hjijMw/s1600/iBeacon%2BTracking%2BServer%2B-%2BStartbildschirm.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;255&quot; src=&quot;https://3.bp.blogspot.com/-2bMhxS8cTbI/Vr4AEBlCKVI/AAAAAAAAAOA/NDYH1hjijMw/s400/iBeacon%2BTracking%2BServer%2B-%2BStartbildschirm.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;Für jeden Mitarbeiter gibt es eine Detail-Ansicht, wo die Arbeitszeit für die letzten Tage, Wochen usw. angezeigt werden. Es wird auch eine Liste mit den einzelnen Events angezeigt, wie sie von den Smartphones geschickt wurden.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-4LHxAbkdNjI/Vr4AlaQAVzI/AAAAAAAAAOI/ysNA3_pS8zw/s1600/iBeacon%2BTracking%2BServer%2B-%2BDetails.png&quot; imageanchor=&quot;1&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-m7AtcIOBWW8/Vr4Am6OhwBI/AAAAAAAAAOQ/_o_RkF25sPA/s1600/iBeacon%2BTracking%2BServer%2B-%2BDetails.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;127&quot; src=&quot;https://1.bp.blogspot.com/-m7AtcIOBWW8/Vr4Am6OhwBI/AAAAAAAAAOQ/_o_RkF25sPA/s200/iBeacon%2BTracking%2BServer%2B-%2BDetails.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-DsH6eIWnMXQ/Vr4ApIpiyiI/AAAAAAAAAOY/0VKXbwPBHuc/s1600/iBeacon%2BTracking%2BServer%2B-%2BDetails%2B%25232.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;127&quot; src=&quot;https://3.bp.blogspot.com/-DsH6eIWnMXQ/Vr4ApIpiyiI/AAAAAAAAAOY/0VKXbwPBHuc/s200/iBeacon%2BTracking%2BServer%2B-%2BDetails%2B%25232.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;Außerdem können neue Mitarbeiter angelegt, die wichtigsten Mitarbeiter-Daten bearbeitet und Geräte einem Mitarbeiter zugeordnet werden. Sendet ein neues Gerät Daten an die Applikation, taucht es automatisch in der Übersicht auf und kann einem&amp;nbsp;Mitarbeiter&amp;nbsp;zugeordnet werden.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-tjPpO4c9IUs/Vr4BWL4zECI/AAAAAAAAAOk/NdM_5OYEbn4/s1600/iBeacon%2BTracking%2BServer%2B-%2BZuordnung.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;255&quot; src=&quot;https://4.bp.blogspot.com/-tjPpO4c9IUs/Vr4BWL4zECI/AAAAAAAAAOk/NdM_5OYEbn4/s400/iBeacon%2BTracking%2BServer%2B-%2BZuordnung.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;h3&gt;REST-API&lt;/h3&gt;Die Daten werden über eine REST-Schnittstelle von der App entgegen genommen. Über diese Schnittstelle kann die App ebenfalls die&amp;nbsp;aggregierten Daten für einen Mitarbeiter abrufen. Außerdem ist eine Schnittstelle für unser zweites ShipIt-Day Projekt, das Dashboard, vorhanden. So könnte auch das Dashboard die anwesenden Mitarbeiter anzeigen.&lt;br /&gt;&lt;br /&gt;Durch Grails 3 kamen diese Schnittstellen praktisch umsonst bei der Entwicklung mit. Grails erkennt automatisch, ob der Client HTML oder JSON empfangen will, und sendet selbständig die richtige Antwort. Eine Funktion, die alle anwesenden und abwesenden Mitarbeiter in zwei Listen zurück gibt sieht z.B. so aus:&lt;br /&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;    def index() {&lt;br /&gt;        List&amp;lt;User&amp;gt; usersInOffice = User.findAllByStatus(UserStatus.IN_OFFICE)&lt;br /&gt;        List&amp;lt;User&amp;gt; usersOutOfOffice = User.findAllByStatus(UserStatus.OUT_OF_OFFICE)&lt;br /&gt;&lt;br /&gt;        def result = [usersInOffice: usersInOffice, usersOutOfOffice: usersOutOfOffice]&lt;br /&gt;&lt;br /&gt;        respond result&lt;br /&gt;    }&lt;/pre&gt;Auch die Integration von Bootstrap gelingt auf Anhieb und macht eine solche Anwendung ohne großen Aufwand gleich um einiges hübscher.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3&gt;Weiter-Entwicklung&lt;/h3&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Für einen produktiven Einsatz fehlen noch einige Komponenten, wie z.B. eine Authentifizierung und die sichere Übertragung der Daten. Diese müssten noch nachgerüstet werden.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Als Erweiterungen könnten wir uns noch eine Verwaltung für die iBeacons vorstellen. Die Applikation könnte mit mehr Informationen über die iBeacons auch feststellen, in welchem Raum sich welcher Mitarbeiter aufhält. So könnte dann auf dem Dashboard immer angezeigt werden, wenn die heiß begehrte &quot;Telefonzelle&quot; besetzt ist.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Fazit&lt;/h3&gt;&lt;div&gt;Der ShipIt Day hat viel Spaß gemacht und für das eine oder andere Gelächter gesorgt. Die Projekte sind für zwei Tage Entwicklung ganz gut gelungen und wir freuen uns schon auf den nächsten ShipIt Day.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/1666826866194558616/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2016/02/shipit-day-2016-ibeacon-tracking-server.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/1666826866194558616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/1666826866194558616'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2016/02/shipit-day-2016-ibeacon-tracking-server.html' title='ShipIt Day 2016: iBeacon Tracking Server'/><author><name>Florian Mutter</name><uri>https://plus.google.com/114456387242978355770</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-uAwDR5U04Gw/AAAAAAAAAAI/AAAAAAAAAKE/LuH7qZeNzfo/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://3.bp.blogspot.com/-2bMhxS8cTbI/Vr4AEBlCKVI/AAAAAAAAAOA/NDYH1hjijMw/s72-c/iBeacon%2BTracking%2BServer%2B-%2BStartbildschirm.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-2425554143627637294</id><published>2015-11-30T14:28:00.002+01:00</published><updated>2015-11-30T14:29:44.601+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Geb"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Spock"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><title type='text'>File Upload bei funktionalen Tests in Grails</title><content type='html'>In diesem kurzen Blog-Post möchte ich zeigen, wie man bei Grails bei funktionalen Tests einen File Upload überprüfen kann. Laut Geb Dokumentation [1] funktioniert das folgendermaßen:&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;&lt;br /&gt;        when: &quot;lorem ipsum &quot;&lt;br /&gt;            ...&lt;br /&gt;            assert uploadedIconFile1.exists()&lt;br /&gt;            form.productIcon = uploadedIconFile1.absolutePath&lt;br /&gt;            report &#39;lorem ipsum&#39;&lt;br /&gt;            saveButton()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Für die Tests bietet es sich an, bereits mit dem Asset-Pipeline-Plug-in verwaltete Bilder zu benutzen. Die Dokumentation hierzu ist jedoch recht dürftig.&lt;br /&gt;&lt;br /&gt;Dieser Zugriff kann so erfolgen:&lt;br /&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;&lt;br /&gt;class ProductSpec extends GebServiceSpec {&lt;br /&gt;    ...  &lt;br /&gt;    @Shared&lt;br /&gt;    File uploadedIconFile1 = new File(AssetHelper.fileForFullName(&#39;layer1.png&#39;).getInputStream().properties[&#39;inIfOpen&#39;].path as String)&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;[1] &lt;a href=&quot;http://www.gebish.org/manual/current/#file-upload&quot;&gt;http://www.gebish.org/manual/current/#file-upload&lt;/a&gt;&lt;br /&gt;[2] &lt;a href=&quot;https://grails.org/plugin/asset-pipeline&quot;&gt;https://grails.org/plugin/asset-pipeline&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/2425554143627637294/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2015/11/in-diesem-kurzen-blog-post-mochte-ich.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2425554143627637294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2425554143627637294'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2015/11/in-diesem-kurzen-blog-post-mochte-ich.html' title='File Upload bei funktionalen Tests in Grails'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-2437039871273310740</id><published>2015-06-07T17:20:00.000+02:00</published><updated>2015-06-07T17:28:31.405+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ElasticSearch"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><title type='text'>Wie frage ich Elasticsearch mit der Elastic Groovy API in Grails ab?</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/--MsKzeWm4ig/VXRf3gnIwJI/AAAAAAAAARU/8np_5cR8CPI/s1600/exensio_groovy_grails_elasticsearch.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/--MsKzeWm4ig/VXRf3gnIwJI/AAAAAAAAARU/8np_5cR8CPI/s1600/exensio_groovy_grails_elasticsearch.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Es gibt verschiedene Möglichkeiten, Elasticsearch in Grails zu integrieren. Am Besten finde ich die Groovy API von Elasticsearch. Das Ganze wird über folgende Zeilen in der BuildConfig.groovy konfiguriert.&lt;br /&gt;&lt;br /&gt;com.spatial4j und com.vividsolutions werden für die Geo Location und Search Funktionalität von Elasticsearch benötigt. &lt;br /&gt;&lt;br /&gt; &lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;dependencies {&lt;br /&gt;       ...&lt;br /&gt;        compile group: &#39;org.elasticsearch&#39;, name: &#39;elasticsearch-groovy&#39;, version: &#39;1.5.0&#39;, classifier: &#39;grails&#39;&lt;br /&gt;        compile group: &#39;com.spatial4j&#39;, name: &#39;spatial4j&#39; , version : &#39;0.4.1&#39;&lt;br /&gt;        compile group: &#39;com.vividsolutions&#39;, name: &#39;jts&#39; , version : &#39;1.13&#39;, excludes: &#39;xerces&#39;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Und folgendes Beispiel zeigt eine Grails Service Methode, die Elasticsearch abfrägt. Hierbei sieht man m.E. sehr schön, wie elegant man die Queries in Groovy definieren und später die Response verarbeiten kann. &lt;br /&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;package competition&lt;br /&gt;&lt;br /&gt;import grails.transaction.Transactional&lt;br /&gt;import org.elasticsearch.action.search.SearchRequestBuilder&lt;br /&gt;import org.elasticsearch.action.search.SearchResponse&lt;br /&gt;import org.elasticsearch.client.Client&lt;br /&gt;import org.elasticsearch.client.transport.TransportClient&lt;br /&gt;import org.elasticsearch.common.settings.ImmutableSettings&lt;br /&gt;import org.elasticsearch.common.settings.Settings&lt;br /&gt;import org.elasticsearch.common.transport.InetSocketTransportAddress&lt;br /&gt;import org.elasticsearch.index.query.QueryBuilders&lt;br /&gt;import org.elasticsearch.search.aggregations.AggregationBuilders&lt;br /&gt;&lt;br /&gt;@Transactional&lt;br /&gt;class ElasticGroovyClientInGrailsService {&lt;br /&gt;    static transactional = false&lt;br /&gt;&lt;br /&gt;    def query() {&lt;br /&gt;&lt;br /&gt;        Client client = getESClient()&lt;br /&gt;        SearchRequestBuilder requestBuilder = client.prepareSearch(&quot;weinsuche&quot;)&lt;br /&gt;&lt;br /&gt;        requestBuilder&lt;br /&gt;                .setQuery(QueryBuilders.matchAllQuery())&lt;br /&gt;                .addFields(&quot;shopname&quot;, &quot;shopurl&quot;, &quot;kategorie&quot;, &quot;herkunftsland&quot;, &quot;anbauregion&quot;, &quot;weingut&quot;, &quot;preiseinheit&quot;)&lt;br /&gt;                .setSize(15)&lt;br /&gt;                .addAggregation(AggregationBuilders.terms(&quot;Händler&quot;).field(&quot;shopurl&quot;))&lt;br /&gt;                .addAggregation(AggregationBuilders.terms(&quot;Kategorie&quot;).field(&quot;kategorie_raw&quot;))&lt;br /&gt;                .addAggregation(AggregationBuilders.terms(&quot;Land&quot;).field(&quot;herkunftsland_raw&quot;))&lt;br /&gt;                .addAggregation(AggregationBuilders.terms(&quot;Anbauregion&quot;).field(&quot;anbauregion_raw&quot;))&lt;br /&gt;                .addAggregation(AggregationBuilders.terms(&quot;Weingut&quot;).field(&quot;weingut_raw&quot;))&lt;br /&gt;                .addAggregation(AggregationBuilders.range(&quot;Preis&quot;).field(&quot;preiseinheit&quot;)&lt;br /&gt;                .addUnboundedTo(10.0f)&lt;br /&gt;                .addRange(10.0f, 15.0f)&lt;br /&gt;                .addRange(15.0f, 20.0f)&lt;br /&gt;                .addRange(20.0f, 30.0f)&lt;br /&gt;                .addRange(30.0f, 50.0f)&lt;br /&gt;                .addRange(50.0f, 100.0f)&lt;br /&gt;                .addUnboundedFrom(100.0f)&lt;br /&gt;        )&lt;br /&gt;&lt;br /&gt;        SearchResponse response = requestBuilder.execute().actionGet()&lt;br /&gt;        printResponse(response)&lt;br /&gt;&lt;br /&gt;        client.close()&lt;br /&gt;&lt;br /&gt;        return response&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private printResponse(SearchResponse response) {&lt;br /&gt;        response.getHits().getHits().each { searchHit -&amp;gt;&lt;br /&gt;            println searchHit.fields.shopname?.value&lt;br /&gt;            println searchHit.fields.shopurl?.value&lt;br /&gt;            println searchHit.fields.kategorie?.value&lt;br /&gt;            println searchHit.fields.herkunftsland?.value&lt;br /&gt;            println searchHit.fields.anbauregion?.value&lt;br /&gt;            println searchHit.fields.weingut?.value&lt;br /&gt;            println searchHit.fields.preiseinheit?.value&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private getESClient() {&lt;br /&gt;        Client retVal&lt;br /&gt;        // clustername is defined in the elastic file elasticsearch.yml&lt;br /&gt;        Settings settings = ImmutableSettings.settingsBuilder().put(&quot;cluster.name&quot;, &quot;---ClusterName---&quot;).build()&lt;br /&gt;        TransportClient transportClient = new TransportClient(settings)&lt;br /&gt;        retVal = transportClient.addTransportAddress(new InetSocketTransportAddress(&quot;localhost&quot;, 9300))&lt;br /&gt;        return retVal&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/2437039871273310740/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2015/06/wie-frage-ich-elasticsearch-mit-der.html#comment-form' title='2 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2437039871273310740'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2437039871273310740'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2015/06/wie-frage-ich-elasticsearch-mit-der.html' title='Wie frage ich Elasticsearch mit der Elastic Groovy API in Grails ab?'/><author><name>Peter Soth</name><uri>https://plus.google.com/110836007036645861778</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/--MsKzeWm4ig/VXRf3gnIwJI/AAAAAAAAARU/8np_5cR8CPI/s72-c/exensio_groovy_grails_elasticsearch.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-8624697754066060928</id><published>2014-12-15T23:46:00.000+01:00</published><updated>2014-12-15T23:46:44.488+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><title type='text'>Eindrücke von der GGX in London</title><content type='html'>&lt;div&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-cmz2_m0KSdI/VI9jniVXyHI/AAAAAAAAAu8/8ihyuCOt130/s1600/ggx1.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-cmz2_m0KSdI/VI9jniVXyHI/AAAAAAAAAu8/8ihyuCOt130/s200/ggx1.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Vom 11. bis 12.12. konnte ich an der Groovy &amp; Grails Exchange in London teilnehmen. Im Vergleich zum Jahr 2012 hatte das Programm weniger Besucher angezogen und bei den Keynotes blieben einige Stühle leer. Dies hatte aber nichts mit der Qualität des Programm zu tun. Es waren fast alle Core-Entwickler von Groovy und Grails mit Vorträgen präsent. &lt;br/&gt;&lt;br/&gt;Den Reigen eröffnete Guillaume Laforge, der die aktuelle Groovy Version vorstellte und auf die Roadmap für die Zukunft einging. Neben Anpassungen für Java8 sind in der Version 2.3 vor allen Dingen die Themen Traits sowie die schnelle Implementierung des JSON-Parsers  erwähnenswert. &lt;br/&gt;&lt;br/&gt;Anschließend gab es über den Tag verteilt zwei parallele Session mit verschiedenen Themengebieten. Besonders interessant fand ich den Vortrag von Geb-Lead Marcin Erdmann über das Betreuen von Open-Source-Projekten. Er zeigte auf, dass ein  erfolgreiches Open-Source-Projekt viel mehr erfordert wie das reine Programmieren. Die Details können im aufgezeichneten  &lt;a href=&quot;https://skillsmatter.com/skillscasts/6064-running-an-open-source-project&quot;&gt;Video&lt;/a&gt; entnommen werden.   &lt;br/&gt;&lt;br/&gt;Bei der Panel-Diskussion am Ende des ersten Tages stellten sich die Kern-Entwickler den Fragen des Plenums. Es wurde unter anderem darüber diskutiert, warum die Frameworks Geb und Spock noch nicht in der Version 1.0 vorliegen.  Beide Frameworks sind seit Jahren stabil und können nach mehrheitlicher Meinung schon längst im Status eines Major-Releases sein. Dieser psychologische Schritt würde einen zusätzlichen Schub für Groovy und die Frameworks selbst bedeuten, da damit auch der Einsatz in Unternehmen einfacher zu vertreten sein wird. Wir dürfen gespannt sein, ob sich  in den nächsten zwei Monaten hierzu etwas tut.  &lt;br/&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-N89IrnSqmHc/VI9jnyJhuxI/AAAAAAAAAvM/A751d7enA6w/s1600/ggx2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-N89IrnSqmHc/VI9jnyJhuxI/AAAAAAAAAvM/A751d7enA6w/s320/ggx2.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br/&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-X17CPYxztiU/VI9joBha_PI/AAAAAAAAAvQ/bpfMqJp9RFk/s1600/ggx3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-X17CPYxztiU/VI9joBha_PI/AAAAAAAAAvQ/bpfMqJp9RFk/s320/ggx3.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Der zweite Tag wurde von Graeme Rocher mit seiner Keynote zu Grails 3.0 eröffnet. Vermutlich schon im Januar 2015 wird der erste Meilenstein verfügbar sein. Das neue Grails-Release wird eng mit Springboot verzahnt sein und damit auch die dort verfügbaren Features einfach einbinden können. Eine weitere große Umstellung wird das Build-System sein, das zukünftig auf Gradle basieren wird. Damit dürfte eine Integration mit Entwicklungsumgebungen deutlich einfacher sein.  &lt;br/&gt;&lt;br/&gt;Ein weiteres Highlight am zweiten Tag war das testgetriebene Vorgehen beim Entwicklen einer Springboot-Applikation mit dem Einsatz des Test-Frameworks Cucumber. Hierbei wurde innerhalb von 45 Minuten eine kleine auf Springboot basierende  Anwendung entwickelt. &lt;br/&gt;&lt;br/&gt;Ich konnte während der zwei Tage in London wieder einige noch nicht bekannte Dinge aus dem Groovy-Öko-System aufschnappen, die  sich in dem ein oder anderen Projekt sinnvoll einsetzen lassen werden. </content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/8624697754066060928/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2014/12/eindrucke-von-der-ggx-in-london.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8624697754066060928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8624697754066060928'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2014/12/eindrucke-von-der-ggx-in-london.html' title='Eindrücke von der GGX in London'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-cmz2_m0KSdI/VI9jniVXyHI/AAAAAAAAAu8/8ihyuCOt130/s72-c/ggx1.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-1037692276214974162</id><published>2014-05-19T18:58:00.001+02:00</published><updated>2014-09-18T16:07:26.538+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><title type='text'>Wie macht man eine Microsoft-Access-Datenbank mit Grails webfähig?</title><content type='html'>In einer aktuellen Kundensituation wurden wir angefragt, ob es machbar sei, eine Microsoft-Access-Datenbank mit Hilfe von &lt;a href=&quot;http://www.grails.org/&quot;&gt;Grails&lt;/a&gt; in eine Web-Applikation zu migrieren. Wir setzen seit Jahren das Grails-Framework erfolgreich ein und einigten uns mit dem Kunden darauf, dies im Rahmen eines Proof-Of-Concepts zu beweisen.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Migration von Microsft-Access nach Microsoft SQL-Server&lt;/h3&gt;Die Northwind-Access-Datenbank benutzten wir für unseren Test. Leider ist diese aktuell als .mdb-Datei äußerst schwer zu finden. Als SQL-Skript kann man sie bspw. von hier [1] heruntergeladen. Wir wurden schließlich doch noch fündig [2]. Der MDB-Viewer [3] eignet sich, um die Tabellen anzuschauen. Es ist indes besser, die MS-Access Runtime inkl. Viewer zu installieren [4]. Für den Import der MS-Acess-Db gibt es verschiedene Tools. Hierbei ist darauf zu achten, dass die Relationen (Kardinalitäten) zwischen den Tabellen mit importiert werden. Hierfür bietet sich der Microsoft SQL Server Migration Assistant for Access an. Diesen haben wir nicht mehr auf der Microsoft-Homepage gefunden, jedoch hier [5]. Der Import-Prozess ist selbsterklärend. Das Bild unten zeigt das Datenbank-Schema im SQL Server.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-iLjObJyz9Qo/VBrmKuwoohI/AAAAAAAAEFk/5P5kADrg2JM/s1600/northwind_er_modell.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-iLjObJyz9Qo/VBrmKuwoohI/AAAAAAAAEFk/5P5kADrg2JM/s1600/northwind_er_modell.png&quot; height=&quot;173&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Reverse-Engineering der Grails-Applikation&lt;/h3&gt;Die Grails Applikation wurde mit dem Grails Database Reverse Engineering Plugin von Burt Beckwith [6] migriert. Der benötigte JDBC-Treiber (sqljdbc4.jar), der in das Grails- Lib-Verzeichnis kopiert werden muss, findest sich hier [7]. Das gesamte Grails-Projekt findet sich zu Lernzwecken auf unserem Github-Repository [8]. Dort ist es möglich nachzuschauen, welche Werte in der BuildConfig.groovy, Config.groovy und DataSource.groovy gesetzt wurden.&lt;br /&gt;&lt;br /&gt;Das Bild unten zeigt das Grails-Domain-Modell. Es sieht analog zu obigem ER-Modell aus.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-3ZW5gJZnY-g/VBrma-s2z4I/AAAAAAAAEFs/j9OdpYFkMAE/s1600/northwind-grails-gorm.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-3ZW5gJZnY-g/VBrma-s2z4I/AAAAAAAAEFs/j9OdpYFkMAE/s1600/northwind-grails-gorm.png&quot; height=&quot;180&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;br /&gt;Folgendes sollte bei der Migration beachtet werden: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Leerzeichen aus Tabellennamen entfernen&lt;/li&gt;&lt;li&gt;Alle Attribute in Kleinbuchstaben wandeln&lt;/li&gt;&lt;li&gt;Umwandlung von Serializable in String&lt;/li&gt;&lt;li&gt;M:N Relationen müssen potentiell manuell aufgelöst werden&lt;/li&gt;&lt;li&gt;Die Tabellenspalten werden durch das Grails-Scaffolding zufällig ausgewählt und können später angepasst werden&lt;/li&gt;&lt;li&gt;Etwaige Business-Logic, die in MS-Access implementiert wurde kann nicht automatisch migriert werden&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fazit&amp;nbsp;&lt;/h3&gt;Das Bild unten zeigt die MS-Access-Northwind-Datenbank im Original und darunter die migrierte Grails Applikation.&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-KQofOg2DSS0/VBrm5c-WnfI/AAAAAAAAEF0/5Pek9ZRZqk0/s1600/northwind.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-KQofOg2DSS0/VBrm5c-WnfI/AAAAAAAAEF0/5Pek9ZRZqk0/s1600/northwind.png&quot; height=&quot;199&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-YESkm-UMNXU/VBrnCZG0xNI/AAAAAAAAEF8/tXs0p2TwfLU/s1600/northwind-grails.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-YESkm-UMNXU/VBrnCZG0xNI/AAAAAAAAEF8/tXs0p2TwfLU/s1600/northwind-grails.png&quot; height=&quot;271&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;Mit dem Plugin ist es ausgesprochen einfach, eine Microsoft-Access-Datenbank in eine Grails-Applikation zu wandeln. Obige Screenshots zeigen meines Erachtens sehr ausdrucksstark das gelungene Ergebnis.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Links&lt;/h3&gt;[1] &lt;a href=&quot;http://northwinddatabase.codeplex.com/%C2%A0&quot;&gt;http://northwinddatabase.codeplex.com/&amp;nbsp;&lt;/a&gt;&lt;br /&gt;[2] &lt;a href=&quot;https://courseconference.wikispaces.com/file/detail/Northwind.mdb&quot;&gt;https://courseconference.wikispaces.com/file/detail/Northwind.mdb&lt;/a&gt;&lt;br /&gt;[3] &lt;a href=&quot;http://download.cnet.com/MDB-Viewer-Plus/3000-10254_4-75285626.html&quot;&gt;http://download.cnet.com/MDB-Viewer-Plus/3000-10254_4-75285626.html&lt;/a&gt;&lt;br /&gt;[4] &lt;a href=&quot;http://www.microsoft.com/de-de/download/details.aspx?id=10910&quot;&gt;http://www.microsoft.com/de-de/download/details.aspx?id=10910&lt;/a&gt;&lt;br /&gt;[5] &lt;a href=&quot;http://microsoft-sql-server-migration-assistant.software.informer.com/&quot;&gt;http://microsoft-sql-server-migration-assistant.software.informer.com/&lt;/a&gt;&lt;br /&gt;[6] &lt;a href=&quot;http://grails-plugins.github.io/grails-db-reverse-engineer/&quot;&gt;http://grails-plugins.github.io/grails-db-reverse-engineer/&lt;/a&gt;&lt;br /&gt;[7] &lt;a href=&quot;http://www.microsoft.com/de-de/download/details.aspx?id=11774&quot;&gt;http://www.microsoft.com/de-de/download/details.aspx?id=11774&lt;/a&gt;&lt;br /&gt;[8] &lt;a href=&quot;https://github.com/exensio/northwind&quot;&gt;https://github.com/exensio/northwind&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/1037692276214974162/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2014/05/wie-macht-man-eine-microsoft-access.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/1037692276214974162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/1037692276214974162'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2014/05/wie-macht-man-eine-microsoft-access.html' title='Wie macht man eine Microsoft-Access-Datenbank mit Grails webfähig?'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-iLjObJyz9Qo/VBrmKuwoohI/AAAAAAAAEFk/5P5kADrg2JM/s72-c/northwind_er_modell.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-5743044664650888959</id><published>2014-04-07T23:15:00.002+02:00</published><updated>2014-04-07T23:15:52.128+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Enterprise Applikationen"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><title type='text'>Erster Java Saxony Day in Dresden</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-AnOHLFJ-6ZE/U0JhS_MWo6I/AAAAAAAAApY/VgmLHedCJGs/s1600/jug_JSD_Logo_transp_300px.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img style=&quot;width:120px&quot; border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-AnOHLFJ-6ZE/U0JhS_MWo6I/AAAAAAAAApY/VgmLHedCJGs/s200/jug_JSD_Logo_transp_300px.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Nach der BED-Con ging es am darauffolgenden Tag gleich weiter zum &lt;a href=&quot;http://www.jug-saxony-day.org/&quot; target=&quot;_blank&quot;&gt;Java Saxony Day&lt;/a&gt; in Dresden. Die Veranstaltung fand zum ersten Mal statt und wurde von der örtlichen Java User Gruppe organisiert. Mit 4 parallelen Tracks und einigen Ausstellern im Forum wurde viel geboten.  &lt;br /&gt;&lt;br /&gt;Mein Vortrag &quot;Grails - schneller zum Ziel - für Enterprise Applikationen&quot; war mit ca. 40-50 Personen erfreulich gut besucht. Auch wenn der Hype um Grails etwas zurückgegangen ist , zeigten mir die anschließenden Fragen, dass dieses Thema weiterhin von Interesse für die Java-Welt ist.   &lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;display: table; margin: 0 auto; width: 500px;&quot;&gt;&lt;script async=&quot;&quot; class=&quot;speakerdeck-embed&quot; data-id=&quot;bfc756b09fea013135d82a6c28d50f63&quot; data-ratio=&quot;1.41436464088398&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;&lt;/div&gt;Von den wenigen Sessions, die ich selbst hören durfte, gefielen mir besonders &quot;Java 8 Streams - How to Kick Ass with Lambdas&quot; sowie &quot;RESTful Services mit Dropwizard&quot;.  &lt;br /&gt;&lt;br /&gt;Die Veranstaltung wurde perfekt organisiert und die Resonanz von den Teilnehmern, mit denen ich sprechen konnte, fiel durchweg positiv aus.  &lt;br/&gt;Insofern wäre es schön, wenn der Java Saxony Day auch im nächsten Jahr stattfindet.</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/5743044664650888959/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2014/04/erster-java-saxony-day-in-dresden.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/5743044664650888959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/5743044664650888959'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2014/04/erster-java-saxony-day-in-dresden.html' title='Erster Java Saxony Day in Dresden'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-AnOHLFJ-6ZE/U0JhS_MWo6I/AAAAAAAAApY/VgmLHedCJGs/s72-c/jug_JSD_Logo_transp_300px.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-5182223500217476166</id><published>2014-02-06T20:12:00.001+01:00</published><updated>2014-02-06T20:14:02.588+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Enterprise Applikationen"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><title type='text'>Umsetzung von Enterprise Applikationen mit Grails (Teil 3)</title><content type='html'>Nachdem die ersten beiden Teile Grails bzw. Enterprise Applikationen separat betrachtet haben, geht der letzte Teil auf beide Seiten im Zusammenhang ein.  Aus diesem Grund werden nachfolgend einige Bereiche, die im Enterprise-Umfeld relevant sind im Zusammenhang mit &lt;a href=&quot;http://www.grails.org&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt; näher beleuchtet.  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Produktivbetrieb&lt;/h2&gt;Für die Entwicklung wird, wie bereits im &lt;a href=&quot;http://blog.exensio.de/2013/12/umsetzung-von-enterprise-applikationen.html&quot;&gt;1. Beitrag&lt;/a&gt; erwähnt, Tomcat benutzt. In größeren Unternehmen ist in der Regel der Laufzeit-Container jedoch vorgegeben und es kommen vor allem kommerzielle Server wie bspw. Websphere oder Weblogic zum Einsatz. Da für den Wirkbetrieb ein WAR-Archiv erzeugt wird, kann Grails auf sämtlichen Servlet-Engines und Applikations-Servern betrieben werden. Clustering zur Erhöhung der Ausfallsicherheit ist dank der Verteilung als WAR-Archiv mit Grails problemlos möglich. Abhängig von den Anforderungen mag Load-Balancing mit und ohne Session-Replication eingesetzt werden [1]. &lt;br/&gt;  Im Produktivbetrieb sind Monitoring-Aktivitäten unumgänglich. Zusätzlich zu den Standardmonitoring-Möglichkeiten von WAR-Archiven können von der Applikation über MBeans Informationen exportiert werden und diese mittels JMX abgefragt werden. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Konfigurationsmöglichkeiten&lt;/h2&gt;Existieren mehrere Umgebungen, wie Test-, Integrations-, Produktionsumgebung, so ist das Auslagern von Konfigurationsparametern sinnvoll. Außerdem sind damit Anpassungen ohne ein neues Deployment möglich. &lt;br/&gt;Hierfür unterstützt Grails JNDI. Hiermit können bspw. die Werte für den Datenbankzugriff oder auch des Mail-Servers aus dem Laufzeit-Container gelesen und ebenso dort angepasst werden. Für das Mail-Plugin ist diese Konfiguration, wie im untenstehenden Codeausschnitt dargestellt, denkbar einfach.  &lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;&lt;br /&gt;grails {&lt;br /&gt;     mail {&lt;br /&gt;          jndiName = &#39;TW_DEMO_MAIL_SESSION&#39;&lt;br /&gt;          disabled = false&lt;br /&gt;     }&lt;br /&gt;     mail.default.from = &#39;noReply@twitter-demo.de&#39;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; Eine weitere Alternative sind Konfigurationsdateien, die über den Klassenpfad oder ein relatives Verzeichnis eingelesen werden. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Security&lt;/h2&gt;In Unternehmen erfolgt die Benutzer- und Rollen-Verwaltung in vielen Fällen über LDAP-Server. Zum Zwecke der zentralen und einmaligen Pflege ist es sinnvoll, dass Software-Applikationen diese Services nutzen. Das Spring-Security-Plugin deckt für Grails-Anwendungen den kompletten Bereich der Authentifizierung und der Autorisierung ab.  &lt;br/&gt;Neben Datenbanken können genauso LDAP-Server angebunden werden. Wird ein Microsoft IIS der Grails-Applikation vorgeschaltet, ist eine Anbindung der passwortfreien integrierten Windows-Authentifizierung (ehemals NTLM) problemlos umsetzbar. Mit den diversen verfügbaren Aufsätzen für das Spring Security Plugin sind auch Authentifizierungsmechanismen für Twitter, Facebook und weitere andere Provider möglich. Durch die Erweiterbarkeit können gleichfalls eigene Authentifizierungs- und Autorisierungs-Mechanismen umgesetzt werden. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Integration mit Java und Drittsystemen&lt;/h2&gt;Mit der JVM als Basisplattform kann in Grails sämtlicher Java-Code ausgeführt und auch entsprechende JAR-Archive integriert werden.  Sehr interessant ist die Integration von EJBs, um mit bestehenden Java-Systemen zu kommunizieren. Im nachfolgenden ist ein Szenario für den Zugriff auf ein Dokumenten-Management-System über EJB’s dargestellt.  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-rQKTjCkkL-M/UpTSBALVKUI/AAAAAAAAAjg/9QJtWuuerYs/s1600/ejbintegration.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-rQKTjCkkL-M/UpTSBALVKUI/AAAAAAAAAjg/9QJtWuuerYs/s320/ejbintegration.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Hierbei wird in Grails via Spring-Beans der JNDI-Zugriff in weniger als 5 Zeilen konfiguriert. Das JAR-Archiv mit den Java-Interfaces ist in beiden Applikationen vorhanden. Über die Spring-Bean können anschließend die in den Schnittstellen definierten Service-Methoden genutzt werden.  &lt;br/&gt;Häufig findet man in der Industrie Messaging-Systeme zur Interkommunikation und asynchronen Abarbeitung vor.  Eine unkomplizierte Integrationsmöglichkeit für Drittsysteme bieten ebenfalls JSON und XML. Mit Hilfe von Groovy ist die Verarbeitung dieser Formate absolut einfach. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Robustheit des Systems&lt;/h2&gt;Die aus Java bekannten Exception-Mechanismen werden auch bei Grails eingesetzt und es können zentrale Fehlerseiten abhängig vom Fehler-Typ angezeigt werden. In den Services ist ein besonders feingranulares Transaktionshandling denkbar, um bedingt vom Anwendungsfall entsprechende Rollbacks zu initiieren. Es ist zu beachten, dass nur Unchecked Exceptions einen Rollback auslösen. Sind 2 Phase-Commits notwendig - falls z.B. mit Messaging und der DB innerhalb einer Transaktion gearbeitet wird, gibt es - wie in Grails üblich - wiederum ein Plugin, das eingebunden werden kann [2].  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Tool- und Fullstack-Unterstützung&lt;/h2&gt;Unumgänglich ist im Enterprise-Umfeld eine ausreichende Tool-Unterstützung. Im Bereich der Entwicklungsumgebung ist die kommerzielle Version von IntelliJ ungemein fortschrittlich und unterstützt immer die aktuellsten Grails-Versionen. Mit den weit verbreiteten Open-Source Produkten Jenkins und Hudson stehen für Continuous Integration sehr ausgereifte Werkzeuge zur Verfügung. Für die in professionellen Projekten unumgänglichen automatisierten Tests bieten Spock und Geb die ideale Basis für Grails. Über die Continuous Integration Server wird auch mit den entsprechenden Grails-Plugins die Testabdeckung [3] oder die Codequalität [4] analysiert.  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Große Code-Basis&lt;/h2&gt;Wie verhält sich Grails bei einer großen bzw. immer weiter wachsenden Code-Basis? Die bis heute von uns umgesetzten Projekte zeigten keine negativen Performance-Einbußen. Bei steigender Code-Basis verlängert sich natürlich die Zeit für Neu-Kompilierungen. Im Zusammenspiel mit Datenbanken und Datenmengen konnten bei den bisher in Produktion befindlichen Applikationen Erfahrungen mit bis zu 20 GB gesammelt werden und auch hier gab es keinerlei Anzeichen von Problemen.  &lt;br/&gt;Aus Architektur-Sicht ist es bei umfangreicheren Vorhaben sinnvoll, nicht alles in einem einzigen Grails-Projekt umzusetzen, sondern vielmehr nach fachlichen Gesichtspunkten die Anwendung zu schneiden und Teile über den Plugin-Mechanismus auszulagern.  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Fazit&lt;/h2&gt;Da die Anforderungs-Szenarien an Enterprise-Anwendungen breitgefächert und umfangreich sind, kann dieser Beitrag nur einige wesentliche Bereiche beleuchten. Es ist jedoch deutlich geworden, dass Grails auf Grund der engen Beziehung zu Java Ansprüche von Enterprise-Applikationen in vielen Fällen erfüllen kann.  &lt;br/&gt;Durch die zahlreichen Plugins können weitere Funktionalitäten abgedeckt werden oder bei Bedarf selbst mit einem eigenen Plugin implementiert werden. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Links&lt;/h2&gt;[1]&amp;nbsp;&lt;a href=&quot;http://blog.exensio.de/2012/05/teil-3-grails-in-produktion-mit-apache.html&quot; target=&quot;_blank&quot;&gt;Grails in Produktion – mit Apache, Tomcat und MySQL&lt;/a&gt;&lt;br /&gt;[2]&amp;nbsp;&lt;a href=&quot; http://grails.org/plugin/atomikos&quot; target=&quot;_blank&quot;&gt;Atomikos&lt;/a&gt;&lt;br /&gt;[3]&amp;nbsp;&lt;a href=&quot;http://grails.org/plugin/code-coverage&quot; target=&quot;_blank&quot;&gt;Test Code Coverage Plugin&lt;/a&gt;&lt;br /&gt;[4]&amp;nbsp;&lt;a href=&quot;http://grails.org/plugin/codenarc&quot; target=&quot;_blank&quot;&gt;Codenarc&lt;/a&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/5182223500217476166/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2014/02/umsetzung-von-enterprise-applikationen.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/5182223500217476166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/5182223500217476166'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2014/02/umsetzung-von-enterprise-applikationen.html' title='Umsetzung von Enterprise Applikationen mit Grails (Teil 3)'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-rQKTjCkkL-M/UpTSBALVKUI/AAAAAAAAAjg/9QJtWuuerYs/s72-c/ejbintegration.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-256468828824858903</id><published>2014-01-29T08:45:00.001+01:00</published><updated>2014-01-29T08:45:08.522+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Enterprise Applikationen"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><title type='text'>Umsetzung von Enterprise Applikationen mit Grails (Teil 2)</title><content type='html'>&lt;h2&gt;Einführung&lt;/h2&gt;Nachdem der &lt;a href=&quot;http://blog.exensio.de/2013/12/umsetzung-von-enterprise-applikationen.html&quot;&gt;1. Teil&lt;/a&gt; dieser Serie einen kurzen Überblick zu &lt;a href=&quot;http://www.grails.org/&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt; gab, wird in diesem Teil auf Anforderungen von Enterprise Applikationen eingegangen.  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-HlL_aMO0-K4/UuivtNmw-FI/AAAAAAAAAmY/1q1GA9donx0/s1600/cluster.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-HlL_aMO0-K4/UuivtNmw-FI/AAAAAAAAAmY/1q1GA9donx0/s200/cluster.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h2&gt;Definition von Enterprise Applikationen&lt;/h2&gt;Eine eindeutige Definition von Enterprise Applikationen ist in der Literatur nicht zu finden. Auch in Wikipedia [1] findet sich nur eine relativ breitgefächerte Beschreibung.  In der Regel wird unter Enterprise Applikationen Software verstanden, die bzgl. der Datenmengen, der Verarbeitungslogik, der Integration von Drittsystemen oder der Oberfläche komplex ist.  &lt;br/&gt;&lt;br/&gt;Neben den erwähnten Charakteristiken weisen Enterprise Applikationen oftmals auch die nachfolgend aufgeführten Merkmale bzw. ein Teil dieser Merkmale auf: &lt;ul&gt;  &lt;li&gt;Integration von Legacy-Systemen&lt;/li&gt;  &lt;li&gt;Integration mit anderen Geschäftsbereichen / Organisationen&lt;/li&gt;  &lt;li&gt;Zugriff auf relationale Datenbanken&lt;/li&gt;  &lt;li&gt;Distributed Computing&lt;/li&gt;  &lt;li&gt;Unterstützung von verteilten Transaktionen&lt;/li&gt;  &lt;li&gt;Datenaustausch zwischen heterogenen Systemen&lt;/li&gt;  &lt;li&gt;Einsatz von Message-Oriented Middleware&lt;/li&gt;  &lt;li&gt;Verwendung von Directory und Naming Services&lt;/li&gt;  &lt;li&gt;Inter-Kommunikation (Mail,  Dokumente, ...)&lt;/li&gt;  &lt;li&gt;Unterstützung von komplexen Security-Anforderungen&lt;/li&gt;  &lt;li&gt;Zentrale Administration und Wartung&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Nicht funktionale Anforderungen an Enterprise Applikationen&lt;/h2&gt;Für die Umsetzung von Enterprise Anforderungen müssen Applikationen in der Lage sein, eine Vielzahl von nicht funktionalen Anforderungen zu erfüllen. Nachfolgend findet sich eine Auswahl relevanter Kriterien: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Betrieb und Monitoring&lt;/li&gt;&lt;li&gt;Verfügbarkeit&lt;/li&gt;&lt;li&gt;Ausfallsicherheit&lt;/li&gt;&lt;li&gt;Wartbarkeit und Erweiterbarkeit über mehrere Jahre&lt;/li&gt;&lt;li&gt;Unterstützung komplexer Authentifizierungs- und Autorisierungs-Mechanismen&lt;/li&gt;&lt;li&gt;Kein Daten-Verlust bzw. Korruption bei Fehlern&lt;/li&gt;&lt;li&gt;Testbarkeit&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Ausblick&lt;/h2&gt;Nach dem in diesem Teil kurz auf Enterprise Applikationen im Allgemeinen eingegangen wurde, beschäftigt sich der nächste und letzte Teil dieser Serie wie solche Anforderungen mit Grails umgesetzt werden können. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Links&lt;/h2&gt;[1] &lt;a href=&quot;http://en.wikipedia.org/wiki/Enterprise_software&quot; target=&quot;_blank&quot;&gt;Definition Enterprise Software in Wikipedia&lt;/a&gt;&lt;br /&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/256468828824858903/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2014/01/umsetzung-von-enterprise-applikationen.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/256468828824858903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/256468828824858903'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2014/01/umsetzung-von-enterprise-applikationen.html' title='Umsetzung von Enterprise Applikationen mit Grails (Teil 2)'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-HlL_aMO0-K4/UuivtNmw-FI/AAAAAAAAAmY/1q1GA9donx0/s72-c/cluster.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-3534370544432722934</id><published>2013-12-18T16:17:00.000+01:00</published><updated>2013-12-19T10:36:21.377+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Batch"/><category scheme="http://www.blogger.com/atom/ns#" term="GORM"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><title type='text'>Unidirectional many-to-many relationships in Grails, and how it could affect the batch performance decisively.</title><content type='html'>Mapping business domains using GORM (Grails Object Relational Mapping) is in most cases straightforward and uncomplicated to implement.&lt;br /&gt;The documented way to map many-to-many relationships in Grails is to define a hasMany on both sides of the relationship &lt;b&gt;AND&lt;/b&gt; having a belongsTo on the owned side: [1] &lt;br /&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;class Fee {&lt;br /&gt;    static hasMany = [foos:Foo]&lt;br /&gt;    ...&lt;br /&gt;}&lt;/pre&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;class Foo {&lt;br /&gt;    static hasMany = [fees:Fee]&lt;br /&gt;    static belongsTo = Fee&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;The example given&amp;nbsp;above demonstrates a bidirectional many-to-many association between Fee and Foo. Both sides of the relationship are holding references to the other side. This might be the most common use case.&lt;br /&gt;&lt;br /&gt;What if you don&#39;t really need references from both sides, but only from one side (unidirectional)? Someone could ask - &quot;Does it make sense to have unidirectional many-to-many? The references are per default lazy loaded and therefore seems to be harmless.&quot; - Not in a particular situation.&lt;br /&gt;&lt;br /&gt;In a project that is in production since years we have batch jobs for persisting a large number of domain entities into the database. Thanks to the approaches introduced by Ted Naleid [2] the performance of the batches was fine. We are using the service method cleanUpGorm() at regular intervals to release memory usage by the hibernate session:&lt;br /&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;def cleanUpGorm() {&lt;br /&gt;    def session = sessionFactory.currentSession&lt;br /&gt;    session.flush()&lt;br /&gt;    session.clear()&lt;br /&gt;    propertyInstanceMap.get().clear()&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Consider the following situation: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;There is a many-to-many relationship between Fee and Foo&lt;/li&gt;&lt;li&gt;A Foo instance, let&#39;s say &quot;foo-1&quot; is referenced by 200,000 Fee instances&lt;/li&gt;&lt;li&gt;Batch has inserted some more Fee instances which are also referenced to &quot;foo-1&quot;&lt;/li&gt;&lt;/ul&gt;In this case, the call to session.clear() was taking minutes after the batch process has inserted 30 Fee instances. With such a performance the batch won&#39;t be finished within the time window. We used to have a much better performance and the problem must be fixed for the total changes on that branch to be released.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One of the solutions here is to have a unidirectional many-to-many relationship from Fee to Foo. The result in the giving situation was a performance boost up to 10x faster. The following example shows how to achieve it with GORM:&lt;br /&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;class Fee {&lt;br /&gt;    static hasMany = [foos:Foo]&lt;br /&gt;    ...&lt;br /&gt;}&lt;/pre&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;class Foo {&lt;br /&gt;    static hasMany = [Fee]&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Fee is the owning side and is responsable for persisting relationships. Foo is the owned side and do not have any references to Fee.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;Unidirectional many-to-many relationships is supported by hibernate [3] and Grails.&lt;br /&gt;The GORM keyword belongsTo is mandatory for bidirectional, but not for unidirectional many-to-many relationships.&lt;br /&gt;To achieve a unidirectional many-to-many you should define a &quot;hasMany&quot; without references and without belongsTo on the owned side.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Links&lt;/h3&gt;&lt;br /&gt;[1] &lt;a href=&quot;http://grails.org/doc/2.3.x/guide/GORM.html#manyToMany&quot; target=&quot;_blank&quot;&gt;http://grails.org/doc/2.3.x/guide/GORM.html#manyToMany&lt;/a&gt;&lt;br /&gt;[2]&amp;nbsp;&lt;a href=&quot;http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql/&quot; target=&quot;_blank&quot;&gt;http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql/&lt;/a&gt;&lt;br /&gt;[3]&amp;nbsp;&lt;a href=&quot;http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/associations.html#assoc-unidirectional-join-m2m&quot; target=&quot;_blank&quot;&gt;http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/associations.html#assoc-unidirectional-join-m2m&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/3534370544432722934/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/12/unidirectional-many-to-many.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/3534370544432722934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/3534370544432722934'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/12/unidirectional-many-to-many.html' title='Unidirectional many-to-many relationships in Grails, and how it could affect the batch performance decisively.'/><author><name>Xuetao Li</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-2036625302894490802</id><published>2013-12-18T12:26:00.003+01:00</published><updated>2014-09-18T16:42:57.492+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="claretportal"/><category scheme="http://www.blogger.com/atom/ns#" term="e20"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Portal"/><title type='text'>Enterprise-2.0-Portale mit Grails − geht das?</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;Unsere Kollegen Manuel Breitfeld und Tobias Kraft haben zu diesem Thema einen Artikel in der aktuellen Ausgabe 01/ 2014 der Zeitschrift JavaAktuell veröffentlicht.&lt;br /&gt;&lt;br /&gt;In dieser Veröffentlichung beschreiben sie äußerst ausführlich die Grundlagen, die die Basis unseres Claretportal-Frameworks bilden.&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://www.exensio.de/articles/images/java-aktuell-exensio-grails-e20-portal.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://www.exensio.de/articles/images/java-aktuell-exensio-grails-e20-portal.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Folgende Aspekte werden beleuchtet:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Was ist ein Portal?&lt;/li&gt;&lt;li&gt;Grails als Basis-Technologie&lt;/li&gt;&lt;li&gt;Schnelles Entwickeln mit Grails&lt;/li&gt;&lt;li&gt;Integration mit Groovy und Grails&lt;/li&gt;&lt;li&gt;Grails für Portale&lt;/li&gt;&lt;li&gt;Umsetzung von Portal-Modulen&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Tagging&lt;/li&gt;&lt;li&gt;Enterprise-Suche&lt;/li&gt;&lt;li&gt;Content/Document Management System&lt;/li&gt;&lt;li&gt;Web 2.0 Funktionalitäten&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Personalisierung und Berechtigungen&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Der Sonderdruck kann von der exensio Homepage &lt;a href=&quot;http://www.exensio.de/media/01-2014_-_Java_aktuell_Sonderdruck_-_Manuel_Breitfeld_und_Tobias_Kraft_-_Enterprise-2.0-Portale_mit_Grails_x_geht_das.pdf&quot;&gt;heruntergeladen&lt;/a&gt; werden.</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/2036625302894490802/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/12/enterprise-20-portale-mit-grails-geht.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2036625302894490802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2036625302894490802'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/12/enterprise-20-portale-mit-grails-geht.html' title='Enterprise-2.0-Portale mit Grails − geht das?'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-9037021653952500872</id><published>2013-12-09T11:26:00.001+01:00</published><updated>2014-02-06T20:15:09.847+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Enterprise Applikationen"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><title type='text'>Umsetzung von Enterprise Applikationen mit Grails (Teil 1)</title><content type='html'>Das Framework Grails ist bereits seit einigen Jahren auf dem Markt und es stellt sich die Frage nach den Einsatzbereichen. Insbesondere ist interessant, inwiefern es sich für Applikationen im Unternehmensbereich eignet, oder ob es nur für die &quot;kleine schnelle Lösung&quot; seine Daseinsberechtigung hat.  &lt;br/&gt;&lt;br/&gt;In dieser dreiteiligen Blog-Serie werden die folgenden Themen beleuchtet: &lt;ul&gt;&lt;li&gt;Grails im Überblick (Teil 1)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.exensio.de/2014/01/umsetzung-von-enterprise-applikationen.html&quot;&gt;Anforderungen an Enterprise Applikationen (Teil 2)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.exensio.de/2014/02/umsetzung-von-enterprise-applikationen.html&quot;&gt;Enterprise Applikationen mit Grails (Teil 3)&lt;/a&gt;&lt;/li&gt;  &lt;/ul&gt;In unserem Blog haben wir schon vielfach zu technischen Themen über Grails berichtet. In diesem 1. Teil wird nochmal auf einige grundlegende Features eingegangen.  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Grails im Überblick&lt;/h2&gt;Bereits im Jahr 2005 startete das Projekt &lt;a href=&quot;http://www.grails.org&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt;, inspiriert im Wesentlichen durch die Konzepte von Ruby on Rails. Zu dieser Zeit war die Entwicklung von JEE-Applikationen schwergewichtig und es floss viel Arbeit in die Programmierung von Glue Code. Mit Hilfe von Spring und Neuerungen im JEE-Standard hat sich dies natürlich in den letzten Jahren massiv geändert. Als Fullstack-Webframework deckt Grails das gesamte Spektrum des Entwickelns, Testens und der Produktivsetzung einer Webapplikation ab. Es hat insbesondere das Ziel, die Komplexität bei der Erstellung von Webapplikationen zu reduzieren, basiert jedoch trotzdem auf etablierten Java-Technologien. &lt;br/&gt;&lt;br/&gt;Zwischenzeitlich werden in regelmäßigen Abständen jährlich Minor- und Major-Releases freigegeben. Das nächste größere Release, die Version 3.0, legt seinen Schwerpunkt auf die Modularisierung und die Verschmelzung mit Gradle als Build-System. Es soll neben Servlet-Containern auch eine bessere Integration für Frameworks wie bspw. Netty geben.  &lt;br/&gt;Grails ist ein Open-Source-Projekt und steht unter der Apache Lizenz 2.0, so dass keine Lizenzkosten bei der Verwendung anfallen. Die Weiterentwicklung und Pflege wird wesentlich von der Firma Pivotal, eine ausgelagerte Tochterfirma von VMWare, vorangetrieben. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Groovy&lt;/h2&gt;Die Basis von Grails bildet die Sprache &lt;a href=&quot;http://groovy.codehaus.org/&quot; target=&quot;_blank&quot;&gt;Groovy&lt;/a&gt;. Hierbei handelt es sich um eine nicht typisierte Programmiersprache, die eine JVM benötigt und es oftmals ermöglicht, Anweisungen - im Vergleich zu Java - kürzer auszudrücken. Das folgende Code-Beispiel zeigt, wie einfach Listen definiert und wie sie mit Hilfe der Groovy-Closure &lt;i&gt;findAll&lt;/i&gt; für Collections verarbeitet werden können. Closures sind das Pendant zu den in Java8 erscheinenden Lambda-Expressions. &lt;br/&gt;&lt;br/&gt;Ab Version 2 von Groovy kann bei Bedarf im Code mit der Annotation &lt;i&gt;@TypeChecked&lt;/i&gt; die Typsicherheit gewährleistet werden. Dies ist beispielsweise für öffentlich verwendete Schnittstellen interessant. &lt;br/&gt;In der Vergangenheit gab es bei Groovy öfters Kritik bzgl. der Performance. Hier wurden in den zurückliegenden Releases wesentliche Optimierungen eingearbeitet und durch explizites Setzen der ebenfalls ab Version 2 verfügbaren Annotation &lt;i&gt;@CompileStatic&lt;/i&gt; können zusätzliche Performance-Gewinne erzielt werden.  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Schlüsselkonzepte&lt;/h2&gt;Wesentliche Schlüsselkonzepte von Grails sind DRY (Don’t repeat yourself) und CoC (Convention over Configuration). Diese Prinzipien erlauben dem Entwickler eine stärkere Konzentration auf die Business-Logik und auch der Code wird übersichtlicher. Nachfolgend werden einige Beispiele aufgeführt:  &lt;ul&gt;&lt;li&gt;Logging kann auf Anhieb verwendet werden ohne vorherige Instanziierung &lt;/li&gt;&lt;li&gt;Zugriff auf Services von Controllern erfolgt durch Definition einer Klassen-Variablen, die den gleichen Namen wie der Service hat &lt;/li&gt;&lt;li&gt;Domain-Klassen, das Pendant zu den Entity Beans, liegen im Verzeichnis &lt;i&gt;grails-app/domain&lt;/i&gt;. Sie werden automatisch mit der DB gemappt und es kann direkt damit gearbeitet werden, ohne dass ein  EntityManager oder Ähnliches benötigt wird &lt;/li&gt;&lt;/ul&gt;Die Flexibilität geht mit CoC aber nicht verloren. Es bedeutet viel mehr, dass der Standard vordefiniert ist, man bei Bedarf jedoch auch davon abweichen kann.  &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Einfacher Start mit Grails&lt;/h2&gt;Besonders der Einstieg in Grails gestaltet sich einfach. Für den Start mit Grails wird von der Homepage die aktuelle Version heruntergeladen und entpackt. Da Grails auf der JVM aufsetzt, muss nichts als das JDK vorhanden, sowie über die Umgebungsvariable eingebunden sein. Es ist möglich, mit einem gewöhnlichen Texteditor loszulegen. Das Basisprojekt wie auch einzelne Code-Fragmente können mit der Anweisung &lt;i&gt;grails&lt;/i&gt; und daran angehängten Optionen erstellt werden. Der Befehl &lt;i&gt;grails create-domain-class com.exensio.Project&lt;/i&gt; erzeugt die entsprechende Domain-Klasse &lt;i&gt;Project&lt;/i&gt; für die Interaktion mit der Persistenzschicht. Die Applikation wird mit dem Befehl &lt;i&gt;grails run-app&lt;/i&gt; auf dem eingebetteten Tomcat-Server gestartet. Alternativ kann ebenfalls mit den später vorgestellten Plugins Jetty als Laufzeitumgebung ausgewählt werden. Während der gesamten Entwicklungszeit und auch beim Ausführen von Tests wird gegen den eingebauten Tomcat-Server entwickelt. &lt;br/&gt;&lt;br/&gt;Ein weiteres Feature, das den Einstieg erleichtert, ist das sogenannte Scaffolding. Über Scaffolding können aus Domain-Klassen Controller und Views für die CRUD-Funktionalitäten erzeugt werden. Dieser Ansatz eignet sich hervorragend für das Rapid Prototyping, um bspw. dem Kunden zeitnah erste Ergebnisse zeigen und besprechen zu können. Es wird zwischen dynamischen und statischen Scaffolding unterschieden. Ersteres generiert den Code der Views und Controller immer zur Laufzeit aus Templates. Die statische Methode erstellt beide Komponenten einmalig und speichert sie in den entsprechenden Dateien. Damit kann der Source-Code im Nachhinein an die eigenen Bedürfnisse angepasst werden. Ändern sich Attribute in der Domain-Klasse, so müssen diese manuell in den erzeugten Dateien nachgezogen werden. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Bewährte Technologien&lt;/h2&gt;Grails setzt auf bewährte Technologien in seinem Technologiestack, wie die nachfolgende Grafik verdeutlicht. Neben der JVM ist Spring eine wesentliche Basistechnologie, die in allen Bereichen eingesetzt wird. Außer JEE-Standards und Hibernate für die Persistenz kommen mit JUnit und Ivy auch im Test- und Build-Zyklus weit verbreitete Technologien zum Einsatz. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-0J2fkMCk5_E/UpTJPvNHpsI/AAAAAAAAAjQ/Zkm0-utPIm4/s1600/grailsstack.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-0J2fkMCk5_E/UpTJPvNHpsI/AAAAAAAAAjQ/Zkm0-utPIm4/s320/grailsstack.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br/&gt;Kurz eingegangen werden soll hier auf GORM, das die Persistenzschicht kapselt und entsprechende Funktionalitäten für einen schnellen Zugriff auf diese bereitstellt. Hibernate ist der standardmäßig konfigurierte OR-Mapper. Es ist aber auch möglich GORM mit NOSQL-Datenbanken wie MongoDB zu verwenden. &lt;br/&gt;&lt;br/&gt;Insbesondere das Erstellen von Datenbankabfragen ist mit GORM sehr komfortabel möglich. Über die Domain-Klasse können direkt Finder-Methoden mit bis zu drei Abfrage-Kriterien erstellt werden, wie das nachfolgende Beispiel zeigt. Ein Zweites zeigt die sogenannten Where-Queries, die Abfragen erlauben mit in Groovy-Code formulierten Bedingungen.   &lt;pre class=&quot;java&quot; name=&quot;code&quot; style=&quot;display: none;&quot;&gt;&lt;br /&gt;// Finde Methode mit zwei AND verknüpften Attributen&lt;br /&gt;Book.findAllByTitleAndAuthor(&#39;Grails in Action&#39;, authorInstance)&lt;br /&gt;// Where-Query&lt;br /&gt;def query = Book.where{ &lt;br /&gt;  year(published) == 2013 &amp;&amp; title =~ &#39;grails&#39;&lt;br /&gt;}&lt;br /&gt;def results = query.list(sort: &#39;title&#39;)&lt;br /&gt;&lt;/pre&gt;  &lt;br/&gt;&lt;h2&gt;Plugins&lt;/h2&gt;Ein zusätzlicher wichtiger Baustein sind Plugins, über die weitere Funktionalitäten hinzugefügt werden können. Derzeit stehen fast 800 Plugins für Grails bereit. Plugins bieten hauptsächlich Erweiterungen für folgende Bereiche: &lt;ul&gt;&lt;li&gt;Frontend (z.B. JQuery, AngularJS)&lt;/li&gt;&lt;li&gt;Integration von Drittsystemen (z.B. Suchmaschinen, BPMN-Maschinen)&lt;/li&gt;&lt;li&gt;Fachliche Funktionalitäten (z.B. Tagging, Voting)&lt;/li&gt;&lt;li&gt;Persistenzschicht (z.B. neo4j, MongoDB)&lt;/li&gt;&lt;li&gt;Nichtfunktionale Erweiterungen (z.B. Auditing zum Messen von Antwortzeiten)&lt;/li&gt;&lt;/ul&gt;Neben der Wiederverwendbarkeit sind als wesentliche Vorteile von Plugins eine einfache Installation und Möglichkeiten zur Modularisierung anzusehen. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Ausblick&lt;/h2&gt;Nach einem Blick aus der Vogelperspektive auf das Grails-Framework, fokusieren sich die beiden folgenden Beiträge dieser Serie auf Enterprise Applikationen. </content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/9037021653952500872/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/12/umsetzung-von-enterprise-applikationen.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/9037021653952500872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/9037021653952500872'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/12/umsetzung-von-enterprise-applikationen.html' title='Umsetzung von Enterprise Applikationen mit Grails (Teil 1)'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-0J2fkMCk5_E/UpTJPvNHpsI/AAAAAAAAAjQ/Zkm0-utPIm4/s72-c/grailsstack.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-8058404292389956850</id><published>2013-11-22T12:04:00.000+01:00</published><updated>2013-11-22T12:04:52.547+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="exensio"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><title type='text'>Vorträge von exensio im Jahr 2013 zum Thema Grails</title><content type='html'>In diesem Blogpost möchte ich kurz auf Vorträge von uns zum Thema Grails hinweisen. Besonderer Dank gilt hier unserem Kollegen Tobias Kraft, der die meisten Vorträge gehalten hat.&lt;br /&gt;&lt;br /&gt;&lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/28504436&quot; width=&quot;427&quot; height=&quot;356&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;https://de.slideshare.net/tobiaskraft/20131121-grails-enterprise&quot; title=&quot;Kann ich mit Grails Enterprise Applikationen umsetzen?&quot; target=&quot;_blank&quot;&gt;Kann ich mit Grails Enterprise Applikationen umsetzen?&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/tobiaskraft&quot; target=&quot;_blank&quot;&gt;Tobias Kraft&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;&lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/23907664&quot; width=&quot;427&quot; height=&quot;356&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;https://de.slideshare.net/tobiaskraft/20130704-exensio-enterprisegrails&quot; title=&quot;Enterprise 2.0 Portale mit Grails. Geht das?&quot; target=&quot;_blank&quot;&gt;Enterprise 2.0 Portale mit Grails. Geht das?&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/tobiaskraft&quot; target=&quot;_blank&quot;&gt;Tobias Kraft&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;&lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/18593595&quot; width=&quot;427&quot; height=&quot;356&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;https://de.slideshare.net/tobiaskraft/130410-jug-kagrailsueberblickundpraxis&quot; title=&quot;Grails im Überblick und in der Praxis&quot; target=&quot;_blank&quot;&gt;Grails im Überblick und in der Praxis&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/tobiaskraft&quot; target=&quot;_blank&quot;&gt;Tobias Kraft&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/8058404292389956850/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/11/vortrage-von-exensio-im-jahr-2013-zum.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8058404292389956850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8058404292389956850'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/11/vortrage-von-exensio-im-jahr-2013-zum.html' title='Vorträge von exensio im Jahr 2013 zum Thema Grails'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-3767546074910430554</id><published>2013-10-10T22:02:00.000+02:00</published><updated>2013-10-11T08:05:57.483+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><title type='text'>Word-Dateien aus Templates mit FreeMarker in Grails erstellen</title><content type='html'>In einem aktuellen Kundenprojekt sollen dynamisch erzeugte Word-Dokumente zum Download angeboten werden. Dabei sollen die Word-Dokumente genau wie die Vorlage des Kunden aussehen und an den entsprechenden Stellen mit Werten aus der Applikation gefüllt sein.&lt;br /&gt;In diesem konkreten Fall erzeugt die Applikation aus einer Fülle an Daten ein Übersichtsdokument in Word - ganz nach dem Template des Kunden.&lt;br /&gt;Dieser Blog-Post soll zeigen, wie einfach die Erstellung eines Word-Dokuments - auch unter Verwendung einer Vorlage - mittlerweile ist.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Word und Office Open XML&lt;/h3&gt;Das &quot;neue&quot; Word-Format &quot;docx&quot; ist im Prinzip eine zip-Datei, die verschiedene Ordner und Dateien enthält [1]. Jede docx-Datei kann entpackt, manuell verändert und wieder gepackt werden. Dabei findet sich der Inhalt des Dokuments maßgeblich in der XML-Datei &quot;word/document.xml&quot; wieder.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;FreeMarker Templates&lt;/h3&gt;&lt;div&gt;FreeMarker Templates [2] sind Textdateien, in denen - im einfachsten Fall - Variablen durch Werte ersetzt werden. Um also ein bestimmtes Word-Dokument in ein FreeMarker Template umzuwandeln, genügen folgende Schritte:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Word-Datei als docx speichern und mit einem Zip-Porgramm entpacken&lt;/li&gt;&lt;li&gt;Die Datei &quot;word/document.xml&quot; in einem Texteditor öffnen und FreeMarker-Variablen in Form von ${titel}, ${datum} etc. an den gewünschten Stellen einfügen&lt;/li&gt;&lt;li&gt;Die in Schritt 1 entstandene Ordnerstruktur wieder zu einem docx-Dokument zippen.&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Implementierung in Groovy / Grails&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;Die Schritte, um jedes beliebige Template als Word-Datei anbieten zu können, sind sehr einfach. Der Ablauf ist wie folgt:&lt;br /&gt;&lt;h4&gt;docx-Template vom ClassPath in ein temporäres Verzeichnis kopieren und entzippen&lt;/h4&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;// Specify path to template in classpath&lt;br /&gt;def applicationContext = grailsApplication.mainContext&lt;br /&gt;def basePath = applicationContext.getResource(&quot;/&quot;).getFile().toString()&lt;br /&gt;def source = new File(&quot;${basePath}/../src/groovy/com/exensio/templates/Project_Summary.docx&quot;)&lt;br /&gt;&lt;br /&gt;// Specify destination file name and path (uses temp directory)&lt;br /&gt;def destinationFile = File.createTempFile(&quot;Project_Summary&quot;, &quot;.zip&quot;)&lt;br /&gt;def destinationFolderName = FilenameUtils.getBaseName(destinationFile.name)&lt;br /&gt;def destinationFolder = new File(destinationFile.getParent() + &quot;/&quot; + destinationFolderName)&lt;br /&gt;destinationFolder.mkdir()&lt;br /&gt;&lt;br /&gt;// Copy the Word template to a temp file and unzip it to the temp folder&lt;br /&gt;FileUtils.copyFile(source, destinationFile)&lt;br /&gt;def ant = new AntBuilder();&lt;br /&gt;ant.unzip(src: destinationFile, dest:destinationFolder,  overwrite:&quot;true&quot;)&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;h4&gt;XML-Datei mit FreeMarker verarbeiten&lt;/h4&gt;&lt;div&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;// Specify FreeMarker to load from temp directory&lt;br /&gt;def freeMarkerConfig = new Configuration()&lt;br /&gt;freeMarkerConfig.setDefaultEncoding(&quot;utf-8&quot;)&lt;br /&gt;def fileTemplateLoader = new FileTemplateLoader(new File(System.getProperty(&quot;java.io.tmpdir&quot;)))&lt;br /&gt;freeMarkerConfig.setTemplateLoader(fileTemplateLoader);&lt;br /&gt;&lt;br /&gt;// Load the template (destinationFolderName has to be relative to the temp directory!)&lt;br /&gt;def template = freeMarkerConfig.getTemplate(destinationFolderName + &quot;/word/document.xml&quot;)&lt;br /&gt;&lt;br /&gt;// Process template and write it to itself&lt;br /&gt;def writer = new FileWriter(new File(destinationFolder.getAbsolutePath() + &quot;/word/document.xml&quot;)&lt;br /&gt;template.process(model, writer);&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;h4&gt;Word-Dokument als docx zippen und zum Download anbieten&lt;/h4&gt;&lt;/div&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;// Zip the docx folder to the final docx file&lt;br /&gt;ant.zip(destfile: destinationFile, basedir:destinationFolder)&lt;br /&gt;&lt;br /&gt;// Send file as stream to response output stream&lt;br /&gt;response.setContentType(&quot;application/vnd.openxmlformats-officedocument.wordprocessingml.document&quot;)&lt;br /&gt;response.setHeader(&quot;Content-Disposition&quot;, &quot;attachment; filename=\&quot;Project_Summary.docx\&quot;&quot;);&lt;br /&gt;def inputStream = new FileInputStream(destinationFile)&lt;br /&gt;IOUtils.copy(inputStream, response.outputStream)&lt;/pre&gt;&lt;h4&gt;&lt;b style=&quot;font-family: &#39;Times New Roman&#39;; white-space: normal;&quot;&gt;Streams schließen und temporäre Dateien/Ordner aufräumen&lt;/b&gt;&lt;/h4&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;finally {&lt;br /&gt; IOUtils.closeQuietly(response.outputStream)&lt;br /&gt; IOUtils.closeQuietly(inputStream)&lt;br /&gt; FileUtils.deleteQuietly(destinationFile)&lt;br /&gt; FileUtils.deleteQuietly(destinationFolder)&lt;br /&gt;}&lt;/pre&gt;&lt;h3&gt;Fazit&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;Das Erstellen &lt;i&gt;echter &lt;/i&gt;Word-Dokumente ist aufgrund des XML-basierten Standards&amp;nbsp;Office Open XML relativ einfach geworden. Mit der Kombination von Grails und FreeMarker lassen sich auch komplizierte Formatvorlagen zügig umsetzen.&lt;/div&gt;&lt;div&gt;Allerdings: Je komplizierter das Vorlagedokument ist, desto aufwändiger ist auch die Erstellung des FreeMarker-Templates. Besonders übersichtlich ist die &quot;document.xml&quot;-Datei nämlich nicht.&lt;br /&gt;&lt;/div&gt;&lt;h3&gt;Links&lt;/h3&gt;&lt;br/&gt;[1]&amp;nbsp;&lt;a href=&quot;http://de.wikipedia.org/wiki/Office_Open_XML&quot;&gt;http://de.wikipedia.org/wiki/Office_Open_XML&lt;/a&gt;&lt;br /&gt;[2]&amp;nbsp;&lt;a href=&quot;http://freemarker.org/&quot;&gt;http://freemarker.org/&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/3767546074910430554/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/10/word-dateien-aus-templates-mit.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/3767546074910430554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/3767546074910430554'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/10/word-dateien-aus-templates-mit.html' title='Word-Dateien aus Templates mit FreeMarker in Grails erstellen'/><author><name>Manuel Breitfeld</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-8265077884062086996</id><published>2013-07-07T15:47:00.000+02:00</published><updated>2013-07-07T15:47:26.443+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Informationsportal"/><category scheme="http://www.blogger.com/atom/ns#" term="Konferenz"/><category scheme="http://www.blogger.com/atom/ns#" term="Portal"/><title type='text'>Java Forum Stuttgart - wir waren als Aussteller und Vortragende mit dabei</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-pQOtOdM_u5w/UdlvzOPHf4I/AAAAAAAAC18/vN41Yz9UF2k/s1600/jfs.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-pQOtOdM_u5w/UdlvzOPHf4I/AAAAAAAAC18/vN41Yz9UF2k/s1600/jfs.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-c-mjb-qhX3U/UdlxGbCvSLI/AAAAAAAAC2M/Bc24IrgOU4Q/s1600/vortrag_exensio_portal_grails_groovy.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-c-mjb-qhX3U/UdlxGbCvSLI/AAAAAAAAC2M/Bc24IrgOU4Q/s1600/vortrag_exensio_portal_grails_groovy.png&quot; /&gt;&lt;/a&gt;Am 04. Juli fand wieder das Java Forum in Stuttgart statt. Das JFS ist eine eintägige Konferenz rund um das Thema Java. exensio war dieses Mal als Aussteller mit dabei. Des Weiteren haben unsere Kollegen Manuel Breitfeld und Tobias Kraft einen Vortrag mit dem Titel »Enterprise 2.0 Portale mit Grails. Geht das?« gehalten. Der Vortag kann von &lt;a href=&quot;http://exensio.de/media/20130704_exensio_enterprise_grails.pdf&quot; target=&quot;_blank&quot;&gt;hier&lt;/a&gt; als PDF heruntergeladen werden.</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/8265077884062086996/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/07/java-forum-stuttgart-wir-waren-als.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8265077884062086996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8265077884062086996'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/07/java-forum-stuttgart-wir-waren-als.html' title='Java Forum Stuttgart - wir waren als Aussteller und Vortragende mit dabei'/><author><name>Peter Soth</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-pQOtOdM_u5w/UdlvzOPHf4I/AAAAAAAAC18/vN41Yz9UF2k/s72-c/jfs.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-8755103572744950200</id><published>2013-05-30T12:51:00.000+02:00</published><updated>2013-05-30T12:51:20.444+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Konferenz"/><title type='text'>Besuch der gr8conf in Kopenhagen</title><content type='html'>Bereits zum fünften Mal wurde die &lt;a href=&quot;http://gr8conf.eu/&quot; target=&quot;_blank&quot;&gt;gr8conf&lt;/a&gt; in Kopenhagen abgehalten. Die Konferenz beschäftigt sich mit Themen rund um die Technologien &lt;a href=&quot;http://groovy.codehaus.org/&quot; target=&quot;_blank&quot;&gt;Groovy&lt;/a&gt; und &lt;a href=&quot;http://www.grails.org/&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;University Day&lt;/h3&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-GiAaXXmsNLA/Uacp8NDqM3I/AAAAAAAAAgs/4jxU7vR7tNQ/s1600/gr8conf_intro.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;http://3.bp.blogspot.com/-GiAaXXmsNLA/Uacp8NDqM3I/AAAAAAAAAgs/4jxU7vR7tNQ/s200/gr8conf_intro.jpg&quot; width=&quot;122&quot; /&gt;&lt;/a&gt;In Kopenhagen wurde man am ersten Tag mit Dauerregen begrüßt, also der ideale Start um beim sogenannten &quot;University Day&quot; nicht anderweitig abgelenkt zur werden. Für mich standen drei Workshops auf dem Plan.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Async Grails Workshop&lt;/h2&gt;Nach einem zögerlichen Start aufgrund von Netzwerkproblemen stellte Collin Harrington verschiedene Möglichkeiten zur Realisierung von asynchronen Anwendungsfällen vor. Es wurden exemplarisch einige mit dem Springframework zur Verfügung gestellten Event-Mechanismen umgesetzt, wie beispielsweise ApplicationEvents oder SecurityEvents. Als weitere Möglichkeiten wurde die Nutzung des Events Push Plugin für Grails sowie Servlet 3.0 vorgestellt.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;GPars Workshop&lt;/h2&gt;Nach dem Mittagessen beschäftigte sich der nächste Workshop mit der parallelen Programmierung unter&amp;nbsp;&lt;a href=&quot;http://gpars.codehaus.org/&quot; target=&quot;_blank&quot;&gt;GPars&lt;/a&gt; ab. Es wurden viele Code-Beispiele vorgeführt, die anschaulich zeigten wie GPars die Entwicklung von parallelen  Tasks durch Abstraktion vereinfacht. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Unleashing the power of AST transformations workshop&lt;/h2&gt;Auch beim letzten Workshop des Tages gab es viele Code-Beispiele und praktische Tipps für den Umgang mit AST-Transformationen. Der Workshop zeigte insbesondere auf, dass AST-Transformationen nicht nur für Frameworks geeignet sind, sondern auch in Kundenprojekten einen Mehrwert bringen können. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Hackergarten&lt;/h2&gt;Nach Pizza am Buffet ging es am Abend mit dem &lt;a href=&quot;http://hackergarten.net/&quot; target=&quot;_blank&quot;&gt;Hackergarten&lt;/a&gt; weiter. Ziel von Hackergarten ist es gemeinsam in kleineren Gruppen einen Beitrag zu einem Open-Source-Projekt innerhalb eines Abends zu leisten.  Ich schloss mich der Gruppe für das &lt;a href=&quot;http://www.ratpack-framework.org/&quot; target=&quot;_blank&quot;&gt;Ratpack-Framework&lt;/a&gt; an, bei der es um die Erweiterung der Framework-Tests ging.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Erster Konferenztag&lt;/h3&gt;Die Konferenz startete nach der Begrüßung mit der Keynote &quot;The rise and fall of empires: Lessons for language designers and programmers&quot; von Dr. Venkat Subramaniam. Er hinterlegte in einem sehr unterhaltsamen Vortrag seine Thesen mit Beispielen aus der Weltgeschichte, so dass man nebenbei noch Gast einer Vorlesung in Geschichte sein durfte.  &lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-Zpr0Ptt2cAA/UacqR1EjHYI/AAAAAAAAAg0/qFmbyk6F4fg/s1600/gr8conf_venkat.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;215&quot; src=&quot;http://1.bp.blogspot.com/-Zpr0Ptt2cAA/UacqR1EjHYI/AAAAAAAAAg0/qFmbyk6F4fg/s320/gr8conf_venkat.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Using Static Analysis with Grails&lt;/h2&gt;Der erste Vortrag des Tages stellte statische Code-Analyse mit Hilfe von Jenkins vor. Jenkins ist auch der von &lt;a href=&quot;http://www.exensio.de/&quot; target=&quot;_blank&quot;&gt;exensio&lt;/a&gt; verwendete Continuous Integration Server. Der Vortrag zeigte, dass exensio bzgl. der Continuous Integration auf dem aktuellsten Stand ist.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Raspberry Pi à la GroovyFX&lt;/h2&gt;exensio hat wie in diesem Blog bereits berichtet ebenfalls schon Projekte mit Raspberry PI umgesetzt, allerdings mit einem anderen Technlogie-Stack. Neben Besonderheiten des Raspberry PI ging Stephen Chin auf Umsetzung der animierten GroovyFX-Applikation ein. Die Applikation wurde während der Konferenz  über den HDMI-Ausgang des Raspberry PI an einen Monitor angeschlossen und zeigte den Besuchern am Info-Stand Details zu den Sessions und Sprechern der gr8conf. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Road to Grails 3.0&lt;/h2&gt;Graeme Rocher ging in seinem Vortrag neben Neuerungen in den aktuellen Releases auch auf das nächste Hauptrelease 3.0 ein. Neben Gradle für Build- und Dependency-Management ist vor allem die Modularisierung von Grails ein wichtiges Hauptthema.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Functional Programming in Groovy&lt;/h2&gt;Funktionale Programmierung mit Life-Coding vom Feinsten zeigte Keynote-Speaker Dr. Subramaniam in seinem Vortrag. Es wurden zwar keine unbekannten Sachen gezeigt, aber es wurde einem wieder vieles ins Gedächtnis gerufen auf das man beim Programmieren achten sollte. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Building Grails apps with Gradle&lt;/h2&gt;Luke Delay stellte für die Grails Version 2.3 vor, wie der Build-Prozess sowie das Depedency-Management über Gradle erfolgen kann. Wie bereits erwähnt wird ab Grails 3.0 der Build-Mechanismus komplett auf Gradle umgestellt. Allerdings wird diese Lösung dann komplett neu sein und nicht auf der Version von Grails 2.3 aufsetzen. Deshalb scheint sich die Umstellung des Builds in 2.3 auf Gradle nur für größere bzw. Multi-Projekte einen entsprechenden Mehrwert zu bieten.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Grails Under The Hood&lt;/h2&gt;Im letzten Vortrag des Tages ging Graeme Rocher detailliert auf die Struktur des  Grails-Core-Projektes ein.  Er stellte einige ausgewählte Details des Source-Codes vor.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Zweiter Konferenztag&lt;/h3&gt;Auch am zweiten Konferenztag gab es wieder jeweils zwei parallele Sessions zur Auswahl. Ich habe die nachfolgenden Sessions besucht. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Is Groovy as Fast as Java?&lt;/h2&gt;Der Vortragstitel bietet natürlich sofort einiges an Gesprächsstoff. Russel Winder hat die Performance anhand eines Code-Beispiels verglichen. Iterativ hat er anschließend die Performance des Groovy-Programms verbessert. Am Ende war das Groovy-Programm sogar etwas schneller wie der Java-Code, wobei dies natürlich keine allgemein gültige Aussage ist.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Speed up your development: GroovyServ and Grails Improx Plugin&lt;/h2&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-IQ7vnWRhP78/UacqaOfCIXI/AAAAAAAAAg8/iImVaevPE8c/s1600/gr8conf_end.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;http://2.bp.blogspot.com/-IQ7vnWRhP78/UacqaOfCIXI/AAAAAAAAAg8/iImVaevPE8c/s200/gr8conf_end.jpg&quot; width=&quot;148&quot; /&gt;&lt;/a&gt;Bei diesem Vortrag wurden zwei Lösungen vorgestellt, die die Produktivität während der Entwicklung steigern sollen. Durch eine interne serverseitige Lösung wird mit GroovyServ die Startzeit von Groovy minimiert. Gleiches gilt für das Grails Improx Plugin bei der die Startzeit von Tests minimiert wird. Insbesondere Integration Tests haben bei Grails eine relativ lange Startzeit, da der Embedded Server hochgefahren wird. Hier verspricht das Improx Plugin beim mehrmaligen Durchführen von Tests Verbesserung.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Spring 4 and Groovy 2&lt;/h2&gt;Jürgen Höller, der Projekt-Manager des Springframeworks, gab Einblicke in die Neuerungen von Spring 4. Interessant ist vor allen Dingen die geplante Unterstützung von Groovy in diesem Release. Damit wäre es beispielsweise möglich Spring-Konfigurationen über Groovy zu erstellen.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Trench code&lt;/h2&gt;In diesem Vortrag wurden einige Best Practices aus einem größeren Grails-Projekt vorgestellt. Hierzu zählten unter anderem die Verwendung von Abstrakten Domain-Klasse oder Hibernate-Filter für SQL-Abfragen. Besonders interessant war der Einsatz von Scaffolding im Produktiv-Betrieb. In dem Projekt wurden die meisten Controller-Klassen zur Laufzeit über Scaffolding erzeugt.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Ratpack&lt;/h2&gt;Das schon erwähnte Ratpack-Framework ist ein leichtgewichtiges Framework, um Applikationen zu bauen. Basis zur Kommunikation ist Netty, so dass hier eine asynchrone Abarbeitung der Reqeusts erfolgt. Ratpack läuft als Standalone-Applikation und nicht in einem Servlet- bzw. JEE-Container. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Grails Transactions&lt;/h2&gt;Bei meinem letzten Vortrag als Zuhörer ging Burt Beckwith auf das Transaktions-Handling von Grails in der Tiefe ein. Bisher weniger bekannt ist wohl die Tatsache, dass es bereits seit längerem  Unterstützung für &quot;Two Phase Commits&quot;  in Form des &lt;a href=&quot;http://grails.org/plugin/atomikos&quot; target=&quot;_blank&quot;&gt;Atomikos-Plugins&lt;/a&gt; gibt.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Fazit&lt;/h3&gt;Auch dieses Jahr hat sich die Reise nach Kopenhagen gelohnt. Es gibt viele Informationen über die aktuellen Entwicklungen und Trends aus den Bereichen Groovy und Grails von den Kern-Entwicklern der jeweiligen Projekte. </content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/8755103572744950200/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/05/besuch-der-gr8conf-in-kopenhagen.html#comment-form' title='1 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8755103572744950200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/8755103572744950200'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/05/besuch-der-gr8conf-in-kopenhagen.html' title='Besuch der gr8conf in Kopenhagen'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-GiAaXXmsNLA/Uacp8NDqM3I/AAAAAAAAAgs/4jxU7vR7tNQ/s72-c/gr8conf_intro.jpg" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-4151690190267271219</id><published>2013-05-08T10:51:00.000+02:00</published><updated>2014-05-26T11:00:29.779+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ElasticSearch"/><category scheme="http://www.blogger.com/atom/ns#" term="Enterprise Search"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="IT-Consulting"/><category scheme="http://www.blogger.com/atom/ns#" term="Tomcat"/><title type='text'>Aufbau einer Intranetsuche leicht gemacht, dank Open Source</title><content type='html'>Jeder kennt vermutlich das folgende Szenario aus eigener Erfahrung: Vor längerer Zeit hat man an einem Projekt gearbeitet und sucht nun verzweifelt nach einem Dokument, in dem bspw. eine Schnittstelle fachlich spezifiziert wurde. Doch wo wurde das Dokument bloß von den Kollegen abgelegt? &lt;br /&gt;&lt;br /&gt;Dieser Blogeintrag beschreibt die einfache Realisierung einer Suchanwendung im Intranet für Dokumente unterschiedlichster Dateiformate, die in einem SVN-Repository sowie auf zentralen Laufwerken abgelegt sind. Dies bedeutet also, dass die Suchanwendung Input aus verschiedenen Quellsystemen erhält.  &lt;br /&gt;Als Suchserver wird &lt;a href=&quot;http://www.elasticsearch.org/&quot; target=&quot;_blank&quot;&gt;elasticsearch&lt;/a&gt; verwendet. Das Frontend für die Suche wurde mit &lt;a href=&quot;http://www.grails.org/&quot; target=&quot;_blank&quot;&gt;Grails&lt;/a&gt; umgesetzt.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Indexieren der Dokumente&lt;/h2&gt;Zunächst ist es wichtig, die Dokumente zu indexieren und eine regelmäßige Aktualisierung des Indexes zu gewährleisten, um Änderungen an den Dokumenten in der Suche abbilden zu können. &lt;br /&gt;Zur Indexierung der Laufwerke wird &lt;a href=&quot;http://manifoldcf.apache.org/en_US/index.html&quot; target=&quot;_blank&quot;&gt;ManifoldCF&lt;/a&gt; in der Version 1.1.1 eingesetzt. ManifoldCF ist ein Open Source Projekt, dessen Ziel es ist, verschiedene Quellsysteme zur gemeinsamen Indices oder Repositories zu verbinden. ManifoldCF bietet einige Vorteile: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Im Idealfall ist kein eigener Code nötig, um geänderte Dokumente aufzufinden und diese zu indexieren&lt;/li&gt;&lt;li&gt;Unterstützung  für eine Vielzahl von Quellsystemen, wie bspw. SharePoint oder zentrale Laufwerke&lt;/li&gt;&lt;li&gt;Nicht auf elasticsearch als Suchserver beschränkt; es ist eine Vielzahl sogenannter OutputConnectoren vorhanden, z.B. auch für&lt;a href=&quot;http://lucene.apache.org/solr/&quot; target=&quot;_blank&quot;&gt; Solr&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Graphische Oberfläche wie in der nachfolgenden Grafik exemplarisch dargestellt zur leichteren Konfiguration der zu indexierenden Ordner, Planung der Ausführungszeitpunkte etc.)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-bv_Za7Zmgdk/UYJwy3YaQtI/AAAAAAAAABM/KQMwh2sVQhI/s1600/manifold.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;170&quot; src=&quot;http://2.bp.blogspot.com/-bv_Za7Zmgdk/UYJwy3YaQtI/AAAAAAAAABM/KQMwh2sVQhI/s400/manifold.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ManifoldCF Oberfläche&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Der in ManifoldCF bereits vorhandene Elasticsearch-Output-Connector wurde angepasst, da die vorhandene Implementierung für den Anwendungsfall nicht ausreichend geeignet war. Diese Anpassungen sind im Wesentlichen &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Statt des&lt;a href=&quot;https://github.com/elasticsearch/elasticsearch-mapper-attachments&quot; target=&quot;_blank&quot;&gt; attachment-Plugin&lt;/a&gt; von Elasticsearch übernimmt der Connector selbst die Analyse der Dokumente (unter Verwendung von &lt;a href=&quot;http://tika.apache.org/&quot; target=&quot;_blank&quot;&gt;Tika&lt;/a&gt;). Dadurch ist eine wesentlich feinere Kontrolle der im Index gespeicherten Informationen möglich.&lt;/li&gt;&lt;li&gt;Die Verbindung zum Elasticsearchserver wurde von einem HTTP-Client auf einen &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/java-api/client/&quot; target=&quot;_blank&quot;&gt;TransportClient&lt;/a&gt;  geändert. Beim HTTP-Client treten einige Probleme mit Sonderzeichen, Zeilenumbrüchen etc. beim Senden der extrahierten Informationen an Elasticsearch auf. &lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Input aus den Quellsystemen&lt;/h2&gt;ManifoldCF ist derzeit nicht in der Lage SVN-Repositories direkt zu indexieren. Jedoch ist es möglich, Ordner im lokalen Dateisystem zu indexieren. Damit ist es möglich, das Repository auszuchecken und danach mit &lt;i&gt;svn up&lt;/i&gt; regelmäßige Updates durchzuführen, um die ausgecheckten Dateien zu aktualisieren. Diese Dateien werden schließlich mit ManifoldCF indexiert.   &lt;br /&gt;Aus Performancegründen wurde diese Lösung jedoch verworfen. Wenn man &lt;i&gt;svn up&lt;/i&gt; ausführt, so erhält man eine Liste mit allen hinzufügten, geänderten oder gelöschten Dateien sowie jeweils die Angabe, welcher Fall vorliegt. &lt;br /&gt;Über eine separates kleines Javaprogramm die Ausgabe des svn-Updates eingelesen und nur die darin verzeichneten Dateien wird die entsprechende Aktion durchgeführt. Obwohl dieses Programm die Aktionen in mehreren Threads parallel ausführt, ist es nur etwa 150 Zeilen lang. Zur Analyse der Dokumente wird ein Teil des angepassten ManifoldCF-Codes, der oben beschrieben wurde, wiederverwendet. &lt;br /&gt;Dieses Vorgehen erspart gegenüber ManifoldCF die Iteration über alle ausgecheckten Dateien, um neue, geänderte und gelöschte Dateien zu identifizieren. Da im Anwendungsfall die Anzahl der geänderten Dateien gegenüber der Gesamtzahl Dateien sehr klein ist, beschleunigt sich der Indexierungsprozess enorm.  Der gesamte Vorgang wird über einen cronjob gesteuert. Wenn keine Änderungen vorliegen, ist die Dauer des Vorgangs im Wesentlichen identisch mit der Zeit, die svn up benötigt.  Müssen dagegen alle oder fast alle Dokumente neu indexiert werden, so dauert der Vorgang etwa so lange wie mit oben beschriebenen ManifoldCF-basierten Ansatz. &lt;br /&gt;&lt;br /&gt;Neben dem SVN-Repository wird als zweites Quellsystem das öffentliche Firmenlaufwerk indexiert. Hier existiert für ManifoldCF ein Standard-Konnektor, der lediglich über die ManifoldCF-Oberfläche konfiguriert werden muss.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Performance&lt;/h2&gt;ManifoldCF ist in der Lage, den Fileshare (ca. 70 000 Dateien) in etwa 15 Minuten nach Änderungen zu durchsuchen und diese zu indexieren. Wurden sehr viele Dokumente geändert oder hinzugefügt, so verlängert sich die Laufzeit.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Elasticsearch&lt;/h2&gt;Es wird Elasticsearch 0.20.5 verwendet. Zum Betrieb der Suche werden keine Plugins benötigt. Das &lt;a href=&quot;https://github.com/mobz/elasticsearch-head&quot; target=&quot;_blank&quot;&gt;elasticsearch-head-plugin&lt;/a&gt; ist jedoch  hilfreich, um den Zustand des Indexes zu überprüfen.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Such-Oberfläche&lt;/h2&gt;Die Grailsanwendung verwendet eine geringfügig angepasste Variante von &lt;a href=&quot;https://github.com/okfn/facetview/&quot; target=&quot;_blank&quot;&gt;FacetView&lt;/a&gt; als Oberfläche. Um die für den Suchbegriff relevanten Textausschnitte anzeigen zu können, werden die Suchanfragen von Facetview nicht direkt an Elasticsearch gesendet, sondern zuvor von der Grailsanwendung weiter verarbeitet.  &lt;br /&gt;Die Anwendung besitzt keine Domainklassen und benötigt auch keine Datenbank. Da keine Domainklassen indexiert werden und der Elasticsearchserver eigenständig läuft, wurde auf die Verwendung des &lt;a href=&quot;http://grails.org/plugin/elasticsearch&quot; target=&quot;_blank&quot;&gt;grails elasticsearch plugins&lt;/a&gt; verzichtet. Stattdessen werden erneut TransportClients verwendet. Um die Anzahl der erzeugten Clients zu reduzieren und sicherzustellen, dass sie korrekt geschlossen werden, werden die Clients in einem Pool verwaltet. Daneben wurde die Möglichkeit, die Dokumente direkt herunterzuladen, geschaffen. Die Anwendung läuft auf einem Tomcat-Server.&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-2JshmTK-ox0/UYJwn9NZaNI/AAAAAAAAABE/cR38Q4fYz2o/s1600/gui.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;261&quot; src=&quot;http://2.bp.blogspot.com/-2JshmTK-ox0/UYJwn9NZaNI/AAAAAAAAABE/cR38Q4fYz2o/s320/gui.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Oberfläche der Suchanwendung&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Fazit&lt;/h2&gt;ManifoldCF eignet sich sehr gut zum Indexieren von Dokumenten aus unterschiedlichen Quellen, jedoch gibt es spezielle Anwendungsfälle, in denen es ineffizient ist, das gesamte Quellsystem zu durchlaufen, um geänderte Dokumente zu finden. In diesen Fällen kann die Performance von ManifoldCF nicht mit einer Anwendung mithalten, die die Eigenheiten dieses Quellsystems, in dem hier beschriebenen Fall SVN, ausnutzt. Es wäre auch möglich, diese Optimierungen in ManifoldCF zu integrieren, indem man einen eigenen, sogenannten RepositoryConnector für SVN schreibt, der Dokumente aus dem Quellsystem bereitstellt. Dazu besteht jedoch in diesem Anwendungsfall keine Notwendigkeit. &lt;br /&gt;ManifoldCF ist, abgesehen von einigen Kleinigkeiten, leicht zu konfigurieren und die Geschwindigkeit beim Aktualisieren des Indexes ist sehr zufriedenstellend. &lt;br /&gt;Über die Qualitäten von Grails, FacetView und Elasticsearch wurde im &lt;a href=&quot;http://www.exensio.de/&quot;&gt;exensio&lt;/a&gt;-Blog bereits berichtet.</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/4151690190267271219/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/05/aufbau-einer-intranetsuche-leicht.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/4151690190267271219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/4151690190267271219'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/05/aufbau-einer-intranetsuche-leicht.html' title='Aufbau einer Intranetsuche leicht gemacht, dank Open Source'/><author><name>Tobias Stengel</name><uri>https://plus.google.com/118290073262760055731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-bv_Za7Zmgdk/UYJwy3YaQtI/AAAAAAAAABM/KQMwh2sVQhI/s72-c/manifold.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4466305307772589754.post-2088405518629665758</id><published>2013-03-07T22:32:00.000+01:00</published><updated>2013-03-08T09:40:28.890+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BI"/><category scheme="http://www.blogger.com/atom/ns#" term="Business Intelligence"/><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Portal"/><title type='text'>Technische Umsetzung von Business Intelligence Applikationen mit Open Source</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-sSrEhjGQSRA/UTmjh77oL1I/AAAAAAAABjc/vSVEgMqqgN4/s1600/iStock_000014898327Large.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;132&quot; src=&quot;http://4.bp.blogspot.com/-sSrEhjGQSRA/UTmjh77oL1I/AAAAAAAABjc/vSVEgMqqgN4/s200/iStock_000014898327Large.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Nach dem mein Kollege Peter Soth im &lt;a href=&quot;http://blog.exensio.de/2013/02/business-intelligence-mit-open-source.html&quot;&gt;ersten Teil&lt;/a&gt; dieser Blogserie die fachlichen Hintergründe sowie die generelle Motivation zur Verwendung von Open Source im Business Intelligence Umfeld beleuchtet hat, geht dieser Teil tiefer auf die technische Umsetzung ein.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Technische Basis&lt;/h2&gt;Wie bereits erwähnt wurde das &lt;a href=&quot;http://www.grails.org/&quot; target=&quot;_blank&quot;&gt;Grails-Framework&lt;/a&gt;  für die Implementierung der Business Intelligence Applikation verwendet. Da Grails eigentlich als Framework für Webapplikationen bekannt ist, werden hier zunächst Gründe aufgeführt, die zu dieser Auswahl geführt haben. &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Keine zusätzlichen Lizenzkosten für die Software&lt;/li&gt;&lt;li&gt;Mitverwendung der bestehenden Server-Infrastruktur&lt;/li&gt;&lt;li&gt;Die Umsetzung als Webapplikation ermöglicht eine einfache Integration in existierende Web-Applikationen (wie bspw. Portale) mit dem kundenspezifischen Look&amp;amp;Feel&lt;/li&gt;&lt;li&gt;Verschiedene Ausgabeformate können einfach durch Plugins unterstützt bzw. selbst implementiert werden&lt;/li&gt;&lt;li&gt;Die Programmiersprache  &lt;a href=&quot;http://groovy.codehaus.org/&quot; target=&quot;_blank&quot;&gt;Groovy&lt;/a&gt; ist die Basis von Grails. Groovy eignet sich auch hervorragend, zur Durchführung von Operationen auf Datenstrukturen, was beim Transformieren der zu importierenden Dateien benötigt wird. Des Weiteren können mit Groovy relativ einfach DSL’s realisiert werden.&lt;/li&gt;&lt;/ul&gt;Ein weiteres wichtiges Kriterium ist die Möglichkeit zur Unterstützung von Batch-Funktionalitäten für die Umsetzung des ETL-Prozesses. Hierfür wird das Batch-Launcher Plugin [1] eingesetzt. Das Plugin ermöglicht die Ausführung von Grails außerhalb eines Servlet-Containers, in dem die Anwendung als normale Java-Applikation über eine Main-Methode gestartet wird. Der Vorteil ist hierbei, dass der ETL-Prozess unabhängig von der Web-Applikation läuft und damit dort zu keinen Ressourcen-Engpässen führt.  &lt;br /&gt;&lt;br /&gt;Da es mehrere Batches in der Applikation gibt, die auch entsprechend überwacht werden müssen, wurde zusätzlich ein leichtgewichtiges Batchframework aufgesetzt. Kern dieses Frameworks ist eine Datenbanktabelle in der die Aktivitäten und der aktuelle Status protokolliert werden. Dadurch kann insbesondere verhindert werden, dass der gleiche Batch mehrfach angestoßen wird und es können Abhängigkeiten zwischen den einzelnen Batches geprüft werden.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Der ETL-Prozess im Detail&lt;/h2&gt;Wie in BI-Applikationen üblich werden auch in diesem Projekt die Daten über den ETL-Prozess [2] importiert und aufbereitet, so dass sie für Analysen in der Applikation vom Endanwender genutzt werden können.  &lt;br /&gt;&lt;br /&gt;Grundlage sind ca. 50 Dateien im CSV-Format, die monatlich von den verschiedenen Quellsystemen geliefert werden. Die CSV-Dateien enthalten Daten über mehrere Monate und sind auch vom Aufbau unterschiedlich. Diese Dateien liefern die Inhalte auf dem untersten Level, die in das Starschema importiert werden.  &lt;br /&gt;&lt;br /&gt;Aus fachlicher Sicht wurden drei Hauptbereiche für Analysen identifiziert, die jeweils über eine eigene Faktentabelle mit den zugehörigen Dimensionstabellen abgebildet werden. In den Dimensionstabellen finden sich unter anderem die zeitlichen Aspekte oder auch Wirkstoffe, da es sich um eine Applikation aus dem Pharmabereich handelt. Die drei Faktentabellen werden jeweils durch einen eigenen Batch mit Inhalten der verschiedenen CSV-Dateien gefüllt. Die Batches sind entsprechend parametrierbar, da die Anzahl der enthaltenen Monate sowie die Monate selbst variieren.  &lt;br /&gt;&lt;br /&gt;Da die Daten zwischen den verschiedenen Quellsystemen abgeglichen und auch gefiltert werden müssen, werden entsprechende Transformationen und Regeln benötigt. Ein hierfür nützliches Hilfsmittel sind Closures [4] in Groovy, um diese Verarbeitungsschritte relativ einfach umzusetzen. Nachfolgend ist eine sehr einfache Bedingung zur Filterung von CSV-Zeilen exemplarisch dargestellt. &lt;br /&gt;&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;def ignoreConditionGeneric = { name -&amp;gt;&lt;br /&gt;  return name &amp;amp;&amp;amp; name != &#39;Summe&#39;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Um für die 50 zu importierenden Dateien nicht unnötig viel ähnlichen oder gar doppelten Code zu erzeugen wurde eine Art DSL [5] entworfen, die für jede Datei die entsprechenden Eigenschaften definiert. Über die DSL wird z.B. definiert welche Spalten sich wiederholen und sich nur durch den Monat unterscheiden oder ob es sich bei den einzelnen Spalten um Beträge oder Namen handelt. Ändert sich die Struktur einer Datei kann somit schnell durch Anpassung der Konfiguration darauf reagiert werden. &lt;br /&gt;&lt;br /&gt;Nachdem die Daten auf der untersten Ebene vorhanden sind, läuft für jede Faktentabelle ein weiterer Batch zur Aggregation der Daten. Hier werden beispielsweise auf der zeitlichen Dimension Daten quartalsweise oder jährlich summiert.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Die grafische Oberfläche&lt;/h2&gt;Eine fachliche Anforderung ist die Unterstützung von verschiedenen Ausgabeformaten. Die folgende Abbildung zeigt die verschiedenen Darstellungsformate exemplarisch für einen Report.  &lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-ZA3s62dr5bg/UTj4yXgFGfI/AAAAAAAAAd4/2L4erVxtpA8/s1600/output.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;90&quot; src=&quot;http://3.bp.blogspot.com/-ZA3s62dr5bg/UTj4yXgFGfI/AAAAAAAAAd4/2L4erVxtpA8/s400/output.png&quot; title=&quot;Die verschiedenen Darstellungsformate&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Die drei verschiedenen Darstellungsformate&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Neben der HTML-Oberfläche stehen Exporte nach CSV sowie PDF zur Verfügung. Diese drei Ausgabeformate werden für sämtliche Reports innerhalb der Applikation auf jeder Ebene angeboten. Dies bedeutet ein Anwender selektiert über entsprechende Auswahlboxen Kriterien und lässt sich für diese einen entsprechenden Report generieren. &lt;br /&gt;&lt;br /&gt;Für den PDF-Report wird eine Open Source Version der itext-Bibliothek eingesetzt. CSV-Dateien werden mit Hilfe des Export-Plugins von Grails umgesetzt. &lt;br /&gt;&lt;br /&gt;Zusätzlich zu den tabellarischen Reportdarstellungen gibt es grafische Aufbereitungen in Form von Diagrammen. Auch hier wird auf die Bibliothek itext zurückgegriffen. &lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-X055tN2-83s/UTj5DOr-axI/AAAAAAAAAeA/E68j6FlABJU/s1600/graphik.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;162&quot; src=&quot;http://4.bp.blogspot.com/-X055tN2-83s/UTj5DOr-axI/AAAAAAAAAeA/E68j6FlABJU/s200/graphik.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Grafische Aufbereitung von Kennzahlen&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2&gt;Fazit&lt;/h2&gt;Mit dem hier vorgestellten Ansatz lassen sich kostengünstig Standardreports erstellen, die vor allen Dingen homogen in bestehende Unternehmens-Applikationen und Infrastrukturen integriert werden können. Applikationen dieser Art sind für Endanwender im Vergleich zu den speziellen BI-Werkzeugen sehr einfach zu bedienen und bieten trotzdem verschiedene Sichten mit entsprechenden Drill-Down Möglichkeiten auf den Daten.  &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Links&lt;/h2&gt;[1] &lt;a href=&quot;http://grails.org/plugin/batch-launcher&quot; target=&quot;_blank&quot;&gt;Batch Launcher Plugin&lt;/a&gt;&lt;br /&gt;[2] &lt;a href=&quot;http://en.wikipedia.org/wiki/Extract,_transform,_load&quot; target=&quot;_blank&quot;&gt;ETL-Process&lt;/a&gt;&lt;br /&gt;[3] &lt;a href=&quot;http://www.rapid-business-intelligence-success.com/star-schema.html&quot; target=&quot;_blank&quot;&gt;Star Schema&lt;/a&gt;&lt;br /&gt;[4] &lt;a href=&quot;http://groovy.codehaus.org/Closures&quot; target=&quot;_blank&quot;&gt;Groovy Closures&lt;/a&gt;&lt;br /&gt;[5] &lt;a href=&quot;http://en.wikipedia.org/wiki/Domain-specific_language&quot; target=&quot;_blank&quot;&gt;Domain Specific Language&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.exensio.de/feeds/2088405518629665758/comments/default' title='Kommentare zum Post'/><link rel='replies' type='text/html' href='http://blog.exensio.de/2013/03/technische-umsetzung-von-business.html#comment-form' title='0 Kommentare'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2088405518629665758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4466305307772589754/posts/default/2088405518629665758'/><link rel='alternate' type='text/html' href='http://blog.exensio.de/2013/03/technische-umsetzung-von-business.html' title='Technische Umsetzung von Business Intelligence Applikationen mit Open Source'/><author><name>Tobias Kraft</name><uri>http://www.blogger.com/profile/07647205088544667726</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-sSrEhjGQSRA/UTmjh77oL1I/AAAAAAAABjc/vSVEgMqqgN4/s72-c/iStock_000014898327Large.jpg" height="72" width="72"/><thr:total>0</thr:total></entry></feed>