tag:blogger.com,1999:blog-37709805790488529722024-03-04T22:41:25.628-08:00My Digital BricksSoftware Engineering tips, fixes, tricks and opinion mixed for fellow programmers.Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.comBlogger35125tag:blogger.com,1999:blog-3770980579048852972.post-80138088738778691962017-02-14T06:32:00.002-08:002017-02-14T06:34:10.566-08:00Secure images with angular.Ever needed to deliver images securely? I had a usecase for delivering map tiles only if the user was authenticated - but I was using stateless authentication with Java Web Tokens (JWT). In order to use web tokens I need to place an Authentication header on each web request - easy enough. But that isnt possible to do when using a html img tag - it only allows for a url souce.<br />
<br />
So you have two options - extend your JWT token reading service to allow it to read tokens from the url params object too (ie <img src='someurl?token=foo'> ) or instead of using the standard src attribute - use javascript to securely download the image data into a blob, and use the html5 blob data attribute for img tags.<br />
<br />
<a href="https://github.com/dougmoscrop/angular-img-http-src" target="_blank">This angular directive will do such a thing</a>. I based my solution on this source code- except noting adding that I use an angular service "Authentication" that has a promise that will resolve only if there is valid non-expired authentication (and will refresh the token if expired).<br />
<br />
So I only needed to modify the directive's core method to add my extra waitForAuthenticated() promise.<br />
<br />
attrs.$observe('httpSrc', function (url) {<br />
revokeObjectURL();<br />
<br />
if (url && url.indexOf('data:') === 0) {<br />
$scope.objectURL = url;<br />
} else if (url) {<br />
<b> Authentication.waitForAuthenticated().then(function(ok){</b><br />
<span style="color: cyan;">$http.get(url, {</span><br />
<span style="color: cyan;"> responseType: 'arraybuffer',</span><br />
<span style="color: cyan;"> headers: {</span><br />
<span style="color: cyan;"> 'accept': 'image/webp,image/*,*/*;q=0.8'</span><br />
<span style="color: cyan;"> }</span><br />
<span style="color: cyan;"> })</span><br />
<span style="color: cyan;"> .then(function (response) {</span><br />
<span style="color: cyan;"> var blob = new Blob(</span><br />
<span style="color: cyan;"> [response.data],</span><br />
<span style="color: cyan;"> {type: response.headers('Content-Type')}</span><br />
<span style="color: cyan;"> );</span><br />
<span style="color: cyan;"> $scope.objectURL = URL.createObjectURL(blob);</span><br />
<span style="color: cyan;"> });</span><br />
<b>});</b><br />
}<br />
});<br />
<br />
The blue text is the core of the directive, pipeling the securely delivered image data into an HTML objectURL. You can<a href="https://thinkster.io/interceptors" target="_blank"> attach headers easily using an injected interceptor</a> to all $http requests..<a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL#Browser_compatibility" target="_blank"> This wont work on older browsers not supporting the objectURL standard</a>.Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-15447197957465273752017-02-14T06:19:00.004-08:002017-02-14T06:36:17.271-08:00A working combination of grails 3, geoDB & hibernate spatial.Ever since the move to grails 3 - dealing with plugins (for me) has become a pain. Almost all the documentation out there is for the now legacy 2.X branches. Makes me consider just using basic servlets like jetty or tomcat embedded. Anyway!<br />
<br />
Hibernate spatial is helpful ORM when working with the Java Topology Suite - and since hibernate 5 is now worked into the core project.<br />
<br />
To setup your grails project to use hibernate spatial - and for grails to successfully select the type of the JTS Geometry derviatives when defined in a domain class - you'll need to make sure all your hibernate versions stack up right to avoid no class data found or no class def found, or java.lang.NoSuchMethodError OR cannot resolve name errrors - all symptoms of mismatch java versions and/or class/interface definitions.<br />
<br />
in your build gradle file:<br />
(I happen to be using the latest grails 3.2.6)<br />
<br />
* Make sure you are using the hibernate5 grails plugin - atleast version 6.0.0<br />
* Make sure the version of hibernate-core & hibernate-ehcache & hibernate-spatial matches - atleast version 5.1.0 - but crucially - not greater than 5.2.0 - 5.2.0+ is currently incompatible with grails.<br />
* Make sure you are using geoDB version 0.7 (for local testing) - but not version 0.8 (incompatible from my testing)<br />
<br />
* In your bootstrap, make sure to upgrade the h2 database to contain goeDB features:<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> try {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> def sql = new Sql(dataSource)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> sql.executeUpdate('CREATE ALIAS InitGeoDB for "geodb.GeoDB.InitGeoDB"')</span><br />
<span style="font-family: Courier New, Courier, monospace;"> sql.executeUpdate('CALL InitGeoDB()')</span><br />
<span style="font-family: Courier New, Courier, monospace;"> } catch (java.sql.SQLException e) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> log.debug('', e)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<br />
And thats it. After changing the plugins, make sure to do a gradle clean, and you should be able to correctly map classes like:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">import com.vividsolutions.jts.geom.Geometry</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">import com.vividsolutions.jts.geom.GeometryFactory</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">class Feature {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> UUID id</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> double lat</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> double lon</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b><br /></b></span>
<span style="font-family: "courier new" , "courier" , monospace;"><b> Geometry geometry</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> def beforeInsert( ) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> if ( lat!=null && lon!=null && geometry == null) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> geometry = (new GeometryFactory()).createPoint( new Coordinate(lon, lat ))</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> static mapping = {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> id(generator: "uuid2", type: "uuid-binary") // H2 & Postgres</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">}</span>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com4tag:blogger.com,1999:blog-3770980579048852972.post-64795796781290713432016-12-20T11:20:00.002-08:002017-02-14T06:34:39.200-08:00Grails 3 Push Notifications: Spring Web Sockets, AngularJS, JWT.Apparantly its not very common to want to combine these sets of technologies. But I did, and I wanted to share some of the tips I found, and hopefully combine the knowledge into one place. There are a bunch of different sources that will help you solve several problems you may encounter.<br />
<br />
<h3>
The use-case</h3>
<br />
I wanted to be able to have my API server push updates to my web-based angular app as interesting things took place. The most common use is that someone else posted new messages in a chat thread - but it can also happen when someone else updates a shared resource or other collaboration. Or perhaps even to populate new items into a news feed without needing a page refresh.<br />
<br />
To "push" data to my angular app, I knew I was going to need sockets. Grails 3 has updated spring support that allows for the use of spring websockets - which use the STOMP protocol & SockJS. SockJS is a library that abstracts away a lot of the lower level websocket management in browsers and allows fallbacks to long polling if needed. STOMP makes it easy to create per-user topics and other utility methods.<br />
<br />
The websocket pattern integrates really nicely with grails reactor integration. As controllers recieve updates from other clients, events can be raised to push notifications to interested listeners.<br />
<h3>
<br />Starting Out</h3>
<br />
Understanding the basics of web sockets are outside the scope of this post - but spring has made <a href="https://spring.io/guides/gs/messaging-stomp-websocket/" target="_blank">integrating web sockets over STOMP easy</a> in recent versions.<br />
<br />
There are a couple good sources for starting out. There is an <a href="https://github.com/zyro23/grails-spring-websocket" target="_blank">up-to-date grails 3 plugin</a> that allows you to use socket topics easily from within existing controllers.<br />
<br />
Defining a controller that takes incoming chat requests over socket is easy.<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #cc7832;">class </span>ChatController {
<span style="color: #cc7832;">def </span><span style="color: #9876aa;">springSecurityService</span><span style="color: #9876aa;"> </span><span style="color: #cc7832;">def </span><span style="color: #9876aa;">messagingService</span><span style="color: #9876aa;">
</span><span style="color: #9876aa;"> </span><span style="color: #cc7832;">def </span><span style="color: #9876aa;">index </span>= {}
<span style="color: #bbb529;">@ControllerMethod</span><span style="color: #bbb529;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #bbb529;"> @MessageMapping</span>(<span style="color: #6a8759;">"/incchat"</span>)
<span style="color: #bbb529;">@PreAuthorize</span>(<span style="color: #6a8759;">"hasRole('ROLE_USER')"</span>)
<span style="color: #bbb529;">@SendToUser</span>(<span style="color: #6a8759;">"/queue/incchat"</span>)
<span style="color: #629755; font-style: italic;">/**</span><span style="color: #629755; font-style: italic;"> * Take a JSON input and parse from object</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #629755; font-style: italic;"> * id: threadid, text: messageText</span><span style="color: #629755; font-style: italic;"> */</span><span style="color: #629755; font-style: italic;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"> String doChat(String jsonIn, Principal principal) {</pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"> ...</pre>
<br />
<br />
Defining a listener to updates from other requests as a service is easy.<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #bbb529;">@Consumer</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #cc7832;">class </span>WebChatService {
SimpMessagingTemplate <span style="color: #9876aa;">brokerMessagingTemplate</span><span style="color: #9876aa;">
</span><span style="color: #9876aa;"> </span><span style="color: #bbb529;">@Selector</span>(<span style="color: #6a8759;">'message.received.chat'</span>)
<span style="color: #cc7832;">void </span>sendChatToUser(WebChatTarget target) {
String data = <span style="color: #e8bf6a; font-weight: bold;">[</span><span style="color: #6a8759;">message</span>: target.<span style="color: #9876aa;">message</span>, <span style="color: #6a8759;">thread</span>: target.<span style="color: #9876aa;">messageThread</span><span style="color: #e8bf6a; font-weight: bold;">] </span><span style="color: #cc7832;">as </span>JSON
<span style="color: #9876aa;">brokerMessagingTemplate</span>.convertAndSendToUser(target.<span style="color: #9876aa;">username</span>, <span style="color: #6a8759;">"/queue/chat"</span>, data)
}
}</pre>
<br />
SOCKJS allows http websocket upgrades - so make sure to use a http:// base url if your sockets are using sockjs. Use ws:// otherwise. Make sure to allow origins that would allow your angular app to connect if its running on a different host.<br />
<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;">@Override</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #cc7832;">public void </span>registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint(<span style="color: #6a8759;">"/stomp"</span>)
.<span style="color: #9876aa;">setAllowedOrigins</span>(<span style="color: #6a8759;">"*"</span>)
.withSockJS() <span style="color: grey;">//SOCKJS requires http connection, other use ws://</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: grey;"></span> .<span style="color: #9876aa;">setSessionCookieNeeded</span>(<span style="color: #cc7832;">false</span>);
}</pre>
<div>
<br /></div>
<h3>
Then it gets tricky.</h3>
<br />
Everything works amazingly up to this point if you are using session based authentication. I'm not doing that in the <a href="http://yolobe.com/" target="_blank">Yolobe </a>stack. Our api is secured via stateless JWT to make mobile integration easier -you just need a couple services and an http interceptor to have angular send the correct headers every time. But what about Web sockets - can you send the right headers to have JWT work on websocket? <a href="https://github.com/sockjs/sockjs-client/issues/196" target="_blank">Kind of</a>.<br />
<br />
Spring sockets was recently updated to <a href="https://github.com/spring-projects/spring-framework/blob/master/src/asciidoc/web-websocket.adoc#token-based-authentication" target="_blank">allow JWT type tokens</a>. The provided example will allow basic JWT authentication to work. But user-based topics and queues will stop functionining as the principal user is missing from the session. Youll have to inject it on every call.<br />
<br />
<h2>
A full solution</h2>
<br />
You'll need to create a pretty extensive configuration bean to integrate spring websocket and spring security rest:<br />
<br />
Your configuration bean<br />
<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;">webSocketConfig(YolobeWebSocketConfigurationBean) {
<span style="color: #9876aa;">jwtService </span>= ref(<span style="color: #6a8759;">"jwtService"</span>)
}</pre>
<br />
Will need to parse JWT tokens on each request (inside <span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "courier new"; font-size: 9pt;">configureClientInboundChannel</span> )- assign the principal to the session/message, then forward the request to a new principal-aware user registry.<br />
<br />
<h4>
Configuration Bean</h4>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;">@Configuration</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;">@EnableWebSocketMessageBroker</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;">@Order</span>(<span style="color: #9876aa; font-style: italic;">HIGHEST_PRECEDENCE</span>)
<span style="color: #cc7832;">public class </span>YolobeWebSocketConfigurationBean <span style="color: #cc7832;">extends </span>AbstractWebSocketMessageBrokerConfigurer {
<span style="color: #cc7832;">public </span>DefaultSimpUserRegistry <span style="color: #9876aa;">_userRegistry </span>= <span style="color: #cc7832;">new </span>DefaultSimpUserRegistry();
<span style="color: #cc7832;">public </span>DefaultUserDestinationResolver <span style="color: #9876aa;">_resolver </span>= <span style="color: #cc7832;">new </span>DefaultUserDestinationResolver(<span style="color: #9876aa;">_userRegistry</span>)
JwtService <span style="color: #9876aa;">jwtService</span><span style="color: #9876aa;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"> MessageBrokerRegistry <span style="color: #9876aa; font-size: 9pt;">messageBrokerRegistry</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #9876aa; font-size: 9pt;">
</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #9876aa;"> </span><span style="color: #bbb529;">@Bean</span><span style="color: #bbb529;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"> @Primary</span><span style="color: #bbb529;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #cc7832;"> public </span>SimpUserRegistry userRegistry() {
<span style="color: #cc7832;">return </span><span style="color: #9876aa;">_userRegistry</span>;
}
<span style="color: #bbb529;">@Bean</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"> @Primary</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"></span><span style="color: #cc7832;"> public </span>UserDestinationResolver userDestinationResolver() {
<span style="color: #cc7832;">return </span><span style="color: #9876aa;">_resolver</span>;
}
<span style="color: #cc7832;">def </span><span style="color: #9876aa;">tokenPattern </span>= ~<span style="color: #6a8759;">/Bearer\s(?<token>\S+)/</span><span style="color: #6a8759;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #cc7832;"> private </span>Principal getUser(StompHeaderAccessor accessor) {
String header = accessor.getNativeHeader(<span style="color: #6a8759;">"Authorization"</span>)[<span style="color: #6897bb;">0</span>]
<span style="color: #cc7832;">def </span>matcher = (header =~ <span style="color: #9876aa;">tokenPattern</span>)
<span style="color: #cc7832;">if </span>(matcher.matches()) {
<span style="color: #cc7832;">try </span>{
JWT jwt = <span style="color: #9876aa;">jwtService</span>.parse(matcher.group(<span style="color: #6a8759;">'token'</span>))
<span style="color: #cc7832;">return new </span>SocketPrincipal(<span style="color: #6a8759;">username</span>:jwt.<span style="color: grey;">payload</span>.toJSONObject()[<span style="color: #6a8759;">"sub"</span>]);
}
<span style="color: #cc7832;">catch </span>(JOSEException formatError) {
<span style="color: #cc7832;">throw new </span>Exception(<span style="color: #6a8759;">"Bad Authorization: Bearer - Token"</span>)
}
}
<span style="color: #cc7832;">throw new </span>Exception(<span style="color: #6a8759;">"Bad Authorization: Bearer - Token"</span>)
}
<span style="color: #bbb529;">@Override</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"></span><span style="color: #cc7832;"> public void </span>configureClientInboundChannel(ChannelRegistration registration) {
registration.<span style="color: #9876aa;">setInterceptors</span>(<span style="color: #cc7832;">new </span>ChannelInterceptorAdapter() { <span style="color: grey;">//authenticate user on connection request</span><span style="color: grey;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;">
</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"> @Override</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #cc7832;"> public </span>Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.<span style="color: #9876aa; font-style: italic;">getAccessor</span>(message, StompHeaderAccessor.<span style="color: #cc7832;">class</span>);
Principal yourAuth = getUser(accessor)
<span style="color: #cc7832;">if </span>(accessor.<span style="color: #9876aa;">messageType </span>== SimpMessageType.<span style="color: #9876aa; font-style: italic;">CONNECT</span>) {
YolobeWebSocketConfigurationBean.<span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">_userRegistry</span>.onApplicationEvent(<span style="color: #cc7832;">new </span>SessionConnectedEvent(<span style="color: #cc7832;">this</span>, message, yourAuth));
} <span style="color: #cc7832;">else if </span>(accessor.<span style="color: #9876aa;">messageType </span>== SimpMessageType.<span style="color: #9876aa; font-style: italic;">SUBSCRIBE</span>) {
YolobeWebSocketConfigurationBean.<span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">_userRegistry</span>.onApplicationEvent(<span style="color: #cc7832;">new </span>SessionSubscribeEvent(<span style="color: #cc7832;">this</span>, message, yourAuth));
} <span style="color: #cc7832;">else if </span>(accessor.<span style="color: #9876aa;">messageType </span>== SimpMessageType.<span style="color: #9876aa; font-style: italic;">UNSUBSCRIBE</span>) {
YolobeWebSocketConfigurationBean.<span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">_userRegistry</span>.onApplicationEvent(<span style="color: #cc7832;">new </span>SessionUnsubscribeEvent(<span style="color: #cc7832;">this</span>, message, yourAuth));
} <span style="color: #cc7832;">else if </span>(accessor.<span style="color: #9876aa;">messageType </span>== SimpMessageType.<span style="color: #9876aa; font-style: italic;">DISCONNECT</span>) {
YolobeWebSocketConfigurationBean.<span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">_userRegistry</span>.onApplicationEvent(<span style="color: #cc7832;">new </span>SessionDisconnectEvent(<span style="color: #cc7832;">this</span>, message, accessor.<span style="color: #9876aa;">sessionId</span>, CloseStatus.<span style="color: #9876aa; font-style: italic;">NORMAL</span>));
}
accessor.<span style="color: #9876aa;">setUser</span>(yourAuth);
accessor.<span style="color: #9876aa;">setLeaveMutable</span>(<span style="color: #cc7832;">true</span>);
<span style="color: #cc7832;">return </span>MessageBuilder.<span style="color: #9876aa; font-style: italic;">createMessage</span>(message.<span style="color: #9876aa;">payload</span>, accessor.<span style="color: #9876aa;">messageHeaders</span>)
}
});
}
<span style="color: #bbb529;">@Override</span><span style="color: #bbb529;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #cc7832;"> public void </span>configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry) {
messageBrokerRegistry.enableSimpleBroker(<span style="color: #6a8759;">"/queue"</span>, <span style="color: #6a8759;">"/topic"</span>);
messageBrokerRegistry.<span style="color: #9876aa;">setUserDestinationPrefix</span>(<span style="color: #6a8759;">"/user"</span>)
messageBrokerRegistry.<span style="color: #9876aa;">setApplicationDestinationPrefixes</span>(<span style="color: #6a8759;">"/app"</span>);
<span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">messageBrokerRegistry </span>= messageBrokerRegistry;
}
<span style="color: #bbb529;">@Override</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"></span><span style="color: #cc7832;"> public void </span>registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint(<span style="color: #6a8759;">"/stomp"</span>)
.<span style="color: #9876aa;">setAllowedOrigins</span>(<span style="color: #6a8759;">"*"</span>)
.withSockJS() <span style="color: grey;">//SOCKJS requires http connection, other use ws://</span><span style="color: grey;"> </span>.<span style="color: #9876aa;">setSessionCookieNeeded</span>(<span style="color: #cc7832;">false</span>);
}
<span style="color: #bbb529;">@Bean</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "Courier New"; font-size: 9pt;"><span style="color: #bbb529;"></span><span style="color: #cc7832;"> public </span>GrailsSimpAnnotationMethodMessageHandler grailsSimpAnnotationMethodMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel, SimpMessagingTemplate brokerMessagingTemplate) {
GrailsSimpAnnotationMethodMessageHandler handler = <span style="color: #cc7832;">new </span>GrailsSimpAnnotationMethodMessageHandler(clientInboundChannel, clientOutboundChannel, brokerMessagingTemplate);
handler.<span style="color: #9876aa;">setDestinationPrefixes</span>([<span style="color: #6a8759;">"/app"</span>,<span style="color: #6a8759;">"/queue"</span>,<span style="color: #6a8759;">"/topic"</span>]);
<span style="color: #cc7832;">return </span>((GrailsSimpAnnotationMethodMessageHandler) (handler));
}
}</pre>
<br />
Client side<br />
<br />
I used <a href="https://github.com/komushi/ng-stomp" target="_blank">this fork of ng-stomp</a> that allows multiple socket connections and disconnection notifications. here is some ugly sample code that shows what is possible. Credentials.decorate() is an angular service that holds stored credentials (persisted to localstorage) and creates a header map.<br />
<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="background-color: #344134;">decorate</span> : <span style="color: #cc7832; font-weight: bold;">function</span>() {
<span style="color: #cc7832; font-weight: bold;">return </span>{ <span style="color: #9876aa;">'Authorization'</span>: <span style="color: #cc7832; font-weight: bold;">this</span>.<span style="color: #ffc66d;">getType</span>() + <span style="color: #6a8759;">" " </span>+ <span style="color: #cc7832; font-weight: bold;">this</span>.<span style="color: #ffc66d;">getToken</span>() }<span style="color: #cc7832;">;</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;">}</pre>
<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="background-color: #344134;">$stomp</span>
.connect(<span style="color: #6a8759;">"messaging"</span><span style="color: #cc7832;">, </span>platformApiEndpoint.<span style="color: #9876aa;">host</span>+<span style="color: #6a8759;">'/stomp'</span><span style="color: #cc7832;">, </span>Credentials.<span style="color: #ffc66d;">decorate</span>() )
.then(<span style="color: #cc7832; font-weight: bold;">function </span>(frame) {
<span style="color: #9876aa;">console</span>.<span style="color: #ffc66d;">log</span>(frame)<span style="color: #cc7832;">;</span><span style="color: #cc7832;"> </span><span style="color: grey;">// notify callback function</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: grey;"> </span><span style="color: #cc7832; font-weight: bold;">var </span><span style="color: #ffc66d;">showResponse </span>= <span style="color: #cc7832; font-weight: bold;">function </span>(res) {
<span style="color: #9876aa;">console</span>.<span style="color: #ffc66d;">log</span>(<span style="color: #6a8759;">"RESPONSE! OMG"</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">JSON</span>.<span style="color: #ffc66d;">stringify</span>(<span style="color: #9876aa;">JSON</span>.<span style="color: #ffc66d;">parse</span>(res.<span style="color: #9876aa;">body</span>)))<span style="color: #cc7832;">;</span><span style="color: #cc7832;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"> }<span style="color: #cc7832;">;</span><span style="color: #cc7832;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="background-color: #344134;"> $stomp</span>.subscribe(<span style="color: #6a8759;">"messaging"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">'/user/queue/chat'</span><span style="color: #cc7832;">, </span>Credentials.<span style="color: #ffc66d;">decorate</span>()).then(<span style="color: #cc7832; font-weight: bold;">null</span><span style="color: #cc7832;">, </span><span style="color: #cc7832; font-weight: bold;">null</span><span style="color: #cc7832;">, </span><span style="color: #ffc66d;">showResponse</span>)<span style="color: #cc7832;">;</span><span style="color: #cc7832;">
</span><span style="color: #cc7832;"> </span>$timeout(<span style="color: #cc7832; font-weight: bold;">function</span>() { <span style="color: grey;">//example sending a message to topic</span><span style="color: grey;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="background-color: #344134;"> $stomp</span>.<span style="color: #ffc66d;">send</span>(<span style="color: #6a8759;">"messaging"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">'/app/incchat'</span><span style="color: #cc7832;">, </span>{
<span style="color: #9876aa;">id</span>: $scope.<span style="color: #9876aa;">threadId</span><span style="color: #cc7832;">,</span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">text</span>: <span style="color: #6a8759;">"connected VIA SOCK"</span><span style="color: #6a8759;"> </span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"> }<span style="color: #cc7832;">, </span>Credentials.<span style="color: #ffc66d;">decorate</span>() )<span style="color: #cc7832;">;</span><span style="color: #cc7832;">
</span><span style="color: #cc7832;"> </span>}<span style="color: #cc7832;">, </span><span style="color: #6897bb;">1000</span>)<span style="color: #cc7832;">;</span></pre>
<br />Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com3tag:blogger.com,1999:blog-3770980579048852972.post-33919354301551071912016-07-26T11:38:00.002-07:002017-02-14T06:35:03.069-08:00Urlmappings break weirdly with GrailsEver started your grails application up and recieved a vomit of stacktraces complaining about being unable to set the URLMappings Bean?<br />
<br />
example:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">Error creating bean with name 'grailsUrlMappingsHolder'</span><br />
<br />
This is generally the result of the url mappings failing to "compile". Check that all your paths start with a "/" slash and any closures are correctly closed.Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-61917886823620592052016-06-08T12:08:00.001-07:002016-06-08T12:08:39.713-07:00Intellij and Grails 3 Command FailureFile this one under "doh, thats obvious" but I completely spaced out as to why my Intellij commands to generate new grails resources - like creating a new taglib<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGilEky4CKHaZmk6-WCYK6vZnJQra68_QIu1HWbhpiHCx6oEMjXOjkM0xTr_12cqZtN8dLu7AH_0WHUbJY1-kggkV_2gY-C9F9VwfLXzhHjw_hllMQXnIVKdzgXYq6VoqeoNwTMGP7pr4/s1600/2016-06-08.png" imageanchor="1"><img border="0" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGilEky4CKHaZmk6-WCYK6vZnJQra68_QIu1HWbhpiHCx6oEMjXOjkM0xTr_12cqZtN8dLu7AH_0WHUbJY1-kggkV_2gY-C9F9VwfLXzhHjw_hllMQXnIVKdzgXYq6VoqeoNwTMGP7pr4/s320/2016-06-08.png" width="320" /></a><br />
<br />
Would fail with an error like:<br />
<br />
<span style="background-color: #666666; color: #eeeeee; font-family: "courier new" , "courier" , monospace;">"C:\Program Files\Java\jdk1.8.0_45\bin\java" -XX:+TieredCompilation [...] \lib\grails-rt.jar" org.grails.cli.GrailsCli create-taglib <className> Error |</span><br />
<span style="background-color: #666666; color: #eeeeee; font-family: "courier new" , "courier" , monospace;">Command [create-taglib] error: Profile [org.grails.profiles:base:3.1.6] declares and invalid dependency on parent profile [org.grails.profiles:base:3.1.6] (Use --stacktrace to see the full trace)</span><br />
<br />
<i>The solution</i> is to ensure the intellij SDK settings are set to the same level of grails SDK specific within your gradle file. I had upgraded to grails 3.1.6 within my build, but IntelliJ uses its own settings to define which grails binary to use for generating classes.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWSvxYr8vFYrcEx4peNTQlcyCMsKf4-wble23O9s2kLijToFVT_feC_m9p3oBZAYvtTPclg45SM1wTf0TPeYdIO-qpie2UjU_JCX-7P9mz4A2qE-XxUYhtv_5-R-RPHROsm_32g6o7goM/s1600/Capture.PNG" imageanchor="1"><img border="0" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWSvxYr8vFYrcEx4peNTQlcyCMsKf4-wble23O9s2kLijToFVT_feC_m9p3oBZAYvtTPclg45SM1wTf0TPeYdIO-qpie2UjU_JCX-7P9mz4A2qE-XxUYhtv_5-R-RPHROsm_32g6o7goM/s640/Capture.PNG" width="640" /></a><br />
<div>
<br /></div>
<div>
(Notice how Intellij still thinks my project is using 3.1.8 although I had downgraded to 3.1.6 - another curious feature that may crop up later)</div>
Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-80437212335791882832016-04-15T09:23:00.004-07:002016-07-26T11:39:03.197-07:00Switching Environments in Grails 3 BootStrapOver the iterations of grails, the best method to do this has changed. But if you want to execute grails code only within a specific environment in grails 3, you can use the static methods on the <span style="font-family: Courier New, Courier, monospace;">grails.util.Environment</span> class.<br />
<br />
Eg:<br />
<br />
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #cc7832;">switch </span>(Environment.<span style="color: #9876aa; font-style: italic;">getCurrent</span>()) {
<span style="color: #cc7832;">case </span>Environment.<span style="color: #9876aa; font-style: italic;">DEVELOPMENT</span>:
<span style="color: #6897bb;">20</span>.times {
//do some thing
}
<span style="color: #cc7832;">break</span>;
<span style="color: #cc7832;">case </span>Environment.<span style="color: #9876aa; font-style: italic;">PRODUCTION</span>:
<span style="color: #6897bb;">2</span>.times {
<span style="font-size: 9pt;">//do some other thing</span>
}
<span style="color: #cc7832;">break</span>;
}</pre>
Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-54895155866085934912016-03-16T08:45:00.001-07:002016-03-16T08:50:30.348-07:00Gmail Oauth2 with Spring and JavaMail with Grails<h3>
Setting up Gmail Email Sending with Grails (2+, 3+) Service</h3>
Most Web applications will send some sort of email to users. Lost passwords, invitations to join. Its a good way to reach professional users. Not so great for young people (they never check them) but in order to send emails, you have to get the connection right. I didn';t build a particular groovy way, I used java code within a grails service to make a simple method available to controllers and other serivces:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">interface EmailService {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> /** </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> * Send a set of emails and collect the results </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> * @param addresses list of addresses to send to</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> * @param subject subject of email </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> * @param text body of email </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> * @return a list of message IDs returned from the sending service </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> */ </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> def send(List<String> addresses, String subject, String text)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> /** </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> * Setup any needed state from config files etc. </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> */ </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> void initialize()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">}</span><br />
<br />
Most of the default grails <a href="http://stackoverflow.com/questions/2016190/how-to-configure-spring-javamailsenderimpl-for-gmail" target="_blank">java mail setup questions</a> (I'm using <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mail.html" target="_blank">spring mail with grails</a> because its mostly already on the classpath) you will find involve turning off enhanced seucirty in your gmail account in order to get around the error message you will recieve on startup:<br />
<br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbvO</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534-5.7.14 SC261Te39VZ5jtNBz2mvwNtIGtZLxYulCRb8D2u6rGTAg69U2-tQsPDzI1YPgWUbVo1ZQm</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534-5.7.14 MlSxYEJHBzyTk-tQKy-6GN5HACShag4XcqNYlxbyWHYvMyMICSTPuwRFzM_Rn2kUOKLcoY</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534-5.7.14 hcEmEC6i4DrvVh4h8KTTdK1VxgyDwD6QzfDxWgUa0vM7ZcLRfIURv1CThW4B0G5XvVgG3a</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534-5.7.14 t-wJ_9P1uU8YoJK2c-QbrhZe2H9qo> Please log in via your web browser and</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534-5.7.14 then try again.</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534-5.7.14 Learn more at</span><br />
<span style="color: red; font-family: "courier new" , "courier" , monospace; font-size: x-small;">534 5.7.14 https://support.google.com/mail/answer/78754 h24sm11972231ioi.17 - gsmtp</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx3NUPRnMjDzh996Tm_weKiritlBQu9EIfsyyqD3aKitAmnwMfbrS1ZXarywBxhXiYdsSgsjxaICxHHD_dTEM1jNNhO6RM0WIH6fh-u5mcGzxG1A4GJtkYeZ7Qs1h85Qta15TeFMxWj9I/s1600/Gmail+Error.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx3NUPRnMjDzh996Tm_weKiritlBQu9EIfsyyqD3aKitAmnwMfbrS1ZXarywBxhXiYdsSgsjxaICxHHD_dTEM1jNNhO6RM0WIH6fh-u5mcGzxG1A4GJtkYeZ7Qs1h85Qta15TeFMxWj9I/s640/Gmail+Error.PNG" width="467" /></a></div>
<blockquote class="tr_bq" style="clear: both; text-align: center;">
Someone just tried to sign in to your Google Account someone@gmailaddress.com from an app that doesn't meet modern security standards." - Oh great, here goes my day</blockquote>
<br />
This cryptic error results from using a setup like (in resources.groovy):<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">emailService(GmailEmailService) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> template=ref("templateMessage")</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> mailSender=ref("mailSender")</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">templateMessage(SimpleMailMessage) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> from="email@sendergmail.com"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">mailSender(JavaMailSenderImpl) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> host="smtp.gmail.com"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> port=587</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> protocol="smtp"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> username="</span><span style="font-family: "courier new" , "courier" , monospace;">email@sendergmail.com</span><span style="font-family: "courier new" , "courier" , monospace;">"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> password="yourpassword"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> javaMailProperties = [</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "mail.transport.protocol" : "smtp",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "mail.smtp.auth" : true,</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "mail.smtp.starttls.enable" : "true",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "mail.smtp.quitwait" : true,</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "mail.debug" : true</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> ]</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<br />
But what if you wanted to use enhanced security? Well available in the more recent versions of <a href="https://java.net/projects/javamail/pages/OAuth2" target="_blank">JavaMail is support of Oauth2</a>.<br />
<br />
To begin with though, you are going to need to follow the <a href="https://developers.google.com/identity/protocols/OAuth2?csw=1" target="_blank">google guidelines </a>here to recieve api credentials for your webserver. You should end up with a set of JSON credentials for your s<a href="https://developers.google.com/identity/protocols/OAuth2ServiceAccount" target="_blank">ervice account</a>. :<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "type": "service_account",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "project_id": "someid",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "private_key_id": "SOMEID",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "private_key": "YOURKEY",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "client_email": "someemail",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "client_id": "someid",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "auth_uri": "https://accounts.google.com/o/oauth2/auth",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "token_uri": "https://accounts.google.com/o/oauth2/token",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> "client_x509_cert_url": "someurl"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">}</span><br />
<br />
There is a branch here. You can choose to give the service account Delegate domain-wide authority (which I have done) or leave it at user-interactive mode. Since I dont want to have to ask my own company user account for permissions (which makes sense for a back-end webservice making sending email from only one user), I chose to upgrade the account to domain-wide, which can be done in the console.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjNDEhR7d_iSuJx5r9rAcMsxNbMSyCk3HQFGTtDbsuAo4GEabnIz7QGx7ffxIsp6phrt-tkqIPsdDILrYUIgojfEutyvb8H3o9Y5K07aylTh9lYOpyQlsGCWx-LPbYwT5yI6zv5kI-7xA/s1600/serviceAccount.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjNDEhR7d_iSuJx5r9rAcMsxNbMSyCk3HQFGTtDbsuAo4GEabnIz7QGx7ffxIsp6phrt-tkqIPsdDILrYUIgojfEutyvb8H3o9Y5K07aylTh9lYOpyQlsGCWx-LPbYwT5yI6zv5kI-7xA/s640/serviceAccount.PNG" width="640" /></a></div>
<blockquote class="tr_bq">
Adding Domain Wide Delegation means no prompts, but using an additional secret file</blockquote>
<br />
We will need to use these credentials to create oauth tokens that can be sent with each senmail request. To create these tokens (oauthtoken) you can use the <a href="https://github.com/google/google-api-java-client#Service_Accounts" target="_blank">Google Java API</a>. (at the time of writing, the latest central maven available was: 'com.google.api-client:google-api-client:1.21.0') . <a href="https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests" target="_blank">Google has a guide available</a> - but essentially you will be using the builder to setup a trusted account.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(authFile)).createScoped(Collections.singleton("https://www.googleapis.com/auth/gmail.send")) //for SMTP access only</span><br />
<br />
Then email setup can be pretty easy. A lot of the boiler plate of creating a <span style="font-family: "courier new" , "courier" , monospace;">javax.mail.transport</span> can be made easier by using some J<a href="https://github.com/google/gmail-oauth2-tools" target="_blank">ava Code provided by Google</a> itself.<br />
<br />
If like us, you were using google apps accounts - there is additional information required. As you may get responses like:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">DEBUG SMTP: SASL: no response</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">DEBUG SMTP: SASL authentication failed</span><br />
<div>
<br /></div>
<div>
<h3>
Things to check:</h3>
* You have allowed the client id access to the same scope you are requesting (<a href="https://developers.google.com/gmail/api/auth/scopes" target="_blank">Google Scopes listing</a>)<br />
* You are creating your Google credential and access token with a user to impersonate:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(authFile))</span><span style="font-family: "courier new" , "courier" , monospace;"> .createScoped(Collections.singleton("https://www.googleapis.com/auth/gmail.send"))</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">credential.serviceAccountUser = "someuser@yourdomain.com" //important</span><br />
<br />
<h3>
Final Thoughts</h3>
</div>
After going through all of this trouble, I ended up switching to using the google api client and the gmail api client for java, while still using JavaMail for convience (<a href="https://developers.google.com/gmail/api/guides/sending" target="_blank">google has a great guide here</a>)<br />
<br />
By the way, I reccomend <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html" target="_blank">using scheduling</a> with retries in order to make sure your customers recieve their emails. It can be really annoying when an app fails to send that crucial forgot password email - they will often leave and not come back.<br />
<br />
Turns out doing it the right way is a bit more involved - but worth it.<b> One day your email's wont stop randomly working when google finally axes support for the basic authentication.</b><br />
<br />Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-77652535740472092002016-02-17T13:19:00.002-08:002016-03-16T08:50:46.110-07:00Grails3 and Spring Security<blockquote class="tr_bq">
ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[grailsDispatcherServlet] - Servlet.service() for servlet grailsDispatcherServlet threw exception<br />
java.lang.IllegalStateException: There was a problem retrieving the current GrailsWebRequest. This usually indicates a filter ordering issue (the 'springSecurityFilterChain' filter-mapping element must be positioned after the 'grailsWebRequest' element when using @Secured annotations).</blockquote>
<br />
This issue was bugging me on a new grails3 and spring security web application. It turns out there are more than few active bugs on this issue. I filed my own:<br />
<br />
https://github.com/grails-plugins/grails-spring-security-core/issues/412<br />
<br />
After a week of noodling on the issue and trying various leads - the root cause seems to be spring security is delegating to the embedded tomcat error pages for routes only covered by static rule mappings (while your application is still using annotations). Instead, the plugin should correctly delegate to your mapped errors pages within the grails system. Included in the above post is my work around, which I will repeat here. This will cause all errors encountered within static routes to use the correct mappings.<br />
<br />
You must setup an error controller:<br />
<br />
<blockquote class="tr_bq">
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #cc7832;">class </span>ErrorController {
<span style="color: #cc7832;">def </span>error() {
render <span style="color: #6a8759;">view</span>: <span style="color: #6a8759;">'error'</span><span style="color: #6a8759;"> </span>}
<span style="color: #cc7832;">def </span>invalid() {
render <span style="color: #6a8759;">view</span>: <span style="color: #6a8759;">'error'</span><span style="color: #6a8759;"> </span>}
<span style="color: #cc7832;">def </span>denied() {
render <span style="color: #6a8759;">view</span>: <span style="color: #6a8759;">'denied'</span><span style="color: #6a8759;"> </span>}
<span style="color: #cc7832;">def </span>notFound() {
render <span style="color: #6a8759;">view</span>: <span style="color: #6a8759;">'notFound'</span><span style="color: #6a8759;"> </span>}
}</pre>
</blockquote>
<br />
And use this controller within your URL mappings:<br />
<br />
<blockquote class="tr_bq">
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="color: #6a8759;">"400"</span>(<span style="color: #6a8759;">controller</span>: <span style="color: #6a8759;">"error"</span>, <span style="color: #6a8759;">action</span>: <span style="color: #6a8759;">"invalid"</span>)
<span style="color: #6a8759;">"500"</span>(<span style="color: #6a8759;">controller</span>: <span style="color: #6a8759;">"error"</span>, <span style="color: #6a8759;">action</span>: <span style="color: #6a8759;">"denied"</span>)
<span style="color: #6a8759;">"403"</span>(<span style="color: #6a8759;">controller</span>: <span style="color: #6a8759;">"error"</span>, <span style="color: #6a8759;">action</span>: <span style="color: #6a8759;">"denied"</span>)
<span style="color: #6a8759;">"404"</span>(<span style="color: #6a8759;">controller</span>: <span style="color: #6a8759;">"error"</span>, <span style="color: #6a8759;">action</span>: <span style="color: #6a8759;">"notFound"</span>)</pre>
</blockquote>
<br />
<br />
And finally disable the spring-secuurity error page within application.groovy<br />
<br />
<blockquote class="tr_bq">
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><span style="background-color: #344134;">grails</span>.plugin.springsecurity.<span style="color: grey;">adh</span>.errorPage = <span style="color: #cc7832;">null</span></pre>
</blockquote>
<br />
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-7965167492994587872016-02-17T13:14:00.005-08:002016-03-16T08:51:02.173-07:00FlowJs Upload Reciever for GrailsI chose flow.js (https://github.com/flowjs/flow.js) for my client side file uploading due to its fault tollerance. I use using the ng-flow (https://github.com/flowjs/ng-flow/) angular library that builds an excellent set of directives around the framework. The one downside is the lack of documentation on what it takes to build a server side compliment to this. The particular beckend I am using is Grails3 based (although this would work for earlier versions of grails with little modification).<br />
<br />
I built a service that can be plugged into multiple controllers to achieve the effect. While I wont publish the whole service, here is a skelleton that should be enough to get you started.<br />
<br />
<a name='more'></a><br />
<br />
A Grails Flow Upload Service (replace config values with your own)<br />
<blockquote class="tr_bq">
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Courier New'; font-size: 9.0pt;"><pre style="font-family: 'Courier New'; font-size: 9pt;"><span style="color: #629755; font-style: italic;">/**</span><span style="color: #629755; font-style: italic;"> * A Simple service that can handle multipart uploads and recombine them once complete</span><span style="color: #629755; font-style: italic;"> */</span><span style="color: #bbb529;">
@Transactional</span>
<span style="color: #cc7832;">class </span>FlowUploadService {
<span style="color: grey;">//Needed to get config values</span><span style="color: grey;"> </span><span style="color: #cc7832;">def </span><span style="color: #9876aa;">grailsApplication</span><span style="color: #9876aa;">
</span><span style="color: #9876aa;"> </span><span style="color: #629755; font-style: italic;">/**</span><span style="color: #629755; font-style: italic;"> * Parse the incoming Flow Upload Chunk Requests</span><span style="color: #629755; font-style: italic;"> * </span><span style="color: #629755; font-style: italic; font-weight: bold;">@param </span><span style="color: #629755; font-style: italic;">params HTTP Request Parameter Map</span><span style="color: #629755; font-style: italic;"> * </span><span style="color: #629755; font-style: italic; font-weight: bold;">@param </span><span style="color: #629755; font-style: italic;">request HTTP Request</span><span style="color: #629755; font-style: italic;"> * </span><span style="color: #629755; font-style: italic; font-weight: bold;">@return</span><span style="color: #629755; font-style: italic; font-weight: bold;"> </span><span style="color: #629755; font-style: italic;">*/</span><span style="color: #629755; font-style: italic;"> </span><span style="color: #cc7832;">def </span>handleFlowPartUpload(params, request) {
<span style="color: #cc7832;">if </span>(!params.flowChunkNumber || !params.flowTotalChunks || !params.flowIdentifier || !params.flowFilename)
<span style="color: #cc7832;">return </span>[<span style="color: #6a8759;">status</span>: <span style="color: #6897bb;">400</span>, <span style="color: #6a8759;">upload</span>: <span style="color: #cc7832;">null</span>, <span style="color: #6a8759;">err</span>: <span style="color: #cc7832;">new </span>Exception(<span style="color: #6a8759;">"invalidParams"</span>)]
<span style="color: #cc7832;">if </span>(request.get) { <span style="color: #629755; font-style: italic;">/** Flow is asking if chunk exists, it might be retrying an upload **/</span><span style="color: #629755; font-style: italic;"> </span>println <span style="color: #6a8759;">"get </span>${params} ${request}<span style="color: #6a8759;">"</span><span style="color: #6a8759;"> </span><span style="color: #cc7832;">def </span>status = checkChunk(params)
<span style="color: #cc7832;">return </span>[<span style="color: #6a8759;">status</span>: status, <span style="color: #6a8759;">upload</span>: <span style="color: #cc7832;">null</span>, <span style="color: #6a8759;">err</span>: <span style="color: #cc7832;">null</span>]
}
<span style="color: #cc7832;">else if </span>(request.post) <span style="color: #629755; font-style: italic;">/** Flow is handling us a chunk to upload **/</span><span style="color: #629755; font-style: italic;"> </span>{
println <span style="color: #6a8759;">"post </span>${params} ${request}<span style="color: #6a8759;">"</span><span style="color: #6a8759;"> </span><span style="color: #cc7832;">def </span>file = request.getFile(<span style="color: #6a8759;">'file'</span>)
<span style="color: #cc7832;">if</span>(file.empty) {
<span style="color: #cc7832;">return </span>[<span style="color: #6a8759;">status</span>: <span style="color: #6897bb;">400</span>, <span style="color: #6a8759;">upload</span>: <span style="color: #cc7832;">null</span>, <span style="color: #6a8759;">err </span>: <span style="color: #cc7832;">new </span>Exception(<span style="color: #6a8759;">'noFile'</span>)]
} <span style="color: #cc7832;">else </span>{
<span style="color: #cc7832;">def </span>finalFile = <span style="color: #cc7832;">null</span><span style="color: #cc7832;"> try </span>{
saveChunk(params, file)
<span style="color: #cc7832;">if </span>(params.<span style="color: grey;">flowChunkNumber </span>== params.<span style="color: grey;">flowTotalChunks</span>) {
finalFile = combineChunks(params, file) <span style="color: #629755; font-style: italic;">/** Handle Final Chunk **/</span><span style="color: #629755; font-style: italic;"> </span>}
}
<span style="color: #cc7832;">catch</span>(Exception e)
{
e.printStackTrace()
<span style="color: #cc7832;">return </span>[<span style="color: #6a8759;">status</span>: <span style="color: #6897bb;">200</span>, <span style="color: #6a8759;">upload</span>: finalFile, <span style="color: #6a8759;">err</span>: e]
}
<span style="color: #cc7832;">return </span>[<span style="color: #6a8759;">status</span>: <span style="color: #6897bb;">200</span>, <span style="color: #6a8759;">upload</span>: finalFile, <span style="color: #6a8759;">err </span>: <span style="color: #cc7832;">null</span>]
}
}
}
<span style="color: #cc7832;">private int </span>checkChunk(params) {
<span style="color: #cc7832;">def </span>destinationPath = <span style="color: #9876aa;">grailsApplication</span>.config.yolobe.upload.temp
<span style="color: #cc7832;">new </span>File(destinationPath).mkdirs() <span style="color: grey;">//</span><span style="color: #a8c023; font-style: italic;">TODO remove for performance</span><span style="color: #a8c023; font-style: italic;"> </span>String output = getHashOfChunk(params)
<span style="color: #cc7832;">def </span>chunkFile = <span style="color: #cc7832;">new </span>File(destinationPath + output)
<span style="color: #cc7832;">return </span>chunkFile.exists()? <span style="color: #6897bb;">200 </span>: <span style="color: #6897bb;">204</span><span style="color: #6897bb;"> </span>}
<span style="color: #cc7832;">private void </span>saveChunk(params, file) {
<span style="color: #cc7832;">def </span>destinationPath = <span style="color: #9876aa;">grailsApplication</span>.config.yolobe.upload.temp + getHashOfChunk(params)
<span style="color: #cc7832;">def </span>finalFile = <span style="color: #cc7832;">new </span>File(destinationPath)
println <span style="color: #6a8759;">"uploading chunk </span>$params.<span style="color: grey;">flowChunkNumber</span><span style="color: #6a8759;"> to </span>$destinationPath<span style="color: #6a8759;">"</span><span style="color: #6a8759;"> </span>file.transferTo(finalFile)
}
<span style="color: #cc7832;">private def </span>combineChunks(params, file) {
<span style="color: grey;">//Determine a final random "Date/(HASH)(UUID)"</span><span style="color: grey;"> </span>String uuid = UUID.<span style="color: #9876aa; font-style: italic;">randomUUID</span>().toString()
String date = <span style="color: #cc7832;">new </span>Date().format(<span style="color: #6a8759;">'M-d-yyyy'</span>)
<span style="color: grey;">/*</span><span style="color: grey;"> def extension = params.flowFilename.lastIndexOf('.').with {</span><span style="color: grey;"> it != -1 ? params.flowFilename[0..<it] : params.flowFilename</span><span style="color: grey;"> } //optionally preserve extension of final file - could be exploited*/</span><span style="color: grey;">
</span><span style="color: grey;"> </span><span style="color: #cc7832;">def </span>fileName = date + File.<span style="color: #9876aa; font-style: italic;">separator </span>+ getHashOfChunk(params) + uuid
<span style="color: #cc7832;">def </span>destinationPath = <span style="color: #9876aa;">grailsApplication</span>.config.yolobe.upload.path + File.<span style="color: #9876aa; font-style: italic;">separator </span>+ fileName
<span style="color: #cc7832;">def </span>chunks = Integer.<span style="color: #9876aa; font-style: italic;">parseInt</span>(params.<span style="color: grey;">flowTotalChunks</span>)
<span style="color: #cc7832;">def </span>finalFile = <span style="color: #cc7832;">new </span>File(destinationPath)
<span style="color: #cc7832;">def </span>fileStream = <span style="color: #cc7832;">new </span>FileOutputStream(finalFile)
println <span style="color: #6a8759;">"combining </span>$params.<span style="color: grey;">flowTotalChunks</span><span style="color: #6a8759;"> chunks to </span>$destinationPath<span style="color: #6a8759;">"</span><span style="color: #6a8759;"> </span><span style="color: #6897bb;">1</span>.upto(chunks) { c ->
<span style="color: #cc7832;">def </span>input = <span style="color: #cc7832;">new </span>FileInputStream(<span style="color: #9876aa;">grailsApplication</span>.config.yolobe.upload.temp + getHashOfChunk(params, c))
fileStream << input
}
fileStream.close()
[<span style="color: #6a8759;">path</span>: destinationPath, <span style="color: #6a8759;">name</span>: fileName]
}
<span style="color: #cc7832;">private </span>String getHashOfChunk(params, chunkNo = <span style="color: #cc7832;">null</span>) {
MessageDigest md = MessageDigest.<span style="color: #9876aa; font-style: italic;">getInstance</span>(<span style="color: #6a8759;">"MD5"</span>)
<span style="color: #cc7832;">byte</span>[] md5sum = md.digest(params.flowIdentifier.getBytes());
String output = String.<span style="color: #9876aa; font-style: italic;">format</span>(<span style="color: #6a8759;">"%032X"</span>, <span style="color: #cc7832;">new </span>BigInteger(<span style="color: #6897bb;">1</span>, md5sum)) + (chunkNo? chunkNo : params.flowChunkNumber);
output
}
}</pre>
</pre>
</blockquote>
Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-59450229576552356442012-10-23T09:42:00.004-07:002012-10-23T09:42:40.810-07:00Simple Scripts for replacing html tagsI recently had to upgrade a tag library we were using. However the latest version had refactored out some of the tags into a separate tag library. I needed to do two things:<br />
<br />
1) Add a reference to the new tag library on each page that had previously been using the base library<br />
2) Change the tags using the old library to update to use the new prefix<br />
<br />
#Add a new line (using a\ option) to the files containing reference to struts-jquery-tags<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">find . -type f \( -iname "*.tag" -or -iname "*.jsp" \) -print -exec sed -i '/struts-jquery-tags\"\s%>/ a\</span><br />
<span style="font-family: Courier New, Courier, monospace;"><%@ taglib prefix=\"sjg\" uri=\"\/struts-jquery-grid-tags\" %>' {} \;</span><br />
<br />
#Find and replace references to the old sj:grid to use the new :sjg:grid<br />
<span style="font-family: Courier New, Courier, monospace;">find . -type f \( -iname "*.tag" -or -iname "*.jsp" \) -print -exec sed -i 's/sj:grid/sjg:grid/g' {} \;</span><br />
<br />
#Finally strip out some redundant information from header tags<br />
<span style="font-family: Courier New, Courier, monospace;">find . -type f \( -iname "*.tag" -or -iname "*.jsp" \) -print -exec sed -i 's/head\suseJqGridPlugin=\"true\"/head/g' {} \;</span><br />
Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-4365926203637684502012-04-25T12:38:00.002-07:002012-04-25T12:38:28.862-07:00Script for fast Mysql DumpThis command will create a file named mysqldum.sql in the current dirrectory:<br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">mysqldump DBNAME --add-drop-table -u USERNAME-p > mysqldump.sql</span><br />
<br />
Remember when going to older versions of mysql to do a search and replace for "USING BTREE" as it is not supported in older mysql installs.<br />
<br />
This file can then be imported via:<br />
<br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> mysql DBNAME -u USERNAME -p < mysqldump.sql </span></div>
<div>
<br /></div>
<div>
This post is mostly for my own information, I keep forgetting the exact order of the arguments!</div>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-83700767914137029012012-03-30T10:27:00.006-07:002012-03-30T10:27:58.930-07:00Flowplayer 201 Error in Commercial BuildOur site had been a long time customer of flowplayer. We have a commercial license. However the person who had purchased said license was no longer at the company and the account was tied to her email address. We didnt get the memo when flowplayer moved its builds to amazon s3.<br />
<br />
Luckily my browser remembered the password to the account site - and I changed the location of the flash builds. However they were still using an older version, 3.2.5-0. After changing the links, the player would load up, but report a 201 error,:<br />
<br />
<span style="background-color: white; color: red; font-family: Consolas, 'Lucida Console', monospace; font-size: 12px; line-height: 12px; white-space: pre-wrap;">201, Unable to load stream or clip file, connection failed, clip: '[Clip] </span>
<br />
<br />
The fix? I needed to download and use the newer commercial builds found on the account page under "Commercial license. v3.2" - that, along with the new javascript libraries associated (3.2.8 at the time of writing) got things going again.<br />
<br />Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-3365284341812766942012-03-26T09:08:00.003-07:002012-03-26T09:08:40.212-07:00Detecting environment for Google Web ToolkitHave a piece of code that you want to run only when running in Hosted Mode? I made a small utility class that uses one method exposed in the Google Web Toolkit API to do just that.<br />
<br />
<br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;">import com.google.gwt.core.client.GWT;</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;">/**</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> * Utility Class to help write debugging code</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> * References to these function could be searched for and removed when deploying to production for increased speed.</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> * @author Jason Lambert</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> *</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> */</span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;">public class Development {</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> </span></span><span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;">public static boolean isDevelopmentEnvironment() { return !GWT.isScript(); } //detect hosted mode</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
Inspired by how Grails handles development, test and production environments, I made this quick static utility function.<br />
<br />
<span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;">GWT.isScript</span> detects if the current environment is running in compiled byte code or not. useful for switching to download local resources when perhaps same origin policy (SOP) prevents your code from downloading data from external databases or servers.<br />Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-51531625781879189692012-03-15T13:06:00.003-07:002012-03-15T13:06:59.866-07:00Tomcat SEVERE: Error listenerStart - Debugging.From my travels across Google, this error is common. In Grails applications it is almost always from a bad datasource.groovy file. This error is the non descriptive string you receive when deploying your servlet (Grails, Spring, Otherwise) to tomcat (probably version 5.5 or higher). The servlet probably runs fine in development mode on your machine. How do we debug it? Read on.<br />
<br />
Lets start by getting more useful error messages from Tomcat.<br />
Tomcat by default uses java.util.logging, which is giving you the basic error messages along the lines of...<br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">DATETIME org.apache.catalina.core.StandardContext startInternal</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">SEVERE: Error listenerStart</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">DATETIME org.apache.catalina.core.StandardContext startInternal</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">SEVERE: Context [/AppName] startup failed due to previous errors</span><br />
<br />
Lets configure tomcat to use log4j, which will produce a much better call stack to debug. Taken from advice <a href="http://tomcat.apache.org/tomcat-7.0-doc/logging.html#Using_Log4j" target="_blank">here</a>, I'll paraphrase [read the source material for a full understanding however].<br />
<br />
<br />
<ol>
<li>Stop Tomcat</li>
<li>Install Log4j on your deployment machine. (package install available for a lot of linux distros)</li>
<li>Download tomcat-juli-adapters.jar and tomcat-juli.jar from the extras section of the tomcat <a href="http://tomcat.apache.org/" target="_blank">website </a>(For version 7.0 that extras page is <a href="http://tomcat.apache.org/download-70.cgi" target="_blank">here</a>) and place them into the $CATALINA_HOME/lib folder</li>
<li>Replace the $CATALINA_HOME/bin/tomcat-juli.jar with that same tomcat-juli.jar you just placed in lib.</li>
<li>Delete $CATALINA_BASE/conf/logging.properties to prevent the old logging system from being used</li>
<li>Start Tomcat</li>
</ol>
<br />
<br />
<div>
In my case, the error stack now looked like the following. As you can see like an idiot I had left the driverClassName to the grails 2.0 default of
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">org.h2.Driver</span> instead of the <span style="font-family: 'Courier New', Courier, monospace;">com.mysql.jdbc.Driver</span> required to connect to a mysql service. Interestingly my eclipse/tomcat development setup was forgiving me and loading the correct driver anyway - be warned!</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">SEVERE: Exception sending context initialized event to listener instance of class org.codehaus.groovy.grails.web.context.GrailsContextLoaderListener</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManagerPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creati$</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.util.concurrent.FutureTask.run(FutureTask.java:166)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.lang.Thread.run(Thread.java:679)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.sp$</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 6 more</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is $</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 6 more</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect];$</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 6 more</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Error while e$</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 6 more</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: org.springframework.jdbc.support.MetaDataAccessException: Error while extracting DatabaseMetaData; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of class 'org.h2.Driver' for connect URL '$</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 6 more</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of class 'org.h2.Driver' for connect URL 'jdbc:mysql://xxx.xx.xx.xx:3306/mydatabasename?autoreconnect=true&zeroDateTimeBehavior=convertToNull'</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at org.apache.commons.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1452)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 6 more</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">Caused by: java.sql.SQLException: No suitable driver</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at java.sql.DriverManager.getDriver(DriverManager.java:279)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> at org.apache.commons.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1437)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ... 8 more</span></div>
</div>
<div>
<br /></div>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com1tag:blogger.com,1999:blog-3770980579048852972.post-45602558307931039322012-02-21T05:07:00.000-08:002012-02-21T12:36:08.100-08:00Searching Multiple Data Sources in GrailsHave you got domain classes mapped to <a href="http://grails.org/doc/2.0.0.RC1/guide/conf.html#multipleDatasources" target="_blank">multiple datasources</a>? The Searchable plugin will mysteriously not be building indexes for domain classes in your secondary datasources by default. The following explains a way to change the behavior.<br />
<br />
This is taken from my personal experience, and information compiled from a helpful thread archived on Grails, found <a href="http://grails.1312388.n4.nabble.com/Searchable-plugin-working-w-Datasources-td2400400.html" target="_blank">here</a>. We are going to need to redefine some beans.<br />
<br />
<span style="font-size: large;">Step 1:</span><br />
Open <b>conf/spring/resources.groovy</b><br />
<b><br /></b><br />
add in the following bean:<br />
<b><br /></b><br />
<span style="font-family: 'Courier New', Courier, monospace;">import org.compass.gps.device.hibernate.HibernateGpsDevice //need this import for new datasource</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">import grails.plugin.searchable.internal.compass.config.SessionFactoryLookup //need this import to get correct sessionfactory class</span><br />
<b><span style="font-family: 'Courier New', Courier, monospace;"><br /></span></b><br />
<span style="font-family: 'Courier New', Courier, monospace;">beans = {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">/* ~ your other beans here */</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">compassGpsDevice(HibernateGpsDevice) { bean -></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>bean.destroyMethod = "stop"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>name = "hibernate"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>sessionFactory = { SessionFactoryLookup sfl -></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>sessionFactory = </span><span style="font-family: 'Courier New', Courier, monospace;">ref('sessionFactory_</span><span style="color: #bf9000; font-family: 'Courier New', Courier, monospace;"><b>datasourceName</b></span><span style="font-family: 'Courier New', Courier, monospace;">') </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}//replace <span style="color: #bf9000;">datasourceName </span>with the name of the datasource as defined in <b>conf/DataSource.groovy</b></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>fetchCount = 5000</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-size: large;">Step 2:</span>
<br />
<span style="font-family: inherit;">You could stop here if this was the only datasource that contained indexable classes, but continue to add each compassGPSdevices for each additional datasource (including the default), making sure they are unique names ie:</span><br />
<span style="font-family: inherit;"><br /></span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">anotherUniquecompassGpsDevice(HibernateGpsDevice) { bean -></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>bean.destroyMethod = "stop"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>name = "unqiueHibernateName"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="font-family: 'Courier New', Courier, monospace;">{ SessionFactoryLookup sfl -></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>sessionFactory = </span><span style="font-family: 'Courier New', Courier, monospace;">ref('sessionFactory_unique</span><span style="font-family: 'Courier New', Courier, monospace;">datasource</span><span style="font-family: 'Courier New', Courier, monospace;">') </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>fetchCount = 5000</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-family: inherit;">then finally we need to combine all of these datasources into one SingleCompassGps for searchable. Add this last bean:</span><br />
<span style="font-family: inherit;"><br /></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">import org.compass.gps.impl.SingleCompassGps</span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">compassGps(SingleCompassGps) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>compass = ref('compass')</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>gpsDevices = [compassGpsDevice, anotherUniqueCompassGpsDevice /* ~add the other sources in here */ ]</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span><br />
<br />
Thanks to the original thread for guidance. Hopefully this compilation of guidance will help someone else save some time. The major additions beyond the original thread are to use the sessionfactorylookup. You can see these objects in SearchableGrailsPlugin.groovy - we are simply re defining them with new datasources!<br />
<br />
<span style="font-family: inherit;"><br /></span><br />
<br />
<span style="font-family: inherit;"><br /></span>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com1tag:blogger.com,1999:blog-3770980579048852972.post-343215615238519672012-02-17T12:45:00.001-08:002012-02-21T11:28:34.570-08:00Undocumented? Grails scaffolding feature. WidgetsPresent in old docs, but weirdly not the latest, you can request a different widget on scaffolded pages for your domain class.<br />
<br />
In the domain's scaffold closure:<br />
<br />
<br />
<pre style="background-color: white; color: #333333; font-size: 13px; line-height: 18px;">def constraints = {
myStringField(widget:'textarea')
}</pre>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com1tag:blogger.com,1999:blog-3770980579048852972.post-87810043424837902552012-02-17T07:58:00.000-08:002012-02-21T11:29:16.088-08:00Make a file list quicklyNeed to write a bunch of filenames to a list (say to build a resources list for a Grails plugin or html page) because you can't include just folders? But you don't want to type them all out individually?<br />
<br />
Open that good old standby, cmd.exe or terminal and navigate to the root folder containing the files.<br />
<br />
<b>[Windows] </b>command: <span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">DIR *.* /s /b > ../yourlistname.LST</span><br />
<br />
<b>[Linux/OSX] </b>command: <span style="font-family: 'Courier New', Courier, monospace;"> <span style="color: #0b5394;">find * . > yourlistname</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span style="color: #0b5394;"><br /></span></span><br />
<span style="font-family: inherit;">You can easily add extensions to the DIR and find commands to find only a particular kind of file (DIR *.js for example) Open in your </span>favorite<span style="font-family: inherit;"> text editor (why not try <a href="http://notepad-plus-plus.org/" target="_blank">notepad</a>++ or <a href="http://macromates.com/" target="_blank">textmate</a>) use some regex-replacing..</span><br />
<span style="font-family: inherit;"><br /></span><br />
<span style="font-family: inherit;">Lets try removing directories from the file list (this could also be done with additional filters in the find commands above):</span><br />
<span style="font-family: inherit;"><br /></span><br />
<br />
<ul>
<li><span style="font-family: inherit;">In notepad++, turn on regular expression searching, and check '</span><u style="font-family: inherit;"><b>mark line</b></u><span style="font-family: inherit;">'</span></li>
<li><b><u>Search for [\w+][\.][\w+]</u></b>, which will bring up every line with a . in it (a valid extension)</li>
<li>Invert the bookmarking with <b><u>Search->Bookmark->Inverse Bookmark</u></b></li>
<li>Finally delete the now bookmarked lines without file extensions by <b><u>Search->Bookmark->Delete Bookmarked Lines</u></b></li>
</ul>
<br />
<span style="font-family: inherit;"><br /></span><br />
<span style="font-family: inherit;">And your done.</span>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-65574021243428515102012-02-16T10:49:00.000-08:002012-02-17T08:01:18.735-08:00Fun fact: Grails GString's require double quotes<b>Will work:</b><br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">def element = "<script type=\"text/javascript\" src=\"${g.resource(dir: 'folder', file: 'file.js')}\"></script>"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">out << element.toString()</span><br />
<br />
<br />
<b>Wont work:</b><br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">def element = '<script type="text/javascript" src="${g.resource(dir: "folder", file: "file.js")}"></script>'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">out << element.toString()</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-family: inherit;">And by "wont work" the groovy expressions in the GString will not be evaluated, they will be delivered literally. Note the double quotes beginning and ending the GString in the first example.</span><br />
<br />
I knew this. Yet I was dissecting why my unit test was failing for over an hour. Turns out junit can't detect silly human error! Since I re-learnt this lesson, I thought I would share it with you, blogosphere!Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-12332421524827189362012-01-20T13:57:00.000-08:002012-01-20T13:58:18.306-08:00Grails Codec file location and extensionIf you are just beginning out in Grails development, the creation of custom codecs can greatly increase productivity. Codecs help you code/decode (funny that) strings. Grails ships with a <a href="http://grails.org/doc/latest/guide/single.html#11.2 Encoding and Decoding Objects" target="_blank">bunch of them already</a>, but when you feel like you want to create you own, greate a groovy class file (extension .groovy), with the name ending in "Codec" in your projects <i>graips-app/utils</i> folder. Remember that if you want to place these files in packages, you will need to <u>create the additional folder structure to match the package structure</u>. A sample codec structure is shown below. This file would be in the folder grails-app/utils/org/mycompany/:<br />
<br />
<br />
package org.mycompany<br />
import /* imports here */<br />
<br />
class SampleCodec{<br />
static encode = { str -><br />
/* Coding data goes here */<br />
}<br />
<br />
static decode = { str -><br />
/* Decoding data goes here */<br />
}<br />
<br />
}<br />
<br />
Once you start your grails app, Spring will inject the methods<b> encodeAsSample()</b> and <b>decodeSample() </b>to the Java.lang.String class.<br />
<br />
Start creating custom hash codes for your passwords now!Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-50417854578347658812012-01-18T14:10:00.000-08:002012-01-19T05:58:22.613-08:00JMX and GrailsDeveloping on windows can sometimes really be a pain. I had found documentation all over the web as to where to place my JVM arguments to make grails accept remote JMX connections while developing in eclipse. Most have you modifying $GRAILS_HOME/bin/grails.bat but I was noticing in <b>Java Visual VM</b> (great tool by the way) that the VM arguments were not changing.<br />
<br />
In one of those 'the answer is simple stupid' moments - some clarity was given to me by this page of documentation: <a href="http://www.objectpartners.com/2009/05/27/eclipse-setup-for-grails-11-development/">http://www.objectpartners.com/2009/05/27/eclipse-setup-for-grails-11-development/</a><br />
<br />
By adding the following string the run configuration for my Grails project:<br />
<br />
-Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=<yourHostIPHere> -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false<br />
[Note this is obviously not a production setting, you will <b>NEED AUTHENTICATION</b> in production]<br />
<br />
I was able to get <b>jconsole </b>to connect to the remote process. Of course local connections had never been a problem as JMX is enabled locally by default.<br />
<br />
For anyone curious, the run configuration for a tutorial project is shown below.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDR869JRus2cyYZqNQbMCO9WN4Q_vPFpWMIGK8VRj4-F22Dl94nKvz_HcrS5D2EOI-UX76hwBhwg4lZeYLoCMiQCvbQiXr56jAt-MyIrEHoLDwaWzo15mALQUD9AC06EUWrtTNm6-uEMM/s1600/runconfig.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDR869JRus2cyYZqNQbMCO9WN4Q_vPFpWMIGK8VRj4-F22Dl94nKvz_HcrS5D2EOI-UX76hwBhwg4lZeYLoCMiQCvbQiXr56jAt-MyIrEHoLDwaWzo15mALQUD9AC06EUWrtTNm6-uEMM/s320/runconfig.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A run configuration for JMX with Grails on Windows Eclipse.</td></tr>
</tbody></table>
<br />Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-40935758819533404222011-12-28T07:40:00.000-08:002011-12-28T11:41:08.355-08:00Moodle Timer Covering Quiz QuestionWhen set to only show one question per page, Moodle's timer was covering up the start of the question - see below:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirGo5hkVtNqITY8rYzASL8brM6vJ8DS1yAkMBfQN6EKFPOlbKdqZpjGyT_2yWfrVM3r_IO7umPwLmafH7WWwBPZW0yRESaYIf4f0n6Ke7wREsUd9tSTyHtOQN3JqH2_IxTH7ZmqxPedzE/s1600/moodleerror.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirGo5hkVtNqITY8rYzASL8brM6vJ8DS1yAkMBfQN6EKFPOlbKdqZpjGyT_2yWfrVM3r_IO7umPwLmafH7WWwBPZW0yRESaYIf4f0n6Ke7wREsUd9tSTyHtOQN3JqH2_IxTH7ZmqxPedzE/s320/moodleerror.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">That pesky timer</td></tr>
</tbody></table>Simple fix, based off an old suggestion from 2007 (<a href="http://moodle.org/mod/forum/discuss.php?d=45719">http://moodle.org/mod/forum/discuss.php?d=45719</a>), but updated to the new moodle 1.9.x code;<br />
<br />
in <b>/moode/mod/quiz/jstimer.php</b><br />
<div class="MsoNormal" style="background-color: white; line-height: 18px; margin-bottom: 1em; margin-left: 1.5in; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: -1.5in;"><span style="font-family: inherit;"> line 26 change width from 150 to 50</span></div><div style="background-color: white; line-height: 18px; margin-bottom: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"></div><div class="MsoNormal" style="background-color: white; line-height: 18px; margin-bottom: 1em; margin-left: 1.5in; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: -1.5in;"><span style="font-family: inherit;"> line 29 change width from 150 to 50</span></div><div style="background-color: white; line-height: 18px; margin-bottom: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"></div><div class="MsoNormal" style="background-color: white; line-height: 18px; margin-bottom: 1em; margin-left: 1.5in; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: -1.5in;"><span style="font-family: inherit;"> line 36 change font point size from 14 to 09</span></div><div style="background-color: white; line-height: 18px; margin-bottom: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"></div><div class="MsoNormal" style="background-color: white; line-height: 18px; margin-bottom: 1em; margin-left: 1.5in; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: -1.5in;"><span style="font-family: inherit;"> line 51 change value of theTop from 100 to 25</span></div><div class="MsoNormal" style="background-color: white; line-height: 18px; margin-bottom: 1em; margin-left: 1.5in; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: -1.5in;">And with that, the quiz now looks like:</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO04YloQTC-XUER-nSWdS_oXGrPUm4fSzzLWEU7bHHab7hX4kcpZU95HltK-oKWkZF860pjiKQ6pWcnbXDYzrfqBp6ZRD_I0lhLRTXpelPEPzwliwNeibH-dF4zh97Q2H9vYhtz64EEJo/s1600/moodlefixed.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO04YloQTC-XUER-nSWdS_oXGrPUm4fSzzLWEU7bHHab7hX4kcpZU95HltK-oKWkZF860pjiKQ6pWcnbXDYzrfqBp6ZRD_I0lhLRTXpelPEPzwliwNeibH-dF4zh97Q2H9vYhtz64EEJo/s320/moodlefixed.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Ah, fixed!</td></tr>
</tbody></table><div class="MsoNormal" style="background-color: white; line-height: 18px; margin-bottom: 1em; margin-left: 1.5in; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: -1.5in;"><br />
</div>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-25707792002916583522011-12-27T07:05:00.000-08:002012-01-19T05:57:54.317-08:00Basic Grails custom tag testing errorsYou may encounter an error such as the following:<br />
<br />
"No such property: <i>out<or other method name></i> in package.classname "<br />
<br />
<br />
This comes from writing the taglibtest like all the tutorials out on the web had said to, e.g. using:<br />
<br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">class DateTagLib {</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>def thisYear = {</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>out << Calendar.getInstance().get(Calendar.YEAR)</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>'' //return empty text</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> }</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
This problem arises from the newer versions of grails producing <b>UNIT </b>tests rather than <b>INTEGRATION </b>tests. In the unit environment, the Grails engine is not active to inject the dynamically created methods, such as validate, out.<br />
<br />
<u>So how do I resolve this?</u> It runs just fine on the test webpage its included on. The test was failing when the tag clearly worked.<br />
<br />
<br />
<br />
<b>Three things to fix:</b><br />
<br />
<ol>
<li>Make sure your test case is extending <span style="font-family: 'Courier New', Courier, monospace;">TagLibUnitTestCase</span>.</li>
<li>Make sure you are calling <span style="font-family: 'Courier New', Courier, monospace;">super.setUp()</span> in your test constructor</li>
<li>Make a call to <span style="font-family: 'Courier New', Courier, monospace;">mockTagLib(YourTagClassHere)</span> at the beginning of your test method.</li>
</ol>
<br />
<br />
So a basic test case for a custom tag (as shown above) might look like the following:<br />
<span style="color: #0b5394;"><br />
</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">package yourpackage</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">import grails.test.*</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">class DateTagLibTests extends TagLibUnitTestCase {</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> def dateTagLib</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> void setUp(){</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> super.setUp()</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> </span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> dateTagLib = new DateTagLib()</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> void testThisYear() {</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> mockTagLib(DateTagLib)</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> String expected = Calendar.getInstance().get(Calendar.YEAR)</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> assertEquals("the years don't match", expected, dateTagLib.thisYear().toString())</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="color: #0b5394; font-family: 'Courier New', Courier, monospace;">}</span>Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-49774614771543422392011-12-21T06:29:00.000-08:002011-12-27T07:09:49.757-08:00Changing syntax highlighting for Groovy/Grails in Eclipse<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTgK5Y8Tk_icQiFbm1F3s-fqaRy4r-71IBmAgvZGwce-3k00d9D52d_aw40vBd48boXDFhadR3PBaFumNY0C7XIpO7aB-n-GvZFwLpnjdpI9PnyQYkdzPGAgPccwbPr1mwbLy_P0EH1g0/s1600/groovy.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTgK5Y8Tk_icQiFbm1F3s-fqaRy4r-71IBmAgvZGwce-3k00d9D52d_aw40vBd48boXDFhadR3PBaFumNY0C7XIpO7aB-n-GvZFwLpnjdpI9PnyQYkdzPGAgPccwbPr1mwbLy_P0EH1g0/s320/groovy.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Looking for something a little easier on the eyes?</td></tr>
</tbody></table>By combining the Eclipse color theme plugin with some specific groovy settings, you can make working with groovy much more 'visually' appealing.<br />
<br />
1) Install the eclipse color theme plugin. <b>Help->Eclipse Marketplace...->Search for "Eclipse Color Theme"</b>. Select a dark theme (or really any one of your choosing). You might notice some elements are not being colored correctly however, namely brackets and operators. To fix these last few elements we can manually change their color to something that suits.<br />
<br />
2) <b>Window->Preferences->Groovy->Editor. </b>In this settings page you will find the rest of the settings you need to change. Set the operator, bracket colors to something more visible (and any other fine tweaks you may wish)<br />
<br />
Enjoy!Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com2tag:blogger.com,1999:blog-3770980579048852972.post-45322851198728706622011-09-28T11:25:00.000-07:002012-02-21T05:10:37.410-08:00Color Profile?After uninstalling Adobe Master Collection CS4 the colors in images displayed inside Windows photo viewer and Photoshop were colored different (and incorrectly so) from what was showing on our website and in windows explorer. Turns out the uninstall of the creative suite had reset the color profiles on my monitors.<br />
<br />
To fix this, go to (In Windows 7)...<br />
<br />
<b>Control Panel - > Color Management -> Profiles</b><br />
<br />
select...<br />
<br />
<b>"Use my settings for this device"</b> (for each monitor attached to your computer)<br />
<br />
Under profiles select...<br />
<br />
<b>Add</b><br />
<br />
Experiment with different profiles, but I found that the generic sRBG WCS profile "sRGB virtual device model profile" produced acceptable results.<br />
<br />
<br />
Enjoy.Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0tag:blogger.com,1999:blog-3770980579048852972.post-77958654687048043652011-08-12T08:38:00.000-07:002011-12-27T07:08:36.926-08:00Generating class diagrams from Eclipse projects<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjel5nnjGknyEucjHm21ZVZ4g-lqBe-LKZgU-tDt_O6UcWzrmS7qG1oGH9BjeWGYUYT-5IhBZu1V1K8kXZpaxxmLurdlvIcQKqybJTT2ZzeMjJC7lzFhzoki0utO3q3Z2ksPulM086Wkm4/s1600/uml.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjel5nnjGknyEucjHm21ZVZ4g-lqBe-LKZgU-tDt_O6UcWzrmS7qG1oGH9BjeWGYUYT-5IhBZu1V1K8kXZpaxxmLurdlvIcQKqybJTT2ZzeMjJC7lzFhzoki0utO3q3Z2ksPulM086Wkm4/s320/uml.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A zoomed out view of my class structure - using subclasses of standard packages prevents some connections from being detected.</td></tr>
</tbody></table><br />
I was looking to jump back into a project I had started a while ago in Eclipse. A GWT project for viewing resources from our website, and the class structure had totally evaded me. So I wanted to generate a UML class diagram quickly to get back up to speed and remind myself of what everything did.<br />
<br />
I recommend <a href="http://essmodel.sourceforge.net/">ESS-Model</a> if you ever encounter the same situation. Its free and fast. No bothersome purchase orders to fill out!Anonymoushttp://www.blogger.com/profile/11878873509261799419noreply@blogger.com0