<?xml version="1.0" encoding="utf-8"?>
<!-- 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
-->
<?xml-stylesheet type="text/xsl" href="https://rollerweblogger.org/roller-ui/styles/rss.xsl" media="screen"?><rss version="2.0" 
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:atom="http://www.w3.org/2005/Atom" >
<channel>
  <title>Blogging Roller</title>
  <link>https://rollerweblogger.org/roller/</link>
    <atom:link rel="self" type="application/rss+xml" href="https://rollerweblogger.org/roller/feed/entries/rss" />
  <description>Dave Johnson on open web technologies, social software and software development</description>
  <language>en-us</language>
  <copyright>Copyright 2026</copyright>
  <lastBuildDate>Mon, 23 Mar 2026 14:54:34 +0000</lastBuildDate>
  <generator>Apache Roller 6.1.5</generator>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/blogql-storybook-and-mocked-service</guid>
    <title>BlogQL: Storybook and Mocked Service Workers</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/blogql-storybook-and-mocked-service</link>
    <pubDate>Sat, 8 Apr 2023 20:43:51 +0000</pubDate>
    <category>Web Development</category>
    <category>graphql</category>
    <category>node</category>
    <category>react</category>
    <category>typescript</category>
<description>&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;One of the most interesting new technologies I learned about during the development of BlogQL is Storybook. Storybook allows you to develop and test React, Angular, and other web components in isolation.&amp;nbsp;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;Once you create stories for your components, and thanks to Chromatic, you can setup GitHub Actions to check each incoming Pull Request for changes to your UI, a visual diff! You can require that a developer approve each change. I found that all pretty compelling and started to develop stories for all of my React components.&amp;nbsp;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;The hardest part of this work was figuring out how to mock the GraphQL data needed for my components. It was difficult for me because I was still learning WebPack and polyfills in Node.js, but I got it working, in part thanks to&amp;nbsp;his very useful blog post:&amp;nbsp;&lt;a href=&quot;https://davidwcai.medium.com/mock-graphql-and-rest-in-storybook-and-jest-with-msw-78521ec44a9c&quot; target=&quot;_blank&quot;&gt;Mock GraphQL and REST in Storybook and Jest with MSW&lt;/a&gt;. &amp;nbsp;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;You can view the BlogQL Storybook online, thanks to Chromatic:&amp;nbsp;&lt;a href=&quot;https://63b0bf0f112b2d8b80a785f5-flfdjbdgsu.chromatic.com&quot; target=&quot;_blank&quot;&gt;https://63b0bf0f112b2d8b80a785f5-flfdjbdgsu.chromatic.com&lt;/a&gt;.&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;a href=&quot;https://rollerweblogger.org/roller/mediaresource/507fd7cc-03a6-420b-ad24-828ddef3bea2&quot;&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/507fd7cc-03a6-420b-ad24-828ddef3bea2&quot; width=&quot;500&quot; alt=&quot;Blog QL Storybook&quot;&gt;&lt;/a&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;This is part of a series of short posts about BlogQL:&amp;nbsp;&amp;nbsp;&lt;a href=&quot;https://github.com/snoopdave/blogql&quot; rev=&quot;en_rl_none&quot;&gt;https://github.com/snoopdave/blogql&lt;/a&gt;.&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/blogql-google-login</guid>
    <title>BlogQL, Apollo Server and Google Login</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/blogql-google-login</link>
    <pubDate>Fri, 31 Mar 2023 14:30:00 +0000</pubDate>
    <category>Web Development</category>
    <category>graphql</category>
    <category>nodejs</category>
    <category>opensource</category>
    <category>reactjs</category>
    <category>typescript</category>
<description>&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;The BlogQL server uses Apollo Server to provide a GraphQL API. Initially, I was using Apollo Server standalone, but when I stared integrating Google Login I realized I needed a couple of REST APIs so I added Express&amp;nbsp;&lt;a href=&quot;https://expressjs.com/&quot; rev=&quot;en_rl_none&quot;&gt;https://expressjs.com&lt;/a&gt;&amp;nbsp;for that. On the frontend, I&amp;#39;m using &lt;a href=&quot;https://www.npmjs.com/package/@react-oauth/google&quot; target=&quot;_blank&quot;&gt;React Oauth2&lt;/a&gt;&amp;nbsp;which makes adding Google Login support very easy to implement.&amp;nbsp; This all works fine, but if I were to start over I might consider using a server-side framework like Next.js&amp;nbsp;&lt;a href=&quot;https://nextjs.org/&quot; rev=&quot;en_rl_none&quot;&gt;https://nextjs.org&lt;/a&gt;&amp;nbsp;instead of rolling my own Apollo Server and Express setup.&amp;nbsp;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;This is part of a series of short posts about BlogQL:&amp;nbsp;&amp;nbsp;&lt;a href=&quot;https://github.com/snoopdave/blogql&quot; rev=&quot;en_rl_none&quot;&gt;https://github.com/snoopdave/blogql&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/blogql-create-react-app</guid>
    <title>BlogQL and create-react-app</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/blogql-create-react-app</link>
    <pubDate>Wed, 29 Mar 2023 17:35:21 +0000</pubDate>
    <category>Web Development</category>
    <category>graphql</category>
    <category>node</category>
    <category>opensource</category>
    <category>react</category>
    <category>typescript</category>
<description>&lt;div slice=&quot;0 1 []&quot; clipboard=&quot;true&quot;&gt;When I first started with &lt;a href=&quot;https://github.com/snoopdave/blogql&quot; target=&quot;_blank&quot;&gt;BlogQL&lt;/a&gt;, I used&amp;nbsp;&lt;a href=&quot;https://create-react-app.dev/&quot; target=&quot;_blank&quot;&gt;create-react-app&lt;/a&gt;&amp;nbsp;(CRA) to generate the code. It worked well and allowed me to focus on learning React, but eventually I found it too constraining. I wanted to learn the underlying technologies of WebPack and Babel and CRA tucks those away. It took some time, but was able to remove CRA and replace it with my own WebPack and Babel setup. &lt;/div&gt;&lt;div slice=&quot;0 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;0 1 []&quot; clipboard=&quot;true&quot;&gt;If I had to start again, I might consider using&amp;nbsp;&lt;a href=&quot;https://vitejs.dev/&quot; target=&quot;_blank&quot;&gt;Vite&lt;/a&gt;, which is growing in popularity as an alternative to WebPack.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;This is part of a series of short posts about BlogQL:&amp;nbsp;&amp;nbsp;&lt;a href=&quot;https://github.com/snoopdave/blogql&quot; rev=&quot;en_rl_none&quot;&gt;https://github.com/snoopdave/blogql&lt;/a&gt;.&lt;/div&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/blogql</guid>
    <title>BlogQL</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/blogql</link>
    <pubDate>Mon, 27 Mar 2023 16:20:32 +0000</pubDate>
    <category>Web Development</category>
    <category>graphql</category>
    <category>node</category>
    <category>react</category>
    <category>typescript</category>
<description>&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;Last summer, I started a side project to enhance my knowledge of Node, TypeScript, React, GitHub Actions and to dog-food &lt;a href=&quot;https://www.apollographql.com&quot; target=&quot;_blank&quot;&gt;Apollo GraphQL&lt;/a&gt; Studio&amp;#39;s features that I was working on. In the past couple of months, I&amp;#39;ve continued to work on BlogQL and now that the UI is not a total embarrassment, I&amp;#39;m going to share a little about the project.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;BlogQL is a blog app made up of a TypeScript/Node backend that provides a GraphQL API and a TypeScript/React-based frontend with a rich-text editor for editing blog entries. Below is a two-minute demo so you can see the features that BlogQL supports. You can find the complete source for BlogQL on GitHub&amp;nbsp;&lt;a href=&quot;https://github.com/snoopdave/blogql&quot; rev=&quot;en_rl_none&quot;&gt;https://github.com/snoopdave/blogql&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;caret-color:rgba(0, 0, 0, 0.9);color:rgba(0, 0, 0, 0.9);font-family:-apple-system, system-ui, BlinkMacSystemFont, &amp;quot;&quot;&gt;I&amp;#39;ll be following this up with a series of short posts about the technologies I&amp;#39;m using in BlogQL.&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/thoughts-on-activitypub-mastodon-etc</guid>
    <title>Thoughts on ActivityPub, Mastodon, etc.</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/thoughts-on-activitypub-mastodon-etc</link>
    <pubDate>Fri, 25 Nov 2022 01:45:50 +0000</pubDate>
    <category>Social Software</category>
    <category>activitypub</category>
<atom:summary type="html">(disclaimer: I&amp;#39;m new at this stuff and do not have full understanding of the AcitvityPub spec so please correct me if you think I got things wrong). Is the demise of Twitter going to spark a resurgence of the &amp;quot;open web&amp;quot; and is that a good thing?</atom:summary><description>&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;i&gt;(disclaimer: I&amp;#39;m new at this stuff and do not have full understanding of the AcitvityPub spec so please correct me if you think I got things wrong)&lt;/i&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div slice=&quot;1 1 []&quot; clipboard=&quot;true&quot;&gt;Is the demise of Twitter going to spark a resurgence of the &amp;quot;open web&amp;quot; and is that a good thing?&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Context: There is a World Wide Web Consortium (W3C) recommendation (a &amp;quot;standard&amp;quot; in the making) for a social networking service and it is called &lt;a href=&quot;https://activitypub.rocks&quot; target=&quot;_blank&quot;&gt;ActivityPub&lt;/a&gt;. If a website implements ActivityPub then it can interact with other websites that also implement ActivityPub. Some are saying 🤣 that ActivityPub based services could be a replacement for Twitter.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;What that means is that your social network feed can include people who are on other social networks. For example, if you have an account on a social network that uses ActivityPub, you can follow people who are on any social network that also uses ActivityPub. In the future, from your Instagram account you might be able to follow people on Twitter, Tumblr, Flickr, TikTok, etc. and have them all appear in your feed at Instagram. That all depends on Twitter, Tumblr, Flickr, TikTok and Instagram adding support for ActivityPub, which may or may not happen.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;ActivityPub makes social networking more like email. If you have an email account you can send email to anybody regardless of what email service they use. Gmail users can send email to AOL users can send email to Apple iCloud email addresses, etc.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;There are already dozens of services that implement ActivityPub and most of them use the &lt;a href=&quot;https://mastodon.social/home&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt; software. You can find me on Mastodon here:&amp;nbsp;&lt;a href=&quot;https://mastodon.social/@snoopdave&quot; style=&quot;background-color:rgb(255, 255, 255);&quot;&gt;https://mastodon.social/@snoopdave&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;This all sounds great, but what motivates people, organizations and companies to pay for running ActivityPub servers and how are those entities going to do content moderation on their servers? Will creators be able to get the reach they need to monetize? Will ActivityPub be another enabler of hate speech and dangerous misinformation or is it the foundation on which people can build systems that fight against those things?&amp;nbsp;&lt;/div&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/upgraded-to-roller-6-1</guid>
    <title>Upgraded to Roller 6.1.1</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/upgraded-to-roller-6-1</link>
    <pubDate>Sat, 30 Apr 2022 20:09:11 +0000</pubDate>
    <category>Roller</category>
    <category>java</category>
<description>&lt;p&gt;A note about this site: I just upgraded rollerweblogger.org to run the recently released&amp;nbsp;&lt;a href=&quot;https://rollerweblogger.org/project/entry/apache-roller-6-1-1&quot; target=&quot;_blank&quot;&gt;Roller 6.1.1&lt;/a&gt;, and Java 17. There was one snag. This site uses the Roller-JSPWiki plugin and old Lucene dependency in that plugin prevented Tomcat from loading Roller. It took me a couple of hours to figure out how to upgrade the plugin to use the latest version of JSPWiki. That fixed it.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/building-an-open-source-j2ee</guid>
    <title>Building an Open Source J2EE Weblogger</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/building-an-open-source-j2ee</link>
    <pubDate>Sun, 17 Apr 2022 16:42:50 +0000</pubDate>
    <category>General</category>
    <category>java</category>
    <category>opensource</category>
    <category>roller</category>
<atom:summary type="html">I wrote this article for O&amp;#39;Reilly&amp;#39;s OnJava.com over twenty years ago and it was published on April 17, 2002. Roller would not become Apache Roller until about five years later. Publishing this article changed my life and set my career on a new trajectory. I can&amp;#39;t find it online anymore so to celebrate this anniversay, i&amp;#39;m going to publish it here on Roller.

As a Java developer, you should be aware of the tremendous wealth of open source development software that is available for your use -- even if you have no desire to release any of your own software as open source. In this article, I will introduce you to some of the most useful open source Java development tools by showing you how I used these tools to develop a complete database-driven Web application called Roller.</atom:summary><description>&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;I wrote this article for O&amp;#39;Reilly&amp;#39;s OnJava.com over twenty years ago and it was published on April 17, 2002. Roller would not become Apache Roller until about five years later. Publishing this article changed my life and set my career on a new trajectory. I can&amp;#39;t find it online anymore so to celebrate this anniversay, i&amp;#39;m going to publish it here on Roller.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;

 
&lt;/p&gt;&lt;p&gt;As a Java developer, you should be aware of the tremendous
wealth of open source development software that is available for your use
-- even if you have no desire to release any of your own software as open
source. In this article, I will introduce you to some of the most useful
open source Java development tools by showing you how I used these tools
to develop a complete database-driven Web application called Roller.&lt;/p&gt;
 
&lt;p&gt;Roller fits into the relatively new category of software called webloggers: applications that make it
easy for you to maintain a weblog, also known as a &lt;i&gt;blog&lt;/i&gt; -- a public diary where you link to recent reading on the Web and comment on items of interest to you.&lt;/p&gt;
 
&lt;p&gt;The Roller Web application
allows you to maintain a Web site that consists of a weblog, an organized collection of favorite Web bookmarks,
and a collection of favorite news feeds. You can define Web pages
to display your weblog, bookmarks, and news feeds. By editing the HTML templates
that define these pages, you have almost total control over the layout and
appearance of these pages. Most importantly, you can do all of this without
leaving the Roller Web application -- no programming is required.&lt;/p&gt;
 
&lt;p&gt;I used over a dozen open source development tools to develop
Roller, the most useful of which are listed in Table 1; however,
this article focuses on just four tools: the XDoclet code generator,
the Castor persistence framework, the Struts Servlet/JSP
framework, and the Velocity code-generation engine. In this article
I will describe the Roller application, its architecture, and specifically
how I used XDoclet, Castor, Struts, and Velocity in its development.&lt;/p&gt;
 
&lt;p&gt;&lt;i&gt;Table 1: Open source tools used in Roller Development&lt;/i&gt;&lt;/p&gt;
 
&lt;table border=&quot;1&quot; bordercolor=&quot;#cccccc&quot; cellpadding=&quot;4&quot; cellspacing=&quot;0&quot;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td valign=&quot;top&quot; bgcolor=&quot;#e6e6e6&quot;&gt;
&lt;p&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot; bgcolor=&quot;#e6e6e6&quot;&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot; bgcolor=&quot;#e6e6e6&quot;&gt;
&lt;p&gt;&lt;strong&gt;Developer&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot; bgcolor=&quot;#e6e6e6&quot;&gt;
&lt;p&gt;&lt;strong&gt;Type of License*&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Castor&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Persistence framework&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Exolab&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Similar to BSD license&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;HSQL&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Small but powerful Java database&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Thomas Meuller&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Similar to BSD license&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Jakarta Ant&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;XML-driven Java build system&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Jakarta Commons&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Collections, utilities&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Jakarta Struts&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Servlet/JSP framework&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Jakarta Tomcat&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Servlet/JSP Server&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Jakarta Velocity&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Template-driven code generator&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Netbeans&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Integrated Dev. Environment&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&amp;nbsp;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Sun Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Xerces&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;XML parser&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Apache Public License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;XDoclet&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Code generator&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Dreambean&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Similar to MIT License&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
 
&lt;p class=&quot;smalltext2&quot;&gt;* For more information on open source licenses see &lt;a href=&quot;http://www.opensource.org/&quot;&gt;&lt;i&gt;opensource.org&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;The Roller Application&lt;/h3&gt;

&lt;p&gt;Roller does not support
all of the features of commercial weblogging software (such as Userland&amp;#39;s
&lt;a href=&quot;http://onjava.com/onjava/2002/04/17/radio.userland.com&quot;&gt;Radio&lt;/a&gt; or Pyra Labs&amp;#39; &lt;a href=&quot;http://www.blogger.com/&quot;&gt;Blogger&lt;/a&gt; products), but Roller does support what I consider the essential weblogging features. With Roller you
can:&lt;/p&gt;
 
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Maintain a weblog, with user-defined categories.&lt;/b&gt; You can write new weblog entries and edit entries that have already been posted. You can define a set of weblog categories and can assign weblog entries to different categories. This allows you to maintain several different weblogs, each covering a different topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Publish your weblog as an RSS news feed.&lt;/b&gt; Roller
makes your weblog available as a standard Rich Site Summary (RSS) news feed
so that readers can subscribe to and read your weblog without visiting your
Roller site.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Maintain a collection of favorite bookmarks, organized
by bookmark folders.&lt;/b&gt; You can define new bookmark folders and can add,
delete, and edit the bookmarks within these folders. You can then display
these bookmarks on one or more of your Roller site&amp;#39;s pages. This allows you to do &lt;i&gt;blogrolling&lt;/i&gt; -- displaying links to your favorite weblogs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Maintain a collection of favorite RSS news feeds.&lt;/b&gt; This allows you
to display headlines with links to news stories from your favorite news
sources or weblogs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Define a set of Web pages to display your weblog, bookmarks, and news feeds.&lt;/b&gt; Pages are defined using HTML templates with embedded macros for each type of data. For example, there is a &lt;code&gt;$Bookmarks&lt;/code&gt; macro that will draw a portion of your bookmark collection on a Web page and a &lt;code&gt;$WeblogCalendar&lt;/code&gt; macro that will draw a calendar view of your past weblog entries. These templates allow you almost complete control over the layout and look-and-feel of your Web pages.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
 
&lt;p&gt;There are two types of Roller users: readers and editors.  Readers are simply anonymous visitors to the Roller Web site. Editors have user accounts and must log in by providing a user name and password.  Editors have the ability to edit their weblog entries, bookmarks, newsfeeds, and page templates.&lt;/p&gt;


 
&lt;p&gt;Figure 1 illustrates the Roller application by showing the Roller Web page navigation tree. The boxes represent Web pages and the arrows represent links between pages. The gray pages are the public pages that any visitor may access, the yellow pages are the login pages, and the red pages are the pages that only editors can access.&lt;/p&gt;
 
&lt;table width=&quot;645&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 1: Roller Web Pages&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;h3&gt;Roller Architecture&lt;/h3&gt;
 
&lt;p&gt;Internally, Roller is divided into a presentation
tier and a business tier, as recommended in Sun&amp;#39;s J2EE Pattern Catalog. The presentation tier is responsible for Roller&amp;#39;s user interface,
and the business tier is responsible for Roller&amp;#39;s application logic and the 
persistence of application data. Figure 2 provides an overview of
the Roller architecture.&lt;/p&gt;
 
&lt;table width=&quot;391&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 2: Roller Architecture&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;The presentation tier is implemented using the Model-View-Controller (MVC) pattern and the Struts MVC framework.  The Model is an abstraction of the
application logic and application data and is represented by a set of interfaces 
defined in the &lt;code&gt;org.roller.model&lt;/code&gt; package.  The View is implemented using Servlets, JSP pages, and Velocity page templates.  The Controller is Struts, which is responsible for receiving incoming requests and dispatching them to the View.  The implementation of the presentation tier is further discussed in the sections on Struts and Velocity.&lt;/p&gt;
 
&lt;p&gt;The business tier implements the interfaces in the &lt;code&gt;org.roller.model&lt;/code&gt; package, using the Castor JDO persistence framework. The business tier exchanges data with the presentation tier in the form of simple, lightweight JavaBeans known as Value Objects. Value Objects are yet another of the Sun J2EE patterns. Each Value Object maps to a table in the Roller database.&lt;/p&gt;
 
&lt;p&gt;Figure 3 shows the Roller Value Objects, their properties, and the relationships between them. Each editor is represented by a User object.  Each User has a Website object, which represents the editor&amp;#39;s Web site and which has weblog entries, bookmark folders, newsfeeds, and page templates.  The Website object also specifies the default page template of the Web site and which page template is used for rendering a day of weblog entries.&lt;/p&gt;
 
&lt;table width=&quot;466&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 3: Roller Value Objects&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;


 
&lt;p&gt;The business tier uses Castor JDO to store and retrieve Value Objects to and from a JDBC-accessible database. Castor JDO is part of the larger Castor data-binding framework, which according to the Castor Web site is &amp;quot;the shortest path between Java objects, XML documents, SQL databases, and LDAP.&amp;quot;&lt;/p&gt;

&lt;p&gt;As a persistence framework, Castor JDO is similar to commercial object-relational mappers such as TopLink and Cocobase. Castor JDO fulfills a role similar to that of Sun&amp;#39;s Java Data Objects, but Castor JDO is not an implementation of Sun&amp;#39;s JDO specification (JSR-000012). Castor JDO allows you to define a mapping between Java classes and tables in a relational database. You can then issue queries using Castor&amp;#39;s own Object Query Language (OQL) and receive the results as collections of Java objects.&lt;/p&gt;
 



&lt;p&gt;Before you can use Castor JDO, you must provide a mapping file -- an XML file that maps each class to a database table and each class property to a field within a database table. Below is a portion of Roller&amp;#39;s mapping file.&lt;/p&gt;
 
&lt;pre&gt;&lt;code&gt;&amp;lt;mapping&amp;gt; 
&amp;lt;class name=org.roller.model.BookmarkData&amp;quot; identity=&amp;quot;id&amp;quot;
    access=&amp;quot;shared&amp;quot; key-generator=&amp;quot;UUID&amp;quot; auto-complete=&amp;quot;false&amp;quot;&amp;gt;
    &amp;lt;map-to table=&amp;quot;bookmark&amp;quot;/&amp;gt; 
    &amp;lt;cache-type type=&amp;quot;count-limited&amp;quot;/&amp;gt;
    &amp;lt;field name=&amp;quot;folderId&amp;quot; type=&amp;quot;java.lang.String&amp;quot;&amp;gt;&amp;lt;/field&amp;gt;
    &amp;lt;field name=&amp;quot;id&amp;quot; type=&amp;quot;java.lang.String&amp;quot;&amp;gt;&amp;lt;/field&amp;gt;
    &amp;lt;field name=&amp;quot;image&amp;quot; type=&amp;quot;java.lang.String&amp;quot;&amp;gt;&amp;lt;/field&amp;gt;
    &amp;lt;field name=&amp;quot;name&amp;quot; type=&amp;quot;java.lang.String&amp;quot;&amp;gt;&amp;lt;/field&amp;gt;
    &amp;lt;field name=&amp;quot;priority&amp;quot; type=&amp;quot;java.lang.Integer&amp;quot;&amp;gt;&amp;lt;/field&amp;gt;
    &amp;lt;field name=&amp;quot;url&amp;quot; type=&amp;quot;java.lang.String&amp;quot;&amp;gt;&amp;lt;/field&amp;gt;
&amp;lt;/class&amp;gt;
...
&amp;lt;/mapping&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once you provide Castor with a mapping file, retrieving a collection of objects from the database can be as simple
as the code snippet shown below:&lt;/p&gt;
 

&lt;pre&gt;&lt;code&gt;// Construct a new query and bind its parameters
String query = &amp;quot;SELECT p FROM BookmarkData p WHERE websiteId=$&amp;quot;;
OQLQuery oql = db.getOQLQuery( query );
oql.bind( websiteId );

// Retrieve results and print each one
QueryResults results = oql.execute();
while ( results.hasMore() ) {
   BookmarkData bookmark = (BookmarkData)results.next();
   System.out.println( bookmark.toString() );
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;XDoclet&lt;/h3&gt;
 
&lt;p&gt;XDoclet is a code generator that is implemented as a Javadoc extension, a &lt;i&gt;Doclet&lt;/i&gt;. To
use XDoclet, you place special Javadoc tags in your Java source code. Based
on these tags, XDoclet can generate additional Java code that supports your
classes, mapping files that map your classes to database tables, and deployment
descriptors that assist in deploying your classes.&lt;/p&gt;
 
&lt;p&gt;XDoclet started out
its life as EJBDoclet, a tool that allows you to implement an Enterprise JavaBean
by writing just one source code file. Now, the XDoclet product includes two
Doclets: EJBDoclet and WebDoclet. EJBDoclet is for generating EJB
classes, value objects, and database mappings. WebDoclet is for generating
all sorts of Servlet Web Application deployment descriptors, including &lt;code&gt;web.xml&lt;/code&gt; files, Tag Library Descriptors, and
Struts configuration files.&lt;/p&gt;
 
&lt;p&gt;The Roller build
process uses both EJBDoclet and WebDoclet, as shown in Figure 4. In
Step 1, EJBDoclet is used to process a set of abstract classes of
type &lt;code&gt;javax.ejb.EntityBean&lt;/code&gt; -- one
for each one of the Roller Value Objects. From these classes, EJBDoclet generates
a Castor mapping file, the Roller Value Object classes, and a set of corresponding
Struts form classes. In Step 2, WebDoclet is used to process a
source directory that contains JSP tags, Servlet classes, and Struts classes.
The output of the WebDoclet is the complete set of Roller Web Application
deployment descriptors.&lt;/p&gt;
 
&lt;table width=&quot;476&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 4: XDoclet and the Roller Build Process&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Below is a simple example bean that shows the EJBDoclet tags necessary to create a Value Object. The &lt;code&gt;@castor&lt;/code&gt; tags provide the information needed to generate the Castor mapping entries for the bean. The &lt;code&gt;@ejb&lt;/code&gt; tags provide the information needed to generate the Value Object and a complete EJB entity bean (which Roller does not use).&lt;/p&gt;
 
&lt;pre&gt;&lt;code&gt;/**
 * Represents a single URL in a user&amp;#39;s favorite web-bookmarks collection.
 * @ejb:bean name=&amp;quot;Bookmark&amp;quot; type=&amp;quot;CMP&amp;quot; jndi-name=&amp;quot;roller/Bookmark&amp;quot;
 * @ejb:data-object extends=&amp;quot;org.roller.model.ValueObject&amp;quot;
 * @struts:form 
 * @castor:class name=&amp;quot;bookmark&amp;quot; table=&amp;quot;bookmark&amp;quot; xml=&amp;quot;bookmark&amp;quot;
 *               id=&amp;quot;id&amp;quot; key-generator=&amp;quot;UUID&amp;quot;&lt;br&gt; */

public abstract class BookmarkBean implements EntityBean 
{

  /** @ejb:interface-method 
   *  @ejb:transaction type=&amp;quot;Required&amp;quot; */
   public abstract void setData(org.roller.model.BookmarkData dataHolder);

  /** @ejb:interface-method */
   public abstract org.roller.model.BookmarkData getData();

  /** @castor:field set-method=&amp;quot;setId&amp;quot;
   *  @castor:field-xml node=&amp;quot;attribute&amp;quot;
   *  @castor:field-sql name=&amp;quot;id&amp;quot; sql-dirty=&amp;quot;check&amp;quot; dirty=&amp;quot;true&amp;quot; 
   *  @ejb:interface-method 
   *  @ejb:pk-field
   *  @ejb:persistent-field */
    public abstract String getId();

  /** @ejb:pk-field
   *  @ejb:persistent-field */
    public abstract void setId( String value );
    ...
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Struts&lt;/h3&gt;
 
&lt;p&gt;The Roller presentation tier is implemented using Struts and Velocity. Struts is a Servlet application framework that is based on the MVC pattern. In a typical Struts application, the Model is a set of JavaBeans that hold the data to be presented in the View; the View is a set of JSP pages that render 
HTML; and the Controller is a Servlet and set of action classes that are
registered to handle incoming requests.&lt;/p&gt;
 
&lt;p&gt;Roller&amp;#39;s Edit-Bookmark form provides a nice, simple example of how Struts works. There are four parts to the Edit-Bookmark form implementation: the &lt;code&gt;edit-bookmark.jsp&lt;/code&gt; page, the &lt;code&gt;BookmarkForm&lt;/code&gt; JavaBean class, the &lt;code&gt;BookmarkFormAction&lt;/code&gt; action handler, and some entries in Roller&amp;#39;s &lt;code&gt;struts-config.xml&lt;/code&gt; file that tie the first three items together. So, let&amp;#39;s introduce the players:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;edit-bookmark.jsp&lt;/code&gt; page looks just
like an HTML page, except that it uses the Struts HTML form tags instead
of standard HTML form tags. The Struts HTML form tags know how to find
the &lt;code&gt;BookmarkForm&lt;/code&gt; JavaBean and how to use its properties to populate the form with data.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The &lt;code&gt;BookmarkForm&lt;/code&gt; class is a dumb JavaBean that just holds data -- it has the exact same properties as the Bookmark Value Object. As you may recall, the &lt;code&gt;BookmarkForm&lt;/code&gt; class and all of its sibling form classes are generated by XDoclet. In Struts, form classes must extend &lt;code&gt;org.apache.struts.action.ActionForm&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The &lt;code&gt;BookmarkFormAction&lt;/code&gt; is essentially an action handler. It is registered (in the &lt;code&gt;struts-config.xml&lt;/code&gt; file) to handle incoming requests that include the pattern &lt;code&gt;/bookmark.do&lt;/code&gt;. In Struts, action classes must extend &lt;code&gt;org.apache.struts.action.Action&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
 
&lt;p&gt;Figure 5 shows the sequence of events that occurs when a request for the Edit-Bookmark form comes into the system. Roller needs to respond to this request by creating an HTML form populated with data for the bookmark that is to be edited.&lt;/p&gt;

&lt;table width=&quot;634&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 5: Incoming request for Edit-Bookmark page&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Here are the steps in processing an incoming request for the Edit-Bookmark page:&lt;/p&gt;
 
&lt;ol&gt;&lt;li&gt;&lt;p&gt;The Struts Controller Servlet receives a request for the Edit-Bookmark action. The Controller uses the URI of the request to look up the  &lt;code&gt;FormAction&lt;/code&gt; that should handle the request.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The Struts Controller Servlet dispatches the request to the &lt;code&gt;BookmarkFormAction.edit()&lt;/code&gt; method. Knowing that the user has requested the Edit-Bookmark page, the &lt;code&gt;BookmarkFormAction&lt;/code&gt; looks for a request parameter that specifies the bookmark that is to be edited.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The &lt;code&gt;BookmarkFormAction&lt;/code&gt; calls the &lt;code&gt;BookmarkManager&lt;/code&gt; to retrieve the bookmark information that is to be edited.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The &lt;code&gt;BookmarkFormAction&lt;/code&gt; creates the &lt;code&gt;BookmarkForm&lt;/code&gt;
bean and adds that bean to the request&amp;#39;s attributes so that it can be accessed
by the JSP page.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The &lt;code&gt;BookmarkFormAction&lt;/code&gt; finally forwards the request to &lt;code&gt;edit-bookmark.jsp&lt;/code&gt; so that the page may be rendered.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The Struts form tags on the &lt;code&gt;edit-bookmark.jsp&lt;/code&gt; page reads data from the &lt;code&gt;BookmarkForm&lt;/code&gt; bean and uses that data to populate the Edit-Bookmark form. After that, the HTML page is returned to the user&amp;#39;s browser for display.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Figure 6 shows
the sequence of events that occurs when the request that contains posted
data from the Edit-Bookmark page comes into the system. Roller needs to take
the incoming form data and use it to update the bookmark that is stored in
the data store managed by the business tier.&lt;/p&gt;

&lt;table width=&quot;608&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 6: Request with data posted from Edit-Bookmark page&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Here are the steps in processing a request with data from a posted Edit-Bookmark page:&lt;/p&gt;
 
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Struts Controller
Servlet receives a request for the &lt;code&gt;Update-Bookmark&lt;/code&gt; action. The Struts Controller
determines which action should handle the request and which form bean should
receive the data from the incoming form post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Struts Controller Servlet populates the &lt;code&gt;BookmarkForm&lt;/code&gt; bean with data from the incoming request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Controller calls the &lt;code&gt;BookmarkFormAction&lt;/code&gt; and passes
in the form bean.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;BookmarkFormAction&lt;/code&gt; retrieves the data
from the &lt;code&gt;BookmarkForm&lt;/code&gt; bean.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The action calls upon the &lt;code&gt;BookmarkManager&lt;/code&gt; to store
the updated bookmark information.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
 
&lt;h3&gt;Velocity&lt;/h3&gt;
 
&lt;p&gt;While JSP pages work
well for the Roller editor pages, which rarely change, JSP does not work
so well for the user pages. Weblog authors are not programmers, and they
cannot be required to learn JSP and Java programming just to customize
their weblog and associated Web pages. Furthermore, allowing Roller users
to add new JSP pages, and thus new Java code, to the Roller application at
runtime is a security risk.&lt;/p&gt;
 
&lt;table width=&quot;622&quot; border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;p class=&quot;tiny&quot;&gt;&lt;br&gt;
Figure 7: Velocity-generated public page&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;The best solution to the user pages problem is Velocity. Velocity is a general purpose template-based code-generation engine. That may sound complicated, but from the user&amp;#39;s point of view, it is simple and easy-to-use. For example, the weblog page shown in Figure 7 is generated by a simple Velocity template. This template is shown below:&lt;/p&gt;
 
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD HTML 4.01 Transitional//EN&amp;quot;&amp;gt;
&amp;lt;html&amp;gt;
;&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;$macros.showWebsiteTitle()&amp;lt;/title&amp;gt;     
&amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;$macros.includePage(&amp;quot;_css&amp;quot;)&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
;&amp;lt;body&amp;gt; 
&amp;lt;table cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;15&amp;quot; &lt;br&gt;   border=&amp;quot;0&amp;quot; align=&amp;quot;center&amp;quot; width=&amp;quot;95%&amp;quot;&amp;gt;   
   &amp;lt;tr&amp;gt;    
   &amp;lt;td width=&amp;quot;20%&amp;quot; valign=&amp;quot;top&amp;quot; bgcolor=&amp;quot;#ffffff&amp;quot;&amp;gt;      
      $macros.showNavBar(true)&amp;lt;br&amp;gt;       
      $macros.showEditorNavBar(true)&amp;lt;br&amp;gt;
      $macros.showBookmarks(&amp;quot;Blogrolling&amp;quot;,true)&amp;lt;br&amp;gt;       
      $macros.showBookmarks(&amp;quot;News&amp;quot;,true)    
   &amp;lt;/td&amp;gt;    
   &amp;lt;td width=&amp;quot;60%&amp;quot; valign=&amp;quot;top&amp;quot; bgcolor=&amp;quot;#ffffff&amp;quot;&amp;gt;                
      &amp;lt;h2&amp;gt;$macros.showWebsiteTitle()&amp;lt;/h2&amp;gt;                       
      $macros.showWeblogCategoryChooser()&amp;lt;br&amp;gt;       
      $macros.showWeblogEntries()    
   &amp;lt;/td&amp;gt;    
   &amp;lt;td valign=&amp;quot;top&amp;quot; bgcolor=&amp;quot;#ffffff&amp;quot; width=&amp;quot;20%&amp;quot;&amp;gt;         
      $macros.showWeblogCalendar()&amp;lt;br&amp;gt;       
      $macros.showRSSBadge()    
   &amp;lt;/td&amp;gt; 
   &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;                  
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The items that start with &lt;code&gt;$&lt;/code&gt; are Velocity expressions, most of which result in calls to JSP tags that have been specially designed to work with Velocity. For example, the &lt;code&gt;$macros.showWeblogCategoryChooser()&lt;/code&gt; expression results in the generation of the navigation bar at the top of the page -- the one that reads &amp;quot;All | Technology | News | Entertainment.&amp;quot; The navigation bar is implemented in a custom JSP tag class named &lt;code&gt;org.roller.presentation.tags.NavigationTag&lt;/code&gt;, which is also used in the JSP-based Roller editor pages.&lt;/p&gt;
 


&lt;p&gt;Each user can define
any number of pages, and since these pages are simply HTML pages, they can
be customized using Front Page or any other HTML editor. The user just has
to put the Velocity expressions in the right place. Below is a list of some
of the Velocity expressions that are available for use in user-defined Roller
Web pages.&lt;/p&gt;
 
&lt;table border=&quot;1&quot; bordercolor=&quot;#cccccc&quot; cellpadding=&quot;4&quot; cellspacing=&quot;0&quot;&gt;
&lt;tbody&gt;&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot; bgcolor=&quot;#e6e6e6&quot;&gt;
&lt;p&gt;&lt;b&gt;Macro&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot; bgcolor=&quot;#e6e6e6&quot;&gt;
&lt;p&gt;&lt;b&gt;Emits HTML for:&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;&lt;b&gt;$macros.showNavBar()&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Navigation bar, with a link to each one of the user&amp;#39;s user-defined pages&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;&lt;b&gt;$macros.showEditorNavBar()&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Editor navigation
bar, with links to the edit-bookmarks,edit-newsfeeds, edit-weblog,
and edit-website pages &lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;&lt;b&gt;$macros.showBookmarks()&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Entire bookmark
collection in a multi-column table &lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;&lt;b&gt;$macros.showNewsfeeds()&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;Current headlines
and story descriptions for the user&amp;#39;s RSS newsfeeds 
&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;&lt;b&gt;$macros.showWeblogEntries()&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;The most recent weblog entries &lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=&quot;top&quot;&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;&lt;b&gt;$macros.showWeblogCalendar()&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;
&lt;p&gt;A weblog calendar,
with a link for each day on which there is a weblog entry 
&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
 
&lt;h3&gt;Conclusion&lt;/h3&gt;
 
&lt;p&gt;In this article,
I have described four open source Java development tools and how these tools
can be used together to develop a fairly sophisticated Web application. I
hope I have given you a good idea of the power and flexibility of these tools.&lt;/p&gt;
 
&lt;p&gt;Although I have not
mentioned any problems with the open source tools that I have discussed,
I did run into a number of bugs.  I was able to find work-arounds and fixes
for these bugs, but it was not always easy.  I had to spend some time browsing
mailing-lists, searching with Google, and, in one case, downloading the latest
source for a product and building it myself. Formal technical support is
not available for many open source tools, so keep in mind that you may have
to solve your own problems.&lt;/p&gt;
 
&lt;p&gt;In closing, I would like to thank the many developers and other contributors that made possible the open source Java development tools that I used in the development of Roller.  The tools are great and they just keep getting better.&lt;/p&gt;
 
&lt;h3&gt;Resources&lt;/h3&gt;
 
&lt;h4&gt;Weblogging&lt;/h4&gt;
 
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://newhome.weblogs.com/historyOfWeblogs&quot;&gt;The History of Weblogs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://radio.userland.com/&quot;&gt;Userland Radio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.blogger.com/&quot;&gt;Pyra Labs Blogger&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;Castor&lt;/h4&gt;
 
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://castor.exolab.org/castor&quot;&gt;The Castor Project Homepage&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;Struts&lt;/h4&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://jakarta.apache.org/struts&quot;&gt; The Jakarta Struts Project&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;http://www.javaworld.com/javaworld/jw-12-2000/jw-1201-struts.html&quot;&gt;&amp;quot;Strut your stuff with JSP tags&amp;quot;&lt;/a&gt; (JavaWorld article)&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;Velocity&lt;/h4&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://jakarta.apache.org/velocity&quot;&gt; The Jakarta Velocity Project Homepage&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;XDoclet&lt;/h4&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://xdoclet.sourceforge.net/&quot;&gt;The XDoclet Project Homepage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://onjava.com/pub/a/onjava/2002/01/30/xdoclet.html&quot;&gt; &amp;quot;Using XDoclet: Developing EJBs with Just the Bean Class&amp;quot;&lt;/a&gt; (onJava.com article)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://onjava.com/pub/a/onjava/2002/01/30/xdoclet.html&quot;&gt; &amp;quot;Deciding Whether EJB is Appropriate&amp;quot;&lt;/a&gt; (OnJava.com article)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Originally published here:&amp;nbsp;&lt;a href=&quot;http://onjava.com/onjava/2002/04/17/wblogosj2ee.html&quot; target=&quot;_blank&quot;&gt;http://onjava.com/onjava/2002/04/17/wblogosj2ee.html&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;i&gt;
&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/blogging-about-roller-6-1</guid>
    <title>Blogging about Roller 6.1</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/blogging-about-roller-6-1</link>
    <pubDate>Wed, 22 Dec 2021 22:53:38 +0000</pubDate>
    <category>Open Source</category>
    <category>blogql</category>
    <category>graphql</category>
    <category>node</category>
    <category>react</category>
    <category>roller</category>
<description>&lt;p&gt;I barely even update this blog, but I do update the software that powers it and I&amp;#39;m happy to still have the help of an awesome team of volunteers who pitch in when they want to and always when needed. Today we released Apache Roller 6.1.0, a release we had been talking about, but that was prompted by the Log4J vulnerability. See the &lt;a href=&quot;https://rollerweblogger.org/project/entry/apache-roller-6-1-0&quot; target=&quot;_blank&quot;&gt;project&amp;#39;s blog&lt;/a&gt; for details.&lt;/p&gt;&lt;p&gt;I&amp;#39;m not the one making the most code changes in Roller now days, but I do help with releases. I&amp;#39;ve been spending my spare cycles hacking on BlogQL, a Node/TypeScript-based blog server with a GraphQL API and a React front-end. It&amp;#39;s really more of an example app to help me understand those technologies, kind of like Roller was. Maybe I&amp;#39;ll write about it someday. That&amp;#39;s all for now.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/apache-roller-6-0-2</guid>
    <title>Apache Roller 6.0.2 released!</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/apache-roller-6-0-2</link>
    <pubDate>Tue, 17 Aug 2021 01:38:55 +0000</pubDate>
    <category>General</category>
<description>&lt;p&gt;&lt;span style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;font-size:small;&quot;&gt;The Roller PMC has approved the release of Apache Roller 6.0.2, a minor bug fix release.&lt;/span&gt;&lt;/p&gt;&lt;div style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;&quot;&gt;&lt;div&gt;You can download the release&amp;nbsp;via Apache mirrors link here:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;http://roller.apache.org/downloads/downloads.html&quot; target=&quot;_blank&quot; saferedirecturl=&quot;https://www.google.com/url?q=http://roller.apache.org/downloads/downloads.html&amp;amp;source=gmail&amp;amp;ust=1629241434814000&amp;amp;usg=AFQjCNFaLoedfyue2CZ58Q2k7rDMh8K7mg&quot; style=&quot;color:rgb(17, 85, 204);&quot;&gt;http://roller.apache.org/downloads/downloads.html&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;&quot;&gt;Roller 6.0.2 includes the fixes listed below:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;Parse referrer URL instead of using string value directly&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/apache/roller/pull/90&quot; target=&quot;_blank&quot; saferedirecturl=&quot;https://www.google.com/url?q=https://github.com/apache/roller/pull/90&amp;amp;source=gmail&amp;amp;ust=1629241434814000&amp;amp;usg=AFQjCNG550OaXgyBTvbmJFQbvdV1GfXTRg&quot; style=&quot;color:rgb(17, 85, 204);&quot;&gt;https://github.com/apache/roller/pull/90&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Improved: the UI for the blog entries page&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/apache/roller/pull/87&quot; target=&quot;_blank&quot; saferedirecturl=&quot;https://www.google.com/url?q=https://github.com/apache/roller/pull/87&amp;amp;source=gmail&amp;amp;ust=1629241434814000&amp;amp;usg=AFQjCNHYjRi7hsVVtpL0HxgB23eiFq2pgA&quot; style=&quot;color:rgb(17, 85, 204);&quot;&gt;https://github.com/apache/roller/pull/87&lt;/a&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Improved: tooltip content on the registration form&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/apache/roller/pull/89&quot; target=&quot;_blank&quot; saferedirecturl=&quot;https://www.google.com/url?q=https://github.com/apache/roller/pull/89&amp;amp;source=gmail&amp;amp;ust=1629241434814000&amp;amp;usg=AFQjCNFyqycpzQHX9PIZCPJL-_LQSgFntw&quot; style=&quot;color:rgb(17, 85, 204);&quot;&gt;https://github.com/apache/roller/pull/89&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Updated: the main menu to have proper space when displaying permission message&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/apache/roller/pull/95&quot; target=&quot;_blank&quot; saferedirecturl=&quot;https://www.google.com/url?q=https://github.com/apache/roller/pull/95&amp;amp;source=gmail&amp;amp;ust=1629241434814000&amp;amp;usg=AFQjCNHdbl6vmAW5AbWdJen2RnA1pvMefg&quot; style=&quot;color:rgb(17, 85, 204);&quot;&gt;https://github.com/apache/roller/pull/95&lt;/a&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Updated: the header to show the tagline only when it&amp;#39;s available&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/apache/roller/pull/99&quot; target=&quot;_blank&quot; saferedirecturl=&quot;https://www.google.com/url?q=https://github.com/apache/roller/pull/99&amp;amp;source=gmail&amp;amp;ust=1629241434814000&amp;amp;usg=AFQjCNEXnicZkSykcq8VQiEtpu9iUzHigw&quot; style=&quot;color:rgb(17, 85, 204);&quot;&gt;https://github.com/apache/roller/pull/99&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;UI fixes are thanks to new contributor&amp;nbsp;Yash Maheshwari.&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;&quot;&gt;On behalf of the Roller PMC,&lt;/div&gt;&lt;div style=&quot;caret-color:rgb(34, 34, 34);color:rgb(34, 34, 34);font-family:Arial, Helvetica, sans-serif;&quot;&gt;Enjoy and best regards!&lt;/div&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/roller-6-released</guid>
    <title>Roller 6 released</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/roller-6-released</link>
    <pubDate>Tue, 31 Dec 2019 15:04:17 +0000</pubDate>
    <category>Roller</category>
    <category>asf</category>
<description>&lt;p&gt;This latest release of Roller includes a new UI that uses&amp;nbsp;&lt;a href=&quot;https://getbootstrap.com/docs/3.3/&quot; target=&quot;_blank&quot;&gt;Twitter Bootstrap 3&lt;/a&gt; and is based on work I started in 2015 and first committed on December 21, 2015 with this commit:&amp;nbsp;&lt;a href=&quot;https://github.com/snoopdave/rollarcus/commit/2da6c3c2e28419f68244e0c362c15be96013d5f9&quot; target=&quot;_blank&quot;&gt;2da6c3c2e28419f68244e0c362c15be96013d5f9&lt;/a&gt;. You can find the &lt;a href=&quot;https://rollerweblogger.org/project/entry/apache-roller-6-released&quot; target=&quot;_blank&quot;&gt;details on on the Roller project blog&lt;/a&gt;. I got lot of help along the way with testing, fixes, dependency upgrades, Java 11 support and more, so thanks to all that helped make this happen.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/digitalocean-kubernetes</guid>
    <title>Powered by Digital Ocean Kubernetes</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/digitalocean-kubernetes</link>
    <pubDate>Sun, 10 Feb 2019 22:42:05 +0000</pubDate>
    <category>Roller</category>
    <category>asf</category>
    <category>digitalocean</category>
    <category>kubernetes</category>
    <category>roller</category>
<description>
&lt;p&gt;Just a note to say that I&amp;#39;ve switched this site over to &lt;a href=&quot;https://blog.digitalocean.com/digitalocean-releases-k8s-as-a-service/&quot;&gt;Digital Ocean Kubernetes&lt;/a&gt; service, which is in Limited Availability right now. &lt;/p&gt;


&lt;p&gt;Digital Ocean&amp;#39;s Kubernetes service is just as simple and well designed as the rest of Digital Ocean. I &lt;a href=&quot;https://rollerweblogger.org/roller/entry/powered-by-kubernetes&quot;&gt;mentioned before&lt;/a&gt; that I rolled my own Kubernetes cluster via Ansible and Kubeadm. Now I can delete all those config files and that&amp;#39;s a good thing. Plus, the price is right; I can get by with one $10/month node (1 CPU / 2 GB memory) and a $10/month load balancer.&lt;/p&gt;


&lt;p&gt;&lt;div style=&quot;margin:1em 0 1em 0;text-align:center;width:100%;&quot;&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/a352bcf3-b7d9-4f2d-821d-a3ae42e0b06c&quot; width=&quot;80%&quot; halign=&quot;middle&quot;&gt;&lt;/div&gt;&lt;/p&gt;


&lt;p&gt;To get this site up and running I had to deploy four things to my cluster. I installed the &lt;a href=&quot;https://www.nginx.com/products/nginx/kubernetes-ingress-controller/&quot;&gt;NGINX Ingress Controller&lt;/a&gt;, &lt;a href=&quot;https://cert-manager.readthedocs.io/en/latest/&quot;&gt;Cert-Manager&lt;/a&gt; for automatic creation of Let&amp;#39;s Encrypt TLS certs, &lt;a href=&quot;https://www.postgresql.org&quot;&gt;PostgreSQL&lt;/a&gt; and my custom build of &lt;a href=&quot;https://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt;. All of that went pretty smoothly and I didn&amp;#39;t run into and problems that I could blame on Digital Ocean. &lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/roller-6-0-0-snapshot</guid>
    <title>Roller 6.0.0-SNAPSHOT</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/roller-6-0-0-snapshot</link>
    <pubDate>Sat, 26 Jan 2019 22:13:38 +0000</pubDate>
    <category>Roller</category>
    <category>apacheroller</category>
    <category>asf</category>
<description>&lt;p&gt;Upgraded this site to Roller 6.0.0-SNAPSHOT today, which meant an hour of fiddling around with my private Docker registry, then giving up and using the one free private repository offered by DockerHub and then, another hour of futzing around trying to figure out my PostgreSQL JDBC driver doesn&amp;#39;t work anymore (I inadvertently upgraded from JDK 1.7 to 1.8) and why I can&amp;#39;t seem to upgrade it (Kubernetes caches Docker images unless you set imagePullPolicy to always). In the end, I got it working. This post is written in the yet to be officially release Apache Roller 6.0.0-SNAPSHOT version.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Side note&lt;/b&gt;: the new rich-text editor in Roller is now &lt;a href=&quot;https://summernote.org&quot;&gt;Summernote&lt;/a&gt; and it seems quite nice. I need to tweak it a bit because there is currently no way to set the font or add a link unless you switch to raw HTML mode.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/roller-6-snapshot</guid>
    <title>Roller&amp;#39;s new web UI</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/roller-6-snapshot</link>
    <pubDate>Mon, 21 Jan 2019 15:59:00 +0000</pubDate>
    <category>Roller</category>
    <category>asf</category>
    <category>docker</category>
    <category>postgresql</category>
    <category>tomcat</category>
<description>&lt;p&gt;About three years ago I decided to modernize and improve the &lt;a href=&quot;http://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt; web UI by rewriting the JSP pages to use the &lt;a href=&quot;https://github.com/struts-community-plugins/struts2-bootstrap&quot;&gt;Struts 2 Bootstrap&lt;/a&gt; tags, which use Twitter&amp;#39;s &lt;a href=&quot;https://getbootstrap.com/docs/3.3/components/&quot;&gt;Bootstrap v3&lt;/a&gt; components and JavaScipt. I also wanted to replace all the HTML &lt;code&gt;table&lt;/code&gt;-based formatting with &lt;code&gt;div&lt;/code&gt;&amp;#39;s and Bootstrap, do a bunch of other improvements and make Roller&amp;#39;s web UI less clunky and annoying.&lt;/p&gt;

&lt;p&gt;Converting Roller&amp;#39;s eight-five JSP pages was a big task and I did not have much time for it. That&amp;#39;s why it took three years. Ironically, the Roller modernization project leaves Roller three years out of date. Still, I think it is a huge improvement over the Roller v5 web UI and I want to get it released in Roller v6. Currently, this work is available as &lt;a href=&quot;https://github.com/apache/roller/pull/22&quot;&gt;Pull Request #22&lt;/a&gt; and you can find some screenshots there too. Here&amp;#39;s one:&lt;/p&gt;

&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/23986d06-37aa-48c1-8301-90419520953b&quot; alt=&quot;screenshot of Roller Edit Entry page&quot; style=&quot;max-width:30em;padding:1em;border:1px;&quot;&gt;

&lt;h4&gt;Try it with Docker-Compose&lt;/h4&gt;

&lt;p&gt;I also did some work to make it super-easy to try the Roller v6 snapshot pre-release for yourself, by using &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt;. You don&amp;#39;t have to fiddle with Tomcat or PostgreSQL. You can find a simple Dockerfile for running Roller v2 snapshot and a &lt;b&gt;docker-compose.yml&lt;/b&gt; file linked below. And you can find a Docker image in my &lt;a href=&quot;https://cloud.docker.com/u/snoopdave/repository/docker/snoopdave/roller&quot;&gt;DockerHub repo&lt;/a&gt;.&lt;/p&gt;

If you want to try Roller v6 snapshot, here&amp;#39;s what you need to do:

&lt;div style=&quot;margin-left:2em;&quot;&gt;
&lt;p&gt;&lt;b&gt;1&lt;/b&gt; - If you don&amp;#39;t aleady have it, install Docker&lt;/p&gt;

&lt;p&gt;&lt;b&gt;2&lt;/b&gt; - Create a directory on your computer where you want Roller to store it&amp;#39;s data.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;3&lt;/b&gt; - Save this file &lt;a href=&quot;https://github.com/apache/roller/blob/0e77733a4567ad19926ea81b6d7afb0de376b908/deployment/docker-compose/docker-compose.yml&quot;&gt;&lt;b&gt;docker-compose.yml&lt;/b&gt;&lt;/a&gt; to that new directory.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;4&lt;/b&gt; - Open a shell in that new directory and run:&lt;/p&gt;

       &lt;p&gt;&lt;pre&gt;docker-compose up&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;5&lt;/b&gt; - Watch the PostgreSQL and Roller startup logs scroll by&lt;/p&gt;

&lt;p&gt;&lt;b&gt;6&lt;/b&gt; - When the log scroll slows go to http://localhost:8080 to access Roller and go through the initial setup.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Alternatively, if you want to try Roller the hard way, you can get the regular-style v6 SNAPSHOT release files here &lt;a href=&quot;https://dist.apache.org/repos/dist/dev/roller/roller-6.0/v6.0.0/&quot;&gt;roller/roller-6.0/v6.0.0&lt;/a&gt;.&lt;p&gt;

&lt;h4&gt;Let us know how it goes&lt;/h4&gt;

&lt;p&gt;I hope you&amp;#39;ll give Roller v6 snapshot a try and let the project know how it can be improved for your use.  Send feedback to the Roller &lt;a href=&quot;https://cwiki.apache.org/confluence/display/ROLLER/Roller+Mailing+Lists&quot;&gt;mailing lists&lt;/a&gt; or ttweet at us at &lt;a href=&quot;https://twitter.com/apache_roller&quot;&gt;@apache_roller&lt;/a&gt;.&lt;/p&gt;

&lt;br&gt;&lt;/p&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/powered-by-kubernetes</guid>
    <title>Powered by Kubernetes</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/powered-by-kubernetes</link>
    <pubDate>Tue, 13 Mar 2018 14:29:55 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>docker</category>
    <category>kubernetes</category>
    <category>postgres</category>
    <category>roller</category>
<description>
&lt;p&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/0a80ebe2-257d-48f3-84ba-9aff47c8c3c8&quot; alt=&quot;kubernetes logo&quot; align=&quot;right&quot;&gt; Just a quick note to say that I ditched Docker Swarm and now this rarely updated blog is powered by &lt;a href=&quot;https://kubernetes.io&quot;&gt;Kubernetes&lt;/a&gt;. Total overkill, I know. Like Roller itself, I did it as a learning exercise. I hope to blog more about what I learned by doing this. For now, here&amp;#39;s a quick summary of what I&amp;#39;ve done so far.&lt;/p&gt;


&lt;p&gt;&lt;b&gt;Created a cluster&lt;/b&gt;&lt;/p&gt;


&lt;p&gt;I created a 2-node Kubernetes cluster on &lt;a href=&quot;https://www.digitalocean.com&quot;&gt;Digital Ocean&lt;/a&gt; using some hand-crafted &lt;a href=&quot;https://www.ansible.com&quot;&gt;Ansible&lt;/a&gt; scripts that call &lt;code&gt;apt-get&lt;/code&gt; to install and &lt;code&gt;kubeadm&lt;/code&gt; to start Kubernetes.  I considered using &lt;a href=&quot;https://www.digitalocean.com/community/projects/kubernetes-on-digital-ocean-typhoon&quot;&gt;Typhoon&lt;/a&gt; to create the cluster, but I really wanted to learn how to install Kubernetes &amp;quot;from scratch&amp;quot;.&lt;/p&gt;


&lt;p&gt;&lt;b&gt;Ran two Ingress Controllers&lt;/b&gt;&lt;/p&gt;


&lt;p&gt;To avoid using Digital Ocean&amp;#39;s &lt;a href=&quot;https://www.digitalocean.com/products/load-balancer/&quot;&gt;$20/month load balancer&lt;/a&gt; I&amp;#39;m running an Nginx Ingress controller on each node, and pinning containers to nodes using labels and nodeSelectors. I had to borrow &lt;a href=&quot;https://github.com/poseidon/typhoon/tree/master/addons/nginx-ingress/digital-ocean&quot;&gt;Nginx Controller setup files from the Typhoon project&lt;/a&gt; because I&amp;#39;m still kind of bewildered by Ingresses.&lt;/p&gt;


&lt;p&gt;&lt;b&gt;Deployed my containers&lt;/b&gt;&lt;/p&gt;


&lt;p&gt;Next, I wrote Kubernetes YAML files for deploying my containers: a private Docker Registry, PostgreSQL and my custom Roller image.  Getting the private registry working properly was the biggest challenge. I need private because I don&amp;#39;t want to make my custom Roller image public. Next, I&amp;#39;ll install Jenkins next for CI/CD of my custom Roller build via the &lt;a href=&quot;https://github.com/jenkinsci/kubernetes-plugin&quot;&gt;Jenkins Kubernetes plugin&lt;/a&gt;. &lt;/p&gt;


&lt;p&gt;Let me know if there are any aspects of this that you&amp;#39;d like to see covered in a blog entry, or suggestions for running the cluster without two Ingress Controllers. I&amp;#39;ve already got a post cooking about installing a TLS secured Docker Registry on Kubernetes.&lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/powered-by-postgresql</guid>
    <title>Powered by Postgresql and Docker Swarm</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/powered-by-postgresql</link>
    <pubDate>Tue, 7 Nov 2017 21:52:40 +0000</pubDate>
    <category>Roller</category>
    <category>asf</category>
    <category>docker</category>
    <category>postgresql</category>
    <category>swarm</category>
<description>It was somewhat painful but due to some problems with MySQL and Docker, and some general uneasiness with MySQL, I switched this site from MySQL v5.7 to &lt;a href=&quot;https://www.postgresql.org&quot;&gt;PostgreSQL&lt;/a&gt; v10. I also switched over to &lt;a href=&quot;https://docs.docker.com/engine/swarm/&quot;&gt;Docker Swarm&lt;/a&gt;. Here&amp;#39;s the Docker-Compose file that I&amp;#39;m using now to run this site:

&lt;pre&gt;
version: &amp;#39;3.2&amp;#39;

services:

   postgresql:
      image: &amp;quot;postgres:10.0&amp;quot;
      ports:
         - &amp;quot;5432:5432&amp;quot;
      deploy:
         resources:
           limits:
              memory: 50M
      volumes:
         - type: bind
           source: /var/lib/postgresql/data
           target: /var/lib/postgresql/data
      environment:
        - POSTGRES_USER=roller
        - POSTGRES_DB=rollerdb
        - POSTGRES_PASSWORD_FILE=/run/secrets/pg_passwd
      secrets:
        - source: db_passwd
          target: pg_passwd

   roller:
      image: &amp;quot;rwo:latest&amp;quot;
      ports:
        -  &amp;quot;80:8080&amp;quot;
      depends_on:
        - postgresql
      deploy:
         resources:
           limits:
              memory: 800M
      volumes:
        - type: bind
          source: /var/lib/roller
          target: /var/lib/roller
      environment:
        - DB_HOST=postgresql
        - STORAGE_ROOT=/var/lib/roller
        - JAVA_OPTS=&amp;quot;-Xmx700m&amp;quot;

secrets:
  db_passwd:
    file: ./db_passwd.txt
&lt;/pre&gt;

It was a pain, but sometimes pain = gain and I learned a lot. I&amp;#39;m hoping the site will be a bit more stable now. </description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/modernizing-the-roller-ui</guid>
    <title>Modernizing the Roller UI</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/modernizing-the-roller-ui</link>
    <pubDate>Sat, 11 Jun 2016 18:28:36 +0000</pubDate>
    <category>Roller</category>
    <category>asf</category>
    <category>bootstrap</category>
<description>
&lt;p&gt;&lt;a href=&quot;http://getbootstrap.com&quot;&gt;&lt;/a&gt; I don&amp;#39;t blog very often but I still find time to work on my blog&amp;#39;s software: &lt;a href=&quot;http://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt;. &lt;/p&gt;


&lt;p&gt;Recently, I decided to focus on improving Roller&amp;#39;s ancient Struts 2-based user interface (UI). I had considered adding a comprehensive API to Roller and building a new UI based on that API, but wow that is a huge amount of work. Instead, I decided to modernize the Roller UI by using Twitter&amp;#39;s &lt;a href=&quot;http://getbootstrap.com&quot;&gt;Bootstrap&lt;/a&gt; components and CSS styles.&lt;/p&gt;


&lt;p&gt;So far, I&amp;#39;ve devoted a couple of weekends to this work and made some pretty good progress. I&amp;#39;m about half-way done. I&amp;#39;m using the &lt;a href=&quot;https://cwiki.apache.org/confluence/display/S2PLUGINS/Bootstrap+Plugin&quot;&gt;Struts2-Bootstrap&lt;/a&gt; plugin, adding better client-side form validation with JavaScript and doing my best to improve the overall user experience. You can see an album of the pages I&amp;#39;ve done so far on Flickr: &lt;a href=&quot;https://www.flickr.com/photos/snoopdave/albums/72157666773620323&quot;&gt;&lt;br&gt;
Roller UI with Bootstrap&lt;/a&gt;. &lt;/p&gt;


&lt;p&gt;I would love any contributions, so if you are interested in helping out, please submit Pull Requests against the &lt;a href=&quot;https://github.com/apache/roller/tree/bootstrap-ui&quot;&gt;bootstrap-ui&lt;/a&gt; branch in the Apache Roller repo on GitHub.&lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/mandelbrot-viewer-typescript-canvas</guid>
    <title>Fractal fun with HTML5 and TypeScript</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/mandelbrot-viewer-typescript-canvas</link>
    <pubDate>Mon, 11 Jan 2016 10:00:00 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>canvas</category>
    <category>html5</category>
    <category>typescript</category>
<description>&lt;p&gt;
&lt;a href=&quot;https://github.com/snoopdave/mbcanvas&quot;&gt;MbCanvas&lt;/a&gt; is a fun project that I did in 2015: a simple 
&lt;a href=&quot;https://en.wikipedia.org/wiki/Mandelbrot_set&quot;&gt;Mandelbrot Set&lt;/a&gt; viewer written in 
&lt;a href=&quot;http://www.typescriptlang.org/Tutorial&quot;&gt;Typescript&lt;/a&gt; and using the 
&lt;a href=&quot;http://www.w3.org/TR/2dcontext/&quot;&gt;HTML5 Canvas&lt;/a&gt;.  

I did the project to learn more about Typescript and the HTML5 Canvas and I must say, Typescript very nice -- so much easier to read and write than plain old JavaScript, at least for me.&lt;/p&gt;

&lt;p&gt;Here&amp;#39;s an example image from the viewer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/d82c09d5-2037-42aa-9b73-6927a754fa36&quot; alt=&quot;mandelbrot set image&quot;&gt;&lt;/p&gt;

&lt;p&gt;The project is fairly easy to build if you&amp;#39;ve got Node and NPM installed, or you can play around with it here: &lt;a href=&quot;http://rollerweblogger.org/mbcanvas&quot;&gt;mbcanvas - Mandelbrot viewer in TypeScript&lt;/a&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/usergrid-vagrant-updated-for-usergrid</guid>
    <title>Usergrid-Vagrant updated for Usergrid 2</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/usergrid-vagrant-updated-for-usergrid</link>
    <pubDate>Sat, 2 Jan 2016 11:44:45 +0000</pubDate>
    <category>Open Source</category>
    <category>asf</category>
    <category>baas</category>
    <category>usergrid</category>
<description>&lt;img align=&quot;right&quot; alt=&quot;GitHub logo&quot; src=&quot;https://rollerweblogger.org/roller/mediaresource/153bf8c1-cdef-4510-ab43-605c1fa0c3e9&quot;&gt;

&lt;p&gt;If you&amp;#39;re interested in trying the not-yet-released &lt;a href=&quot;http://usergrid.apache.org&quot;&gt;Apache Usergrid&lt;/a&gt; 2 you might want to checkout my &lt;a href=&quot;https://github.com/snoopdave/usergrid-vagrant&quot;&gt;Usergrid-Vagrant project on GitHub&lt;/a&gt;.  I just updated the project to support Usergrid 2, using the latest code from the Usergrid &amp;quot;release&amp;quot; brach.  The big changes were switching to OpenJDK 8 and adding ElasticSearch.  I also rewrote the scripts to use plain old Bash instead of Groovy.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/snoopdave/usergrid-vagrant&quot;&gt;https://github.com/snoopdave/usergrid-vagrant&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want the old Usergrid 1 Vagrant-file then checkout the &amp;quot;1.x&amp;quot; branch.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/moved-blog-to-digitalocean</guid>
    <title>Now hosted on DigitalOcean</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/moved-blog-to-digitalocean</link>
    <pubDate>Sat, 2 May 2015 13:46:00 +0000</pubDate>
    <category>Roller</category>
    <category>cloud</category>
<description>
&lt;p&gt;After &lt;a href=&quot;http://rollerweblogger.org/roller/entry/if_you_ve_been_following&quot;&gt;thirteen years&lt;/a&gt; of hosting this blog at &lt;a href=&quot;http://www.kattare.com/&quot;&gt;Kattare.com&lt;/a&gt;, I&amp;#39;ve moved it over to &lt;a href=&quot;http://digitalocean.com&quot;&gt;DigitalOcean&lt;/a&gt;. Kattare was great, but nowadays I prefer managing my own server and DigitalOcean makes that very easy -- and costs less ($10/month vs. $26/month at Kattare).&lt;/p&gt;


&lt;p&gt;The move was easy, or as easy as setting up OpenJDK 8, Tomcat 7 and MySQL 5.5 can be. I only hit one little snag. Once I added the Roller WAR to Tomcat, Tomcat would hang on startup. I used jstack to look at the Java VM threads and found some clues that led me to a post on ServerFault.com: &lt;a href=&quot;http://serverfault.com/questions/655616/tomcat7-hangs-on-deploying-apps&quot;&gt;Tomcat 7 hangs on deploying apps&lt;/a&gt;. As recommended in that post, I added &lt;code&gt;-Djava.security.egd=file:/dev/./urandom&lt;/code&gt; to my CATALINA_OPTS and was back in action.&lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/shiro-not-spring</guid>
    <title>Apache Shiro for authentication in Roller</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/shiro-not-spring</link>
    <pubDate>Mon, 9 Feb 2015 07:27:50 +0000</pubDate>
    <category>Roller</category>
    <category>asf</category>
    <category>opensource</category>
    <category>shiro</category>
<description>&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/80d2d56d-a8cd-43f2-825d-d0474b67139b&quot; alt=&quot;Shiro logo&quot; align=&quot;right&quot;&gt;


&lt;p&gt;
This is the third of my 2014 side projects that I&amp;#39;m sharing and one that involves the 
&lt;a href=&quot;http://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt; blog server and the 
&lt;a href=&quot;http://shiro.apache.org&quot;&gt;Apache Shiro&lt;/a&gt; security framework. 

You might find this interesting if you&amp;#39;re considering using Shiro for authentication and authorization, or if your interested in how security works in Apache Roller.
&lt;/p&gt;


&lt;p&gt;
Inspired by my work with &lt;a href=&quot;http://emberjs.com&quot;&gt;Ember.js&lt;/a&gt; in Fall 2014, I started thinking about what it would take to build an Ember.js-based editor/admin interface for Apache Roller.

To do that, I&amp;#39;d need to add a comprehensive REST API to Roller, and I&amp;#39;d need a way to implement secrity for the new API.

I&amp;#39;ve enjoyed working with &lt;a href=&quot;http://shiro.apache.org&quot;&gt;Apache Shiro&lt;/a&gt;, so I decided that a good first step would be to figure out how to use Apache Shiro in Roller for Roller&amp;#39;s existing web interface.
&lt;/p&gt;

&lt;p&gt;
Working over the winter break I was able to replace Roller&amp;#39;s existing Spring security implementation with Shiro and remove all Spring dependencies from my &lt;a href=&quot;https://github.com/snoopdave/rollarcus&quot;&gt;Rollarcus&lt;/a&gt; fork of Roller. 

Below I&amp;#39;ll describe what I had to do get Shiro working for Form-base Authentication in Roller.
&lt;/p&gt;

&lt;h3&gt;Creating a Shiro Authorizing Realm&lt;/h3&gt;

&lt;p&gt;The first step in hooking Shiro into Roller is to implement a Shiro interface called &lt;code&gt;ShiroAuthorizingRealm&lt;/code&gt;. 

This interface enables Shiro to do username and password checks for users when they attempt to login, and to get the user&amp;#39;s roles. 
&lt;/p&gt;

&lt;p&gt;
Below is the first part of the class, which includes the &lt;code&gt;doGetAuthenticationInfo()&lt;/code&gt; method, which returns the &lt;code&gt;AuthenticationInfo&lt;/code&gt; for a user specified by an &lt;code&gt;AuthenticationToken&lt;/code&gt; that includes the user&amp;#39;s username. 

In other words, this method allows Shiro to look-up a user by providing a username and get back the user&amp;#39;s (hashed) password, so that Shiro can validate a user&amp;#39;s username and password.
&lt;/p&gt;

&lt;b&gt;ShiroAuthorizingRealm.java (&lt;a href=&quot;https://github.com/snoopdave/rollarcus/blob/shiro_not_spring/app/src/main/java/org/apache/roller/weblogger/auth/ShiroAuthorizingRealm.java&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
public class ShiroAuthorizingRealm extends AuthorizingRealm {

    public ShiroAuthorizingRealm(){
        setName(&amp;quot;ShiroAuthorizingRealm&amp;quot;);
        setCredentialsMatcher(
            new HashedCredentialsMatcher(Sha1Hash.ALGORITHM_NAME));
    }

    @Override
    public AuthenticationInfo doGetAuthenticationInfo(
        AuthenticationToken authToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authToken;

        User user;
        try {
            user = loadUserByUsername( token.getUsername() );

        } catch (WebloggerException ex) {
            throw new AuthenticationException(
                &amp;quot;Error looking up user &amp;quot; + token.getUsername(), ex);
        }

        if (user != null) {
            return new SimpleAuthenticationInfo( 
                user.getUserName(), user.getPassword(), getName());

        } else {
            throw new AuthenticationException(
                &amp;quot;Username not found: &amp;quot; + token.getUsername());
        }
    }

&lt;/pre&gt;

&lt;p&gt;In the code above you can see how we pull the username out of the &lt;code&gt;authToken&lt;/code&gt; provided by Shiro and we call a method, &lt;code&gt;loadUserByUserName()&lt;/code&gt;, which uses Roller&amp;#39;s Java API to load a Roller user object specified by name.&lt;/p&gt;

&lt;p&gt;The next method of interest is &lt;code&gt;doGetAuthorizationInfo()&lt;/code&gt;, which allows Shiro to look-up a user&amp;#39;s Role. This allows Shiro to detmerine if the user is a Roller admin user or a blog editor.&lt;/p&gt;

&lt;b&gt;ShiroAuthorizingRealm.java (continued)&lt;/b&gt;
&lt;pre class=&quot;brush:js&quot;&gt;

    public AuthorizationInfo doGetAuthorizationInfo(
        PrincipalCollection principals) {

        String userName = (String)
            (principals.fromRealm(getName()).iterator().next());

        User user;
        try {
            user = loadUserByUsername( userName );
        } catch (WebloggerException ex) {
            throw new RuntimeException(&amp;quot;Error looking up user &amp;quot; + userName, ex);
        }

        Weblogger roller = WebloggerFactory.getWeblogger();
        UserManager umgr = roller.getUserManager();

        if (user != null) {
            List roles;
            try {
                roles = umgr.getRoles(user);
            } catch (WebloggerException ex) {
                throw new RuntimeException(
                    &amp;quot;Error looking up roles for user &amp;quot; + userName, ex);
            }
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            for ( String role : roles ) {
                info.addRole( role );
            }
            log.debug(&amp;quot;Returning &amp;quot; + roles.size() 
                + &amp;quot; roles for user &amp;quot; + userName + &amp;quot; roles= &amp;quot; + roles);
            return info;

        } else {
            throw new RuntimeException(&amp;quot;Username not found: &amp;quot; + userName);
        }
    }
&lt;/pre&gt;

&lt;p&gt;In the code above you can see that we use the &lt;code&gt;loadUserByUsername()&lt;/code&gt; too look-up a user by username, then we use Roller&amp;#39;s Java API to get the user&amp;#39;s roles. We add those roles to an instance of the Shiro class &lt;code&gt;SimpleAuthorizationInfo&lt;/code&gt; and return it to Shir.&lt;/p&gt;

&lt;h3&gt;Creating a Shiro Authorizing Filter&lt;/h3&gt;

&lt;p&gt;Now that we&amp;#39;ve implementated a realm, we&amp;#39;ve provided Shiro with everything needed to authenticate Roller users and get access to Roller user role information. Next, we need to configure Shiro to enforce roles for the URL apths found in Roller. Shiro includes a &lt;code&gt;RolesAuthorizationFilter&lt;/code&gt;, which is close to what we need but not exactly right for Roller. I had to extend Shiro&amp;#39;s roles filter so that we can allow a user who has any (not all) of the required roles for a resource.&lt;/p&gt;

&lt;b&gt;RollerRolesAuthorizationFilter.java (&lt;a href=&quot;https://github.com/snoopdave/rollarcus/blob/shiro_not_spring/app/src/main/java/org/apache/roller/weblogger/rest/auth/RollerRolesAuthorizationFilter.java&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
public class RollerRolesAuthorizationFilter 
    extends RolesAuthorizationFilter {

    @Override
    public boolean isAccessAllowed( 
        ServletRequest request, 
        ServletResponse response, 
        Object mappedValue) throws IOException {

        final Subject subject = getSubject(request, response);
        final String[] roles = (String[]) mappedValue;

        if (roles == null || roles.length == 0) {
            return true;
        }

        // user is authorized if they have ANY of the roles
        for (String role : roles) {
            if (subject.hasRole(role)) {
                return true;
            }
        }
        return false;
    }
}
&lt;/pre&gt;


&lt;h3&gt;Configuring Shiro for Roller&lt;/h3&gt;

&lt;p&gt;Now that we&amp;#39;ve seen the Java code needed to hook Shiro into Roller, lets look at how we configure Shiro to use that code. We do that using the Shiro configuration file: shiro.ini, as shown below.&lt;/p&gt;

&lt;b&gt;shiro.ini (&lt;a href=&quot;https://github.com/snoopdave/rollarcus/blob/shiro_not_spring/app/src/main/resources/shiro.ini&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
[main]

defaultRealm = org.apache.roller.weblogger.auth.ShiroAuthorizingRealm
securityManager.realms = $defaultRealm

cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
securityManager.cacheManager = $cacheManager

authc = org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authc.loginUrl = /roller-ui/login.rol
authc.successUrl = /roller-ui/menu.rol

rollerroles = org.apache.roller.weblogger.rest.auth.RollerRolesAuthorizationFilter

[urls]

/roller-ui/login.rol          = authc
/roller-ui/login-redirect.rol = authc, rollerroles[admin,editor]
/roller-ui/profile**          = authc, rollerroles[admin,editor]
/roller-ui/createWeblog**     = authc, rollerroles[admin,editor]
/roller-ui/menu**             = authc, rollerroles[admin,editor]
/roller-ui/authoring/**       = authc, rollerroles[admin,editor]
/roller-ui/admin/**           = authc, rollerroles[admin]
/rewrite-status/**            = authc, rollerroles[admin]
/roller-services/rest/**      = authcBasic, rollerroles[admin,editor]
&lt;/pre&gt;


&lt;p&gt;
In the configuration file above, you see how we hook in the new &lt;code&gt;ShiroAuthorizingRealm&lt;/code&gt; on line 3.  

The next couple lines are boiler-plate code to hook in Shiro&amp;#39;s caching mechanism and then, on line 9, we configure an authentication method called &lt;code&gt;authc&lt;/code&gt;, which is configured to use Shiro&amp;#39;s Form Authentication feature. 

And, on line 13, we hook in our new &lt;code&gt;RollerRolesAuthorizationFilter&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Next, we tell Shiro that the login page for Roller is &lt;code&gt;/roller-ui/login.rol&lt;/code&gt; and which page to direct a user to on a successful login, &lt;code&gt;/roller-ui/menu.rol&lt;/code&gt;, if the user did not specify which page they wanted to access.

And finally, on lines 17-25, you see the list of Roller URL patterns that need protection, which authentication method to use (authc or authcBasic) and the authorization filter and roles required for access to the URL pattern.
&lt;/p&gt;


&lt;h3&gt;Wrapping up...&lt;/h3&gt;

&lt;p&gt;
That&amp;#39;s all there is to the story of Roller and Shiro so far. 

I was able to get Roller&amp;#39;s form-based authentication working with Shiro, but I did not try to test with OpenID or LDAP, so I assume more work will be necessary to get them working. 

I did the work in my experimental 
&lt;a href=&quot;https://github.com/snoopdave/rollarcus&quot;&gt;Rollarcus&lt;/a&gt; fork of Roller. 

You can get the code from the 
&lt;a href=&quot;https://github.com/snoopdave/rollarcus/tree/shiro_not_spring&quot;&gt;shiro_not_spring&lt;/a&gt; branch. 

Pull requests are quite welcome as are suggestions for improvement. 

Please let me know if you see anything wrong in the above code.
&lt;/p&gt;

&lt;p&gt;
This work may not find its way into Roller proper, but it plays a part in my the next side-project that I will share: A REST API for Roller with JAX-RS. 
&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/usergrid-ember-pt-2</guid>
    <title>Usergrid and Ember.js - part 2</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/usergrid-ember-pt-2</link>
    <pubDate>Mon, 26 Jan 2015 10:23:01 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>baas</category>
    <category>ember</category>
    <category>javascript</category>
    <category>usergrid</category>
<description>&lt;p&gt;In &lt;a href=&quot;http://rollerweblogger.org/roller/entry/usergrid-ember-pt-2&quot;&gt;part one&lt;/a&gt;, I explained the basics of the example Usergrid-Ember &amp;quot;Checkin&amp;quot; app, how the index page is displayed and how login is implemented. In part two, I&amp;#39;ll explain how Ember.js can be hooked into the Usergrid REST API to store and query JSON objects.&lt;/p&gt;

&lt;a href=&quot;http://emberjs.com&quot;&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/87879b47-6b14-4c18-a17e-576a68bf62a0&quot; alt=&quot;Ember logo&quot; align=&quot;right&quot;&gt;&lt;/a&gt;

&lt;p&gt;Ember.js includes a feature referred to as &lt;a href=&quot;http://emberjs.com/guides/models&quot;&gt;Ember-Data&lt;/a&gt;, which provides a persistence interface for storing and retrieving JavaScript objects that could be stored in memory, or stored on a server and accessed via REST API.&lt;/p&gt;

&lt;p&gt;To use Ember-Data with your REST API you&amp;#39;ve got to define an Ember-Data model and add an Ember-Data REST adapter. If your REST API differs from what Ember-Data expects then you will probably have to extend the built-in REST adapter to handle your URL pattens, and extend the built-in REST serializer to handle your JSON format. By extending Ember-Data in this way, you can use it to store and query data from Usergrid without using the Usergrid JavaScript SDK at all. Below I&amp;#39;ll explain what I had to do to make the Checkin app&amp;#39;s Activities collection available via Ember-Data.&lt;/p&gt;


&lt;h3&gt;Define Ember-Data models&lt;/h3&gt;

&lt;p&gt;Ember-Data expects each of your REST API collections to have a defined data model, one that extends the DS.Model class. Here&amp;#39;s what I added for the Activities collection:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;From app.js (&amp;lt;a href=&amp;quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L18&amp;quot;
&amp;gt;link)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
App.Activity = DS.Model.extend({
  uuid: DS.attr(&amp;#39;string&amp;#39;),
  type: DS.attr(&amp;#39;string&amp;#39;),
  content: DS.attr(&amp;#39;string&amp;#39;),
  location: DS.attr(&amp;#39;string&amp;#39;),
  created: DS.attr(&amp;#39;date&amp;#39;),
  modified: DS.attr(&amp;#39;date&amp;#39;),
  actor: DS.attr(&amp;#39;string&amp;#39;),
  verb: DS.attr(&amp;#39;string&amp;#39;),
  published: DS.attr(&amp;#39;date&amp;#39;),
  metadata: DS.attr(&amp;#39;string&amp;#39;)
});
&lt;/pre&gt;


&lt;h3&gt;Create a custom RESTAdapter&lt;/h3&gt;

&lt;p&gt;The Ember-Data REST adapter expects a REST API to follow some common conventions for URL patterns and for JSON data formats. For example, if your REST API provides a collection of cats then Ember-Data will expect your REST API to work like so:


&lt;p&gt;&lt;b&gt;What Ember-Data expects for a cats collection:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;GET /cats&lt;/b&gt; - get collection of cats&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POST /cats&lt;/b&gt; - create new cat.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GET /cats/{cat-id}&lt;/b&gt; - get cat specified by ID.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PUT /cats/{cat-id}&lt;/b&gt; - update cat specified by ID.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DELETE /cats/{cat-id}&lt;/b&gt; - delete cat specified by ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usergrid follows the above conventions for collections, but there are some exceptions. For example, the Usergrid Activities collection. A GET on the &lt;code&gt;/activities&lt;/code&gt; path will return the Activities of the users that you (i.e. the currently authenticated user) follow. You don&amp;#39;t POST new activities there, instead you post to your own Activities collection at the path &lt;code&gt;/users/{your-user-id}/activities&lt;/code&gt;. It works like this:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Usergrid&amp;#39;s Activities collection:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;GET /activities&lt;/b&gt; - get Activities of all users that you follow.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POST /user/{user-id}/activities&lt;/b&gt; - create new Activity for user specified by ID&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GET /user/{user-id}/activities&lt;/b&gt; - get Activities for one specific user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To adapt the Activities collection to Ember-Data, I decided to create a new model called NewActivity. A NewActivity represents the data needed to create a new Activity, here&amp;#39;s the model:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;From app.js (&amp;lt;a href=&amp;quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L132&amp;quot;
&amp;gt;Link)&lt;/b&gt;&lt;/p&gt;&lt;pre class=&quot;brush:js&quot;&gt;
// Must have a special model for new activity because new 
// Activities must be posted to the path /{org}/{app}/users/activities, 
// instead of the path /{org}/{app}/activities as Ember-Data expects.
App.NewActivity = DS.Model.extend({
  content: DS.attr(&amp;#39;string&amp;#39;),
  location: DS.attr(&amp;#39;string&amp;#39;),
  actor: DS.attr(&amp;#39;string&amp;#39;),
  verb: DS.attr(&amp;#39;string&amp;#39;)
});
&lt;/pre&gt;

&lt;p&gt;Then, in Checkin&amp;#39;s custom REST adapter, I added logic to the &lt;code&gt;pathForType()&lt;/code&gt; function to ensure that NewActivities are posted to the correct path. Here&amp;#39;s the adapter:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;From app.js (&amp;lt;a href=&amp;quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L40&amp;quot;
&amp;gt;Link)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
App.ApplicationAdapter = DS.RESTAdapter.extend({

  host: Usergrid.getAppUrl(),

  headers: function() { 
    if ( localStorage.getItem(&amp;quot;access_token&amp;quot;) ) {
      return { &amp;quot;Authorization&amp;quot;: &amp;quot;Bearer &amp;quot; 
          + localStorage.getItem(&amp;quot;access_token&amp;quot;) }; 
    } 
    return {};
  }.property().volatile(), // ensure value not cached

  pathForType: function(type) {
    var ret = Ember.String.camelize(type);
    ret = Ember.String.pluralize(ret);

    if ( ret == &amp;quot;newActivities&amp;quot; ) {
      // Must have a special logic here for new activity 
      // because new Activities must be posted to the 
      // path /{org}/{app}/users/activities, instead of the 
      // path /{org}/{app}/activities as Ember-Data expects.
      ret = &amp;quot;/users/&amp;quot; + Usergrid.user.username + &amp;quot;/activities&amp;quot;;
    }
    return ret;
  }

});
&lt;/pre&gt;

&lt;p&gt;You can see a couple of other interesting things in the example above. First, there&amp;#39;s the &lt;code&gt;host&lt;/code&gt; field which specifies the base-URL of the REST API for the Checkin app. Next, there&amp;#39;s the &lt;code&gt;headers()&lt;/code&gt; function, which ensures that every request carries the &lt;code&gt;access_token&lt;/code&gt; that was acquired during login.&lt;/p&gt;


&lt;h3&gt;Create a custom RESTSerializer&lt;/h3&gt;

&lt;p&gt;Ember-Data also has expectations about the JSON format returned by a REST API. Unfortunately, what Ember-Data expects and what Usergrid provides are quite different. The two examples below illustrate the differences:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Ember-Data vs. Usergrid JSON formats&lt;/b&gt;&lt;/p&gt;
&lt;table&gt;
   &lt;tr&gt;
      &lt;td valign=&quot;top&quot;&gt;

&lt;p&gt;Ember-Data expects collections like this:&lt;/p&gt;
&lt;pre&gt;
{
   cats: [{
       &amp;quot;id&amp;quot;: &amp;quot;6b2360d0&amp;quot;,
       &amp;quot;name&amp;quot;: &amp;quot;enzo&amp;quot;,
       &amp;quot;color&amp;quot;: &amp;quot;orange&amp;quot;
   },{
       &amp;quot;id&amp;quot;: &amp;quot;a01dfaa0&amp;quot;,
       &amp;quot;name&amp;quot;: &amp;quot;bertha&amp;quot;,
       &amp;quot;color&amp;quot;: &amp;quot;tabby&amp;quot;
   }]
}






&lt;/pre&gt;

      &lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
      &lt;td valign=&quot;top&quot;&gt;

&lt;p&gt;Usergrid returns collections like this:&lt;/p&gt;
&lt;pre&gt;
{
   action: &amp;quot;get&amp;quot;,
   path: &amp;quot;/cats&amp;quot;,
   count: 2,
   entities: [{
       &amp;quot;uuid&amp;quot;: &amp;quot;6b2360d0&amp;quot;,
       &amp;quot;type&amp;quot;: &amp;quot;cat&amp;quot;,
       &amp;quot;name&amp;quot;: &amp;quot;enzo&amp;quot;,
       &amp;quot;color&amp;quot;: &amp;quot;orange&amp;quot;
   },{
       &amp;quot;uuid&amp;quot;: &amp;quot;a01dfaa1&amp;quot;,
       &amp;quot;type&amp;quot;: &amp;quot;cat&amp;quot;,
       &amp;quot;name&amp;quot;: &amp;quot;bertha&amp;quot;,
       &amp;quot;color&amp;quot;: &amp;quot;tabby&amp;quot;
   }]
}

&lt;/pre&gt;

      &lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td valign=&quot;top&quot;&gt;

&lt;p&gt;Ember-Data expects individual objects like this:&lt;/p&gt;
&lt;pre&gt; 
{
   cat: {
       &amp;quot;id&amp;quot;: &amp;quot;a01dfaa0&amp;quot;,
       &amp;quot;name&amp;quot;: &amp;quot;bertha&amp;quot;,
       &amp;quot;color&amp;quot;: &amp;quot;tabby&amp;quot;
   }
}
&lt;/pre&gt; 

      &lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
      &lt;td valign=&quot;top&quot;&gt;

&lt;p&gt;Usergrid returns individual objects like this:&lt;/p&gt;
&lt;pre&gt; 
{
   &amp;quot;id&amp;quot;: &amp;quot;a01dfaa0&amp;quot;,
   &amp;quot;type&amp;quot;: &amp;quot;cat&amp;quot;,
   &amp;quot;name&amp;quot;: &amp;quot;bertha&amp;quot;,
   &amp;quot;color&amp;quot;: &amp;quot;tabby&amp;quot;
}

&lt;/pre&gt; 

      &lt;/td&gt;
   &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;You can see two differences above. Ember-Data expects JSON objects to be returned with a &amp;quot;type key&amp;quot; which you can see above: the &amp;quot;cats&amp;quot; field in the collection and the &amp;quot;cat&amp;quot; field in the individual object. Also, Ember-Data expects an object&amp;#39;s ID field to be named &amp;quot;id&amp;quot; but Usergrid returns it as &amp;quot;uuid.&amp;quot;&lt;/p&gt;

&lt;p&gt;The deal with these differences, the Checkin app extends Ember-Data&amp;#39;s &lt;code&gt;DS.RESTSerializer&lt;/code&gt;. Here&amp;#39;s the code:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;From app.js (&amp;lt;a href=&amp;quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L75&amp;quot;
&amp;gt;Link)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
App.ApplicationSerializer = DS.RESTSerializer.extend({

  // Extract Ember-Data array from Usergrid response
  extractArray: function(store, type, payload) {

    // Difference: Usergrid does not return wrapper object with 
    // type-key. So here we grab the Usergrid Entities and stick 
    // them under a type-key
    var typeKey = payload.path.substring(1);
    payload[ typeKey ] = payload.entities;

    // Difference: Usergrid returns ID in &amp;#39;uuid&amp;#39; field, Ember-Data 
    // expects &amp;#39;id&amp;#39;. So here we add an &amp;#39;id&amp;#39; field for each Entity, 
    // with its &amp;#39;uuid&amp;#39; value.
    for ( var i in payload.entities ) {
      if ( payload.entities[i] &amp;&amp; payload.entities[i].uuid ) {
        payload.entities[i].id = payload.entities[i].uuid;
      }
    }
    return this._super(store, type, payload);
  },

  // Serialize Ember-Data object to Usergrid compatible JSON format
  serializeIntoHash: function( hash, type, record, options ) {

    // Usergrid does not expect a type-key
    record.eachAttribute(function( name, meta ) {
      hash[name] = record.get(name);
    });

    return hash;
  }
});
&lt;/pre&gt;

&lt;p&gt;In the code above you can see how the &lt;code&gt;extractArray()&lt;/code&gt; method moves the &amp;quot;entities&amp;quot; collection returned by Usergrid into a type-key field as expected by Ember-Data and how it copies the &amp;quot;uuid&amp;quot; field to add the &amp;quot;id&amp;quot; field that Ember-Data expects. &lt;/p&gt;

&lt;p&gt;We also need to transform the data that Ember-Data sends to Usergrid. You can see this above in the &lt;code&gt;serializeInHash()&lt;/code&gt; function, which ensures that when data is POSTed or PUT to Usergrid, the type key is removed because that&amp;#39;s what Usergrid expects.&lt;/p&gt;


&lt;h3&gt;Implementing Add-Checkin&lt;/h3&gt;

&lt;p&gt;To implement Add-Checkin, I added an HTML template called &amp;quot;add-checkin&amp;quot; to Checkin&amp;#39;s index.html file. The template displays an Add-Checkin form with two fields: one for content and one for the location. Here&amp;#39;s what it looks like in all its modal glory:&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;
&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/5bc05437-f090-409e-90e4-c0fd978ea3ca&quot; alt=&quot;screenshot of add-checkin page&quot;&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;p&gt;Both fields are simple strings (someday I&amp;#39;d like to extend Checkin to use location information from the browser). I won&amp;#39;t go into detail here, but it took a bit of research to figure out how to make a Bootstrap modal dialog work with Ember.js. Below you can see the add-checkin controller, which provides a &lt;code&gt;save()&lt;/code&gt; function to save a new checkin.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;From app.js (&amp;lt;a href=&amp;quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L390&amp;quot;
&amp;gt;Link&lt;/b&gt;)&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;
App.AddCheckinModalController = Ember.ObjectController.extend({

  actions: {

    save: function( inputs ) {

      var content = inputs.content;
      var location = inputs.location;
      var target = this.get(&amp;quot;target&amp;quot;);

      var activity = this.store.createRecord( &amp;quot;NewActivity&amp;quot;, {
        content: content,
        location: location,
        verb: &amp;quot;checkin&amp;quot;,
        actor: {
          username: Usergrid.user.username
        }
      });

      activity.save().then(
        function( success ) { 
          alert(&amp;quot;Saved&amp;quot;); 
        },
        function( error ) { 
          alert(&amp;quot;Error &amp;quot; + error.responseJSON.error_description); 
        }
      ); 

    } 
  }
});
&lt;/pre&gt;

&lt;p&gt;In the code above you can see how easy it is to access Usergrid data via Ember-Data now that we&amp;#39;ve got our custom REST adapter and serializer in place. We create a new Activity with a call to &lt;code&gt;this.store.createRecord()&lt;/code&gt; and to save it all we need to do is &lt;code&gt; activity.save()&lt;/code&gt;.&lt;/p&gt;


&lt;h4&gt;Time to wrap up...&lt;/h4&gt;

&lt;p&gt;To sum things up, here are some closing thoughts and observations.&lt;/p&gt; 

&lt;ul&gt;
   &lt;li&gt;If you are considering JavaScript MVC frameworks, then Ember.js is definitely worthy of  your consideration. The documentation makes it easy to learn and the community is friendly and helpful.&lt;/li&gt;
   &lt;li&gt;It would be great for Usergrid to provide an Ember.js SDK that makes it really easy to build apps with Ember.js and Usergrid.&lt;/li&gt;
   &lt;li&gt;Ember-Data is an integral part of Ember.js, something that you need to do pretty much anything, but it is treated as a separate package with separate documentation. That is somewhat confusing for a new user.&lt;/li&gt;
   &lt;li&gt;Ember-Data does not include built-in form validation so if your app includes a large number of non-trivial  forms, then you may prefer AngularJS over Ember.js. 
   &lt;li&gt;There is a &lt;a href=&quot;https://github.com/dockyard/ember-validations&quot;&gt;form validation plugin&lt;/a&gt; for Ember.js, but it requires the experimental &lt;a href=&quot;http://www.ember-cli.com&quot;&gt;Ember-CLI&lt;/a&gt; utility. I tried to use it, but Ember-CLI was unpleasnt enough that I gave up.&lt;/li&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;I appreciate any feedback you might have about this article, the Usergrid-Ember project and Apache Usergrid. If you want to see how the whole Usergrid-Ember project fits together, find it on GitHub here: &lt;a href=&quot;https://github.com/snoopdave/usergrid-ember&quot;&gt;Usergrid-Ember&lt;/a&gt;. Next up, I&amp;#39;ll write about my experiences using Apache Shiro to replace Spring Security in Apache Roller.&lt;/p&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/usergrid-ember-part-1</guid>
    <title>Usergrid and Ember.js - part 1</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/usergrid-ember-part-1</link>
    <pubDate>Tue, 20 Jan 2015 09:39:47 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>baas</category>
    <category>emberjs</category>
    <category>javascript</category>
<description>&lt;p&gt;The next one of my &lt;a href=&quot;http://rollerweblogger.org/roller/entry/2014-side-projects&quot;&gt;2014 Side projects&lt;/a&gt; that I&#146;d like to share is Usergrid-Ember, an experiment and attempt to learn more about &lt;a href=&quot;http://emberjs.com&quot;&gt;Ember.js&lt;/a&gt; and &lt;a href=&quot;http://usergrid.incubator.apache.org&quot;&gt;Apache Usergrid&lt;/a&gt; by implementing the Checkin example from my Usergrid mobile development talk. If you&amp;#39;re interested in either Usergrid or JavaScript web development then I hope you&amp;#39;ll read on...&lt;/p&gt;


&lt;h4&gt;Why Ember.js?&lt;/h4&gt;

&lt;a href=&quot;http://emberjs.com&quot;&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/87879b47-6b14-4c18-a17e-576a68bf62a0&quot; alt=&quot;Ember logo&quot; align=&quot;right&quot;&gt;&lt;/a&gt;

&lt;p&gt;Ember.js is one of the leading frameworks for building browser-based apps. It&amp;#39;s one of &lt;a href=&quot;http://www.infoq.com/research/top-javascript-mvc-frameworks&quot;&gt;many JavaScript Model View Controller (MVC) frameworks&lt;/a&gt;. Generally speaking, these frameworks let you define a set of routes or paths in your app, for example /index, /orders, /about, etc. and map each to some JavaScript code and HTML templates. Handling a route usually means using Ajax to grab some &#147;model&#148; data from a server and using a template to create an HTML &#147;view&#148; of the data that calls functions provided in a &amp;quot;controller&amp;quot; object.&lt;/p&gt;

&lt;p&gt;JavaScript MVC frameworks are not simple and each has its own learning curve. Is it really worth the learning time when you can do so much with a little library like jQuery?  For most projects I think the answer is yes. These frameworks force you to organize your code in a logical and consistent way, which is really important as projects grow larger, and they provide features that may save you a lot of development time.&lt;/p&gt;

&lt;p&gt;Based on what I&amp;#39;ve seen on the net and local meet-ups, the leading frameworks these days are Ember.js and AngularJS. After I saw &lt;a href=&quot;http://allthingsopen.org/talks/the-ember-js-framework-everything-you-need-to-know/&quot;&gt;Yehudi Katz&#146;s talk at All Things Open&lt;/a&gt;, I decided to spend some time learning Ember.js.&lt;/p&gt;


&lt;h4&gt;Getting started with Ember.js&lt;/h4&gt;

&lt;p&gt;The first thing you see when you visit the &lt;a href=&quot;http://emberjs.com&quot;&gt;Ember.js&lt;/a&gt; site is a big button that says &amp;quot;DOWNLOAD THE STARTER KIT&amp;quot; and so that is where I started. The Starter Kit is a, a minimal Ember.js project with about twenty JavaScript, HTML and CSS files. It&amp;#39;s a good way to start: small and simple.&lt;/p&gt;  

&lt;p&gt;&lt;b&gt;Ember.js Starter Kit files:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;
&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/208a9957-5c35-4376-824a-1839f1dd6e02&quot; alt=&quot;screenshot of Starter Kit directory layout&quot;&gt;
&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Sidebar: I do hope they keep the Starter Kit around when the new &lt;a href=&quot;http://www.ember-cli.com&quot;&gt;Ember-CLI&lt;/a&gt; tool matures. Ember-CLI generates too many magic boiler-plate files and sub-directories for somebody who is trying to understand the basics of the framework. And this is an interesting point of view: &lt;a href=&quot;https://ca.non.co.il/index.php/ember-cli-is-making-you-stupid/&quot;&gt;Ember-CLI is Making You Stupid&lt;/a&gt; by Yoni Yechezkel.&lt;/i&gt;&lt;/p&gt;


&lt;h4&gt;Other stuff: Bower, Grunt and Bootstrap&lt;/h4&gt;

&lt;p&gt;I like to bite off more than I can chew, so I decided to use a couple of other tools. I used &lt;a href=&quot;http://bower.io&quot;&gt;Bower&lt;/a&gt; to manage dependencies and &lt;a href=&quot;http://gruntjs.com&quot;&gt;Grunt&lt;/a&gt; to concatenate and minify those dependencies, and other things like launching a simple web server for development purposes. I also decided to use &lt;a href=&quot;http://getbootstrap.com&quot;&gt;Bootstrap&lt;/a&gt; to provide various UI components needed, like a navbar and nicely styled list views.&lt;p&gt;

&lt;p&gt;I won&amp;#39;t cover the details, but it was relatively easy to get Bower and Grunt working. Here are the config files in case you are interested: &lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/master/bower.json&quot;&gt;bower.json&lt;/a&gt; and &lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/master/Gruntfile.js&quot;&gt;Gruntfile.js&lt;/a&gt;. I did hit one problem: when I included Bootstrap as one of my dependencies the Glyphicons would all appear as tiny boxes, so I decided to pull Bootstrap from a CDN instead  (looks like there is a &lt;a href=&quot;https://github.com/twbs/bootstrap/issues/9940&quot;&gt;fix&lt;/a&gt; for that now).&lt;/p&gt;


&lt;h4&gt;Defining Index Route, Model and Template&lt;/h4&gt;

&lt;p&gt;Every Ember.js app needs to define some routes. There is a default route for the &amp;quot;/&amp;quot; path which is called the index route, and you can add your own routes using the Router object. The snippet below shows what I needed to get started:&lt;/p&gt;

&lt;b&gt;Part of app.js (&lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L18&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush: js;&quot;&gt;
// create the ember app object
App = Ember.Application.create();

// define routes
App.Router.map(function() {
    this.route(&amp;quot;login&amp;quot;, { path: &amp;quot;/login&amp;quot; });  
    this.route(&amp;quot;logout&amp;quot;, { path: &amp;quot;/logout&amp;quot; });
    this.route(&amp;quot;register&amp;quot;, { path: &amp;quot;/register&amp;quot; });
});
&lt;/pre&gt;

&lt;p&gt;Ember.js will look for the JavaScript Route and Controller objects as well as the HTML template using the names above. For example: Ember.js will expect the login route to be named &lt;code&gt;App.LoginRoute&lt;/code&gt;, the controller to be named &lt;code&gt;App.LoginController&lt;/code&gt; and the template to be named &amp;quot;login.&amp;quot;&lt;/p&gt;

&lt;p&gt;Let&amp;#39;s talk about the index route. When a user arrives at your app they&#146;ll be directed to the index route. Ember.js will then look for a JavaScript object called &lt;code&gt;App.IndexRoute&lt;/code&gt; to provide the model data and JavaScript functions needed for the index page. Here&#146;s a partial view of the index route:&lt;/p&gt;

&lt;b&gt;Part of app.js (&lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L175&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush: js;&quot;&gt;
App.IndexRoute = Ember.Route.extend( {

    // provide model data needed for index template
    model: function() {
        if ( this.loggedIn() ) {
            return this.store.find(&amp;quot;activity&amp;quot;);
        }
        return [];
    }

});
&lt;/pre&gt;

&lt;p&gt;The index page of the Checkin app shows the Checkin activities of the people that you follow. Above you can see how to route&amp;#39;s &lt;code&gt;model()&lt;/code&gt; function makes that data available to the template for display.  If the user is logged in we call the &lt;code&gt;store.find(&#147;activity&#148;)&lt;/code&gt; function to call the Usergrid REST API to get an array of the latest Activity objects. There is some serious Ember-Data magic going on there and I&amp;#39;ll cover that in part two of this article.&lt;/p&gt;

&lt;p&gt;To display the index route, Ember looks for an HTML template called &#147;index&#148; and will use that template to display the index page. Below is the index template. The template is a &lt;a href=&quot;http://handlebarsjs.com&quot;&gt;Handlebars&lt;/a&gt; template and the things that appear in double curly-braces are Handlebars expressions.&lt;/p&gt;

&lt;b&gt;Part of index.html (&lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/v2/index.html#L46&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush: js;&quot;&gt;


  
    &lt;div class=&quot;container-fluid&quot;&gt;

      &lt;div class=&quot;navbar-header&quot;&gt;
        Checkin
      &lt;/div&gt;

      
          {{action &amp;#39;showModal&amp;#39; &amp;#39;add-checkin-modal&amp;#39; model }}&amp;gt;Add Checkin
      

      &lt;ul class=&quot;nav navbar-nav navbar-right&quot;&gt;
        &lt;li&gt;Logout &lt;/li&gt;
      &lt;/ul&gt;

    &lt;/div&gt;
  

  &lt;div class=&quot;row&quot;&gt;
    &lt;div class=&quot;col-md-12&quot;&gt;

      &lt;ul class=&quot;list-group&quot;&gt;
        {{#each item in model}}
          &lt;li class=&quot;list-group-item&quot;&gt;
              {{item.content}} | {{item.location}}
          &lt;/li&gt;
        {{/each}}
      &lt;/ul&gt;

    &lt;/div&gt;
  &lt;/div&gt;


&lt;/pre&gt;

&lt;p&gt;In the above template you can see a couple of &lt;code&gt;{{action}}&lt;/code&gt; expressions that call out to JavaScript methods defined in the Checkin app. The part of the code that uses the model is in the &lt;code&gt;{{#each}}&lt;/code&gt; loop which loops through each Activity in the model and dispays an HTML list with the the item.content and item.location of each Activity.&lt;/p&gt;

&lt;p&gt;Here&amp;#39;s what the above template looks like when displayed in a browser:&lt;/p&gt;

&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/d052ee9f-4c60-44ea-b41a-b462b2bef876&quot; alt=&quot;screenshot of checkin app index page&quot;&gt;
&lt;br&gt;
&lt;br&gt;

&lt;h4&gt;Implementing Login&lt;/h4&gt;

&lt;p&gt;In Checkin, login is implemented using HTML Local Storage. Once a user has successfully logged in, the app stores the username and the user&amp;#39;s access_token in Local Storage. When user arrives at the index page, we check Local Storage to see if that user is logged in and if not, we direct them to the login route, which in turn displays the login page using the template below.&lt;/p&gt;

&lt;b&gt;Part of index.html (&lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/v2/index.html#L46&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush: js;&quot;&gt;


  
    &lt;div class=&quot;container-fluid&quot;&gt;
      &lt;div class=&quot;navbar-header&quot;&gt;
        Checkin
      &lt;/div&gt;
    &lt;/div&gt;
  

  &lt;div class=&quot;row&quot;&gt;
    &lt;div class=&quot;col-md-3&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;col-md-6&quot;&gt;

        

         &lt;h2 class=&quot;form-login-heading&quot;&gt;Please Login&lt;/h2&gt;

         Email address
         {{input class=&amp;quot;form-control&amp;quot; type=&amp;quot;text&amp;quot; 
              valueBinding=&amp;quot;username&amp;quot; placeholder=&amp;quot;Username&amp;quot;}}

         Password
         {{input class=&amp;quot;form-control&amp;quot; type=&amp;quot;password&amp;quot; 
             valueBinding=&amp;quot;password&amp;quot; placeholder=&amp;quot;Password&amp;quot;}}
           
         
            Login
         

         Register as new user.
        

    &lt;/div&gt;
    &lt;div class=&quot;col-md-3&quot;&gt;&lt;/div&gt;
  &lt;/div&gt;


&lt;/pre&gt;

&lt;p&gt;The LoginController provides the functions needed by the Login page itself and there are two. There is a &lt;code&gt;login()&lt;/code&gt; function (called on line 27 above) that performs the login, and there is a &lt;code&gt;register()&lt;/code&gt; function (called on line 31 above) that directs the user to the New User Registration page. Here&amp;#39;s a snippet of code from the &lt;code&gt;App.LoginController&lt;/code&gt; that provides these two functions:&lt;/p&gt;

&lt;b&gt;Part of app.js (&lt;a href=&quot;https://github.com/snoopdave/usergrid-ember/blob/v2/js/app.js#L250&quot;&gt;link&lt;/a&gt;)&lt;/b&gt;
&lt;pre class=&quot;brush: js;&quot;&gt;
App.LoginController = Ember.Controller.extend({

  actions: {

    login: function() { 

      // login by POST to Usergrid app&amp;#39;s /token end-point

      var loginData = {
        grant_type: &amp;quot;password&amp;quot;,
        username: this.get(&amp;quot;username&amp;quot;),
        password: this.get(&amp;quot;password&amp;quot;)
      };

      $.ajax({
        type: &amp;quot;POST&amp;quot;,
        url: Usergrid.getAppUrl() + &amp;quot;/token&amp;quot;,
        data: loginData,
        context: this,
        error: function( data ) {

          // login failed, show error message
          alert( data.responseJSON.error_description );
        },
        success: function( data ) { 

          // store access_token in local storage
          Usergrid.user = data.user;
          localStorage.setItem(&amp;quot;username&amp;quot;, loginData.username );
          localStorage.setItem(&amp;quot;access_token&amp;quot;, data.access_token );

          // clear the form
          this.set(&amp;quot;username&amp;quot;, &amp;quot;&amp;quot;); 
          this.set(&amp;quot;password&amp;quot;, &amp;quot;&amp;quot;);

         // call route to handle post-login transition
          this.get(&amp;quot;target&amp;quot;).send(&amp;quot;onLogin&amp;quot;); 
        }
      });
    },

    register: function() {
      this.transitionToRoute(&amp;quot;register&amp;quot;);
    }

  }
});
&lt;/pre&gt;

&lt;p&gt;The above code shows how to login to a Usergrid app using jQuery&amp;#39;s Ajax feature. The &lt;code&gt;login()&lt;/code&gt; function takes the username and password values from the login form, puts those in a JSON object with grant_type &amp;quot;password&amp;quot; and posts that object to the &lt;code&gt;/token&lt;/code&gt; end-point of the Usergrid app. If that post succeeds, the response will include an access_token. We store that in Local Storage; we&amp;#39;ll need to use it in all subsequent calls to Usergrid.&lt;/p&gt;

&lt;p&gt;Usergrid fans will notice that I&amp;#39;m not using the &lt;a href=&quot;https://github.com/snoopdave/incubator-usergrid/tree/master/sdks/html5-javascript&quot;&gt;Usergrid JavaScript SDK&lt;/a&gt;. That&amp;#39;s because Ember.js provides Ember-Data, which acts as a very nice REST client and can be adapted to work with the URL structure and JSON formats of just about any REST API. I&amp;#39;ll write about that in part two of this article.&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/2014-side-projects</guid>
    <title>2014 side projects</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/2014-side-projects</link>
    <pubDate>Mon, 5 Jan 2015 08:37:04 +0000</pubDate>
    <category>Open Source</category>
    <category>asf</category>
    <category>javascript</category>
    <category>usergrid</category>
<description>
&lt;p&gt;For various reasons, I&amp;#39;ve always got a couple of coding projects on the back burner, things that I hack around with on weekends and breaks. In 2014, I started four projects and learned about Ember.js, jQuery Mobile, Apache Shiro, Apache CXF and the Arquillian test framework. &lt;/p&gt;


&lt;p&gt;I like to share my code, so I&amp;#39;ve put my code on GitHub and I&amp;#39;m going to write a brief post about each here on my blog. I&amp;#39;ll provide links as I go and, of course, I welcome any criticisms and suggestions for improvement that you might have. First up: the Usergrid-Mobile project.&lt;/p&gt;


&lt;p&gt;&lt;h3&gt;The Usergrid-Mobile project&lt;/h3&gt;&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/128ca398-027d-491d-9e0b-b24c77c8e9b9&quot; alt=&quot;ApacheCon EU logo&quot; align=&quot;right&quot; height=&quot;100&quot;&gt;&lt;br&gt;
To be honest, Budapest was the goal of this project. In the Spring of 2014, I decided that the best chance of getting to ApacheCon EU in Budapest was to create a great &amp;quot;mobile development with Usergrid&amp;quot; talk, and to do that I needed a great example project. The resulting project shows how to create a dumbed-down Foursquare-style &amp;quot;checkin&amp;quot; app using HTML5, JavaScript, &lt;a href=&quot;http://jquerymobile.com&quot;&gt;jQuery Mobile&lt;/a&gt; and &lt;a href=&quot;http://cordova.apache.org&quot;&gt;Apache Cordova&lt;/a&gt;. &lt;/p&gt;


&lt;p&gt;Luckily for me, my talk was &lt;a href=&quot;http://apacheconeu2014.sched.org/event/edbc9c695e39c2960441690a8a23b5d3#.VKceecaZ7tE&quot;&gt;accepted&lt;/a&gt; for ApacheCon EU and in November I traveled to Budapest (took some &lt;a href=&quot;https://www.flickr.com/photos/snoopdave/15859954201/in/set-72157649019765409&quot;&gt;photos&lt;/a&gt;) and gave the talk there. &lt;/p&gt;


&lt;p&gt;I also presented the talk at the All Things Open conference in Raleigh, NC and you can view a video of that talk, &lt;a href=&quot;https://www.youtube.com/watch?v=DjFG-QbxxLw&quot;&gt;Mobile Development with Usergrid&lt;/a&gt; on YouTube.&lt;/p&gt;


&lt;p&gt;&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;&lt;/p&gt;


&lt;p&gt;You can find the code for &lt;a href=&quot;https://github.com/snoopdave/usergrid-mobile&quot;&gt;usergrid-mobile&lt;/a&gt; on  GitHub. I also created a Vagrant File to launch a local instance of Usergrid for demo purposes. It&amp;#39;s called usergrid-vagrant.&lt;/p&gt;


&lt;p&gt;That&amp;#39;s all for now. Next up: Usergrid-Ember.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/phoenix-websites</guid>
    <title>Phoenix Websites</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/phoenix-websites</link>
    <pubDate>Mon, 1 Dec 2014 07:07:25 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>business</category>
<description>&lt;a href=&quot;http://phoenixrdu.com&quot;&gt;

&lt;/a&gt;

&lt;p&gt;My eldest son Alex and his friend Austin have started a website design and creation business called &lt;a href=&quot;http://phoenixrdu.com&quot;&gt;Phoenix Websites&lt;/a&gt; and, of course, I think this is a great thing. They&amp;#39;re not yet out of high school and just getting started, but they&amp;#39;ve already landed a couple of real live customers. They&amp;#39;ve got some skills and are not afraid of hard work, so if you&amp;#39;re a Triangle-area small business owner and you need a nice new website, check them out. &lt;/p&gt;

&lt;p&gt;Like any new business, they need some link love so here we go: &lt;a href=&quot;http://phoenixrdu.com&quot;&gt;Phoenix Websites: Website design and creation services in the Raleigh-Durham Triangle-area&lt;/a&gt;.  Follow them on twitter at &lt;a href=&quot;http://twitter.com/phoenixrdu&quot;&gt;@phoenixrdu&lt;/a&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/introduction-to-apache-usergrid</guid>
    <title>Introduction to Apache Usergrid</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/introduction-to-apache-usergrid</link>
    <pubDate>Sat, 29 Nov 2014 10:09:48 +0000</pubDate>
    <category>Open Source</category>
    <category>apache</category>
    <category>apachecon</category>
    <category>asf</category>
    <category>baas</category>
<description>&lt;p&gt;I travelled to Budapest, Hungary for a couple of weeks for a very nice &lt;a href=&quot;https://www.flickr.com/photos/snoopdave/sets/72157649019765409/&quot;&gt;vacation&lt;/a&gt; with my wife and to speak at ApacheCon EU. Here are the slides that I presented at ApacheCon EU:&lt;/p&gt;

&lt;br&gt;

  

&lt;br&gt;

&lt;p&gt;(you can also &lt;a href=&quot;http://www.slideshare.net/snoopdave/introduction-to-usergrid-apache-con-eu-2014&quot;&gt;view the presentation on Slideshare&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;And here is the session abstract:&lt;/p&gt;

&lt;p&gt;Whether you are building a mobile app or a web app, Apache Usergrid (incubating) can provide you with a complete backend that supports authentication, persistence and social features like activities and followers all via a comprehensive REST API &#151; and backed by Cassandra, giving you linear scalability. All that, and Usergrid is open source too. &lt;/p&gt;

&lt;p&gt;This session will explain how you can use Usergrid to provide a back-end for your application. We&#146;ll start with an overview of Usergrid features, then explore in depth how to authenticate users, store data and query data with the REST API provided by a Usergrid server. We&#146;ll develop a simple HTML5 app and package it as a native mobile app via Apache Cordova. We&amp;#39;ll also cover how to run Usergrid locally for development and testing.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/talking_usergrid_at_apachecon2014</guid>
    <title>Talking Usergrid at ApacheCon 2014</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/talking_usergrid_at_apachecon2014</link>
    <pubDate>Sun, 16 Mar 2014 16:10:16 +0000</pubDate>
    <category>Open Source</category>
    <category>apachecon</category>
    <category>asf</category>
    <category>baas</category>
    <category>cassandra</category>
    <category>java</category>
    <category>opensource</category>
    <category>usergrid</category>
<description>&lt;a href=&quot;http://www.apachecon.com/&quot;&gt;
&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/59d5e8f1-074f-4fd2-84a3-0b0a108c089c&quot; align=&quot;right&quot; alt=&quot;ApacheCon 2014&quot;&gt;
&lt;/a&gt;

&lt;p&gt;I&amp;#39;ve been working at &lt;a href=&quot;http://apigee.com&quot;&gt;Apigee&lt;/a&gt; since September 2013 and one of the things I love most about my new job is the fact that I&amp;#39;m actively contributing to open source again. &lt;/p&gt;

&lt;p&gt;I&amp;#39;m working on &lt;a href=&quot;http://usergrid.incubator.apache.org/&quot;&gt;Apache Usergrid&lt;/a&gt; (incubating), an open source &lt;b&gt;Backend-As-A-Service&lt;/b&gt; (BaaS) that&amp;#39;s built on the Apache Cassandra database system. Apigee uses Usergrid as part of &lt;a href=&quot;http://apigee.com/about/content/apigee-edge&quot;&gt;Apigee Edge&lt;/a&gt; (see the &lt;a href=&quot;http://apigee.com/docs/content/build-apps-home&quot;&gt;Build Apps&lt;/a&gt; part of the docs).&lt;/p&gt;

&lt;p&gt;Apigee contributed code for Usergrid to the &lt;a href=&quot;http://apache.org&quot;&gt;Apache Software Foundation&lt;/a&gt; back in October 2013 and Usergrid is now part of the Apache Incubator. The project is working towards graduating from the Incubator. That means learning the Apache way, following the processes to get a release out and most importantly, building a diverse community of contributors to build and maintain Usergrid.&lt;/p&gt;

&lt;p&gt;One on the most important parts of building an open source community is making it easy for people to contribute and and that&amp;#39;s why I submitted a talk to the &lt;a href=&quot;http://www.apachecon.com/&quot;&gt;ApacheCon US 2014&lt;/a&gt; conference (April 7-9 in Denver, CO) titled &lt;a href=&quot;http://events.linuxfoundation.org/events/apachecon-north-america/program/schedule&quot;&gt;How to Contribute to Usergrid&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The talk is intended to be a briefing for contributors, one that will lead you through building and running Usergrid locally, understanding the code-base and test infrastructure and how to get your code accepted into the Usergrid project.&lt;/p&gt;

&lt;p&gt;Here&amp;#39;s the outline I have so far:&lt;/p&gt;

&lt;h3&gt;How to Contribute to Apache Usergrid&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Motivation
    &lt;ul&gt;&lt;li&gt;Why would anybody want to contribute to Usergrid?&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;First steps
    &lt;ul&gt;&lt;li&gt;The basics&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Getting signed up&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Contributing to the Stack
    &lt;ul&gt;&lt;li&gt;Understanding the architecture &amp; code base&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Building the code. Making and testing changes&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Running Usergrid locally via launcher &amp; via Tomcat&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Contributing to the Portal
    &lt;ul&gt;&lt;li&gt;Understanding the architecture &amp; code base&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Building the code. Making and testing changes&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Running the portal locally via node.js&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Contributing to the SDKs
    &lt;ul&gt;&lt;li&gt;Understanding the architecture &amp; code base&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Building the code. Making and testing changes&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Contributor workflow: how to get your code into Usergrid
    &lt;ul&gt;&lt;li&gt;For quickie drive-by code contributions&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;For more substantial code contributions&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;For documentation &amp;  website changes&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Contributing Docs and Website changes
    &lt;ul&gt;&lt;li&gt;Website, wiki and GitHub pages&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;How to build the website and docs&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Roadmap
    &lt;ul&gt;&lt;li&gt;First release&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;New Core Persistence system&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;The two-dot-o branch&lt;/li&gt;&lt;/ul&gt;
    &lt;ul&gt;&lt;li&gt;Other ideas&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I&amp;#39;m in the process of writing this talk now so suggestions and other feedback are most welcome.&lt;/p&gt;


</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/new_bootstrap_based_theme</guid>
    <title>New Bootstrap based theme</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/new_bootstrap_based_theme</link>
    <pubDate>Sun, 19 Jan 2014 21:10:01 +0000</pubDate>
    <category>Blogging</category>
    <category>bootstrap</category>
    <category>roller</category>
<description>&lt;p&gt;I&amp;#39;m not motivated to write new blog entries but for some reason I was motivated to update my blog&amp;#39;s theme. This time I decided to go with &lt;a href=&quot;http://getbootstrap.com/&quot;&gt;Twitter Bootstrap&lt;/a&gt; + &lt;a href=&quot;http://jquery.com/&quot;&gt;jQuery&lt;/a&gt;. It&amp;#39;s responsive, so to speak. What do you think? I think it takes a little too long for the banner image to load.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/upgraded_to_latest_5_1</guid>
    <title>Upgraded to latest 5.1.0-SNAPSHOT </title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/upgraded_to_latest_5_1</link>
    <pubDate>Sun, 3 Nov 2013 11:26:07 +0000</pubDate>
    <category>Roller</category>
<description>&lt;p&gt;Just a quick note about this site:&lt;/p&gt;

&lt;p&gt;Today I upgraded this site to the latest Roller 5.1.0-SNAPSHOT (unreleased) code base. I did this to fix a couple of vulnerabilities found recently by researchers from &lt;a href=&quot;http://www.coverity.com/&quot;&gt;Coverity&lt;/a&gt; (thanks Coverity!).&lt;/p&gt;

&lt;p&gt;The Roller team fixed the vulnerabilities reported by Coverity in both the Roller 5.1-SNAPSHOT (unreleased)  and the Roller 5.0.x branch. If you&amp;#39;re running Roller, you should upgrade to Roller 5.0.1 immediately.&lt;/p&gt;

&lt;p&gt;See also: &lt;a href=&quot;http://rollerweblogger.org/project/entry/apache_roller_5_0_2&quot;&gt;Apache Roller 5.0.2 security fix release now available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It&amp;#39;s been while since I upgraded this site (or even blogged here) so the upgrade was a little painful. When I first upgraded I found that the JSPWiki plugin was broken and the site would not even load. Turns out, I was using an archaic copy of the JSPWiki jar and it was compiled against an equally archaic version of Lucene. Since we recently upgraded the version of Lucene included in Roller, this broke things. I fixed this by upgrading the Roller JSPWiki plugin to use the latest code from Apache JSPWiki. I also created a Github repo with the new JSPWiki code, which you can find here: &lt;a href=&quot;https://github.com/snoopdave/roller-jspwiki-plugin&quot;&gt;Roller JSPWiki Plugin on Github.&lt;/a&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/how_wayin_does_orchestration</guid>
    <title>How Wayin does Cloud Orchestration</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/how_wayin_does_orchestration</link>
    <pubDate>Tue, 18 Jun 2013 16:16:34 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>aws</category>
    <category>cloud</category>
    <category>devops</category>
    <category>orchestration</category>
    <category>wayin</category>
<description>&lt;p&gt;I&amp;#39;ve already mentioned this on Twitter and LinkedIn, but just in case you missed it: I&amp;#39;ll be speaking tomorrow night at the &lt;a href=&quot;http://www.meetup.com/Triangle-DevOps/events/121662952&quot;&gt;Triangle AWS and Triangle DevOps&lt;/a&gt; joint meetup at &lt;a href=&quot;http://argylesocial.com/&quot;&gt;Argyle Social&lt;/a&gt; in Durham, NC. I&amp;#39;ll give a quick overview of cloud orchestration and &lt;a href=&quot;http://wayinhub.com&quot;&gt;Wayin Hub&lt;/a&gt;. Then I&amp;#39;ll dive into the details of how we automate deployment, scaling and backups for Wayin Hub using AWS and AWS Cloud Formation.&lt;/p&gt;

&lt;p&gt;As a little teaser, here&amp;#39;s a GIF animation of my automated deployment slide:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;

&lt;p&gt;For more information check the &lt;a href=&quot;http://www.meetup.com/Triangle-DevOps/events/121662952&quot;&gt;Triangle DevOps page&lt;/a&gt; for the event.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE: &lt;/b&gt; slide are now on &lt;a href=&quot;http://www.slideshare.net/snoopdave/wayin-devops2013&quot;&gt;SlideShare&lt;/a&gt;.&lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/composite_keys_in_cassandra</guid>
    <title>Composite Keys in Apache Cassandra</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/composite_keys_in_cassandra</link>
    <pubDate>Thu, 23 May 2013 17:56:49 +0000</pubDate>
    <category>Open Source</category>
    <category>asf</category>
    <category>bigdata</category>
    <category>cassandra</category>
    <category>wayin</category>
    <category>wayinhub</category>
<atom:summary type="html">&lt;p&gt;Newer versions of &lt;a href=&quot;http://cassandra.apache.org&quot;&gt;Apache Cassandra&lt;/a&gt; include &lt;a href=&quot;http://cassandra.apache.org/doc/cql3/CQL.html&quot;&gt;CQL&lt;/a&gt;, an SQL-like query language that supports both query, update and delete statements as well as the Data Definition Language (DDL) statements like create and alter for tables and indexes. You can create tables (known as column families in Cassandra lingo) just like you can in a relational database, but there are some caveats.&lt;/p&gt;</atom:summary><description>&lt;p&gt;Newer versions of &lt;a href=&quot;http://cassandra.apache.org&quot;&gt;Apache Cassandra&lt;/a&gt; include &lt;a href=&quot;http://cassandra.apache.org/doc/cql3/CQL.html&quot;&gt;CQL&lt;/a&gt;, an SQL-like query language that supports both query, update and delete statements as well as the Data Definition Language (DDL) statements like create and alter for tables and indexes.&lt;/p&gt;

&lt;p&gt;You can create tables (known as column families in Cassandra lingo) just like you can in a relational database, but there are some caveats. One caveat is this: if you want to sort and use ORDER BY in queries on a table, then you will have to use composite-key in that table and that composite key must include the field that you wish to sort on. You have to decide which fields you wish to sort on when you design your data model, not when you formulate queries. Another caveat is that, with Cassandra 1.1, there is no support for secondary indexes on composite-keyed tables. That means you can only query on the fields in the composite-key and in certain specific ways. More on that later.&lt;/p&gt;

&lt;p&gt;In version 1.1, Cassandra supports (at least) two different models for storing data. You can use a single primary key in your table, or you can use a composite key. Cassandra stores your data differently for these two cases and the queries that you can perform on these two types of tables vary as well. &lt;/p&gt;

&lt;p&gt;I&#146;ll explain how simple-keyed and composite-keyed differ with some examples. The example is the Bite table, which holds a chunk of data identified by an ID and sorted by an integer score value. I don&#146;t want to get into details, but in the product I&amp;#39;m working on, &lt;a href=&quot;http://www.wayin.com/products/wayin-hub&quot;&gt;Wayin Hub&lt;/a&gt;, a Site contains Feeds and Feeds contain Bites, which might represent Tweets, RSS items or other social network activities. Hopefully, that will be enough background for you to understand the examples below.

&lt;h3&gt;Single-keyed Table&lt;/h3&gt;

&lt;p&gt;With a single keyed table you have a row for each entity that you store and a column in that row for each field of the entity. For example, to represent the Bite table as a single-keyed table it would be defined like so:&lt;/p&gt;

&lt;pre&gt;

   create table bite (
	id varchar PRIMARY KEY,
	feedid varchar,
	score bigint,
	data varchar
   );

   create index bite_feedid on bite (feedid);
   create index bite_score on bite (score);

&lt;/pre&gt;

&lt;p&gt;We need those id, feedid and score fields so we can look up bites by those values. The data field is used to store a JSON representation of other data we associate with each Bite. For example, if you only have three Bites in the table, here&#146;s what the results of a select * from bite might look like:&lt;/p&gt;

&lt;pre&gt;

 id   |  feedid  |  score  | data
------+----------+---------+----------------------
bite2 |  feed0   |   101   | { &#147;status&#148; : &#147;APPROVED&#148;, ... 
bite3 |  feed0   |   102   | { &#147;status&#148; : &#147;DENIED&#148;, ...
bite1 |  feed0   |   100   | { &#147;status&#148; : &#147;APPROVED&#148;, ...  

&lt;/pre&gt;

&lt;p&gt;The way the data is stored in Cassandra would look about the same, as illustrated in the diagram below. Each table row corresponds to a Row in Cassandra, the id of the table row is the Cassandra Row Key for the row. Each value in the row is a Cassandra Column with a key and a value. If you add more table rows, you get more Cassandra Rows.&lt;/p&gt;

&lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/fcac32c5-6a8d-4be9-acfa-1e56389c6c4c&quot;&gt;

&lt;p&gt;Now let&#146;s get back to the topic of this post and that caveat that I mentioned earlier.&lt;/p&gt;

&lt;h3&gt;Composite-keyed Table &lt;/h3&gt;

&lt;p&gt;If we want to sort data in a table, then we need to use a composite-keyed table.  With a composite-keyed table you define a composite-key made up of multiple fields from the table. The first key is known as the partition-key. To sort by score we also include the score in the composite key. And since it is possible for two Bites with the same partition key to occur at the very same time, we also include varchar ID to ensure uniqueness.&lt;p&gt;

&lt;p&gt;Here&#146;s how the Bite table is defined. The composite-key is the list of three fields in PRIMARY KEY parentheses.&lt;/p&gt;

&lt;pre&gt;

   create table Bite (
	 partkey varchar,
	 score bigint,
	 id varchar,
	 data varchar,
	 PRIMARY KEY (partkey, score, id)
   ) with clustering order by (score desc);

&lt;/pre&gt;

&lt;p&gt;And if you had three Bites in the table the query select * from bite would return this:&lt;/p&gt;

&lt;pre&gt;

 partkey  |  score  |  id   |  data
----------+---------+-------+----------------------
  feed0   |   102   | bite3 | { &#147;id&#148; : &#147;bite2&#148;, ... 
  feed0   |   101   | bite2 | { &#147;id&#148; : &#147;bite3&#148;, ...
  feed0   |   100   | bite1 | { &#147;id&#148; : &#147;bite1&#148;, ...  

&lt;/pre&gt;

&lt;p&gt;The surprise is how this table is stored in Cassandra. Instead of storing a Cassandra Row for each table row, the data is stored as one row. The more Bites you add to the table, the more Cassandra Columns are added to that Row. The diagram below illustrates how this works for the three Bites of data above. There is one Row with key of feed0. And there is one Column for each table row of data. Each Column uses a key that combines the score and id plus a string that indicates what field is stored in the Column Value, which in our case is only the data field.&lt;/p&gt;

&lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/255341b2-0705-47b1-9d3b-9725cb140566&quot;&gt;

&lt;h3&gt;Querying a Composite-keyed Table&lt;/h3&gt;

&lt;p&gt;With Cassandra 1.1, if you want to select a single Bite, you must know all of the composite-keys. Here&#146;s an example query that selects a single Bite:&lt;/p&gt;

&lt;pre&gt;

   select data from bite where partkey=&#146;feed0&#146; and score=101 and id=&#146;bite2&#146;

&lt;/pre&gt;

&lt;p&gt;To get latest Bites in a Site&#146;s Feed, you specify only the partition-key and ask for ordering by score, like so:&lt;/p&gt;

&lt;pre&gt;

   select data from bite where partkey=&#146;feed0&#146; order by score desc limit 20

&lt;/pre&gt;

&lt;p&gt;If you try to query without specifying the partition key and the score, you will get an error message. For example, this query:&lt;/p&gt;

&lt;pre&gt;

   select data from bite where id=&#146;bite2&#146; limit 20

&lt;/pre&gt;

&lt;p&gt;Would give you this error message:&lt;/p&gt;

   &lt;pre&gt;

   Bad Request: PRIMARY KEY part id cannot be restricted (preceding
   part score is either not restricted or by a non-EQ relation)

   &lt;/pre&gt;

&lt;p&gt;That means we can&#146;t look up Bites by a single ID. That&#146;s not very convenient but that&#146;s the way it is with Cassandra 1.1 which does not allow additional indexes on composite-key tables.. If you really want to lookup Bites by id, you have to create an entirely new simple-keyed table with Bite id as the primary key and use that table to look up the a Bite&#146;s partKey and score. This problem is fixed in Cassandra 1.2 because it allows secondary indexes on fields in composite-key table. Let&#146;s talk about that.&lt;/p&gt;

&lt;h3&gt;Secondary Indexes&lt;/h3&gt;

&lt;p&gt;Cassandra 1.2 comes with support for secondary indexes on composite-keyed tables, but you cannot create a secondary index on keys that are already part of the composite-key. So, if we want to be able to look-up Bites by ID, then we must add a second and redundant biteid field like so:&lt;/p&gt;

   &lt;pre&gt;

   alter table Bite add biteId varchar;
   create index Bite_biteId on Bite (biteId); 

   &lt;/pre&gt;

&lt;p&gt;Inside Cassandra, the the data would look like this, a new field in the table means a new Column in the row, as show below:&lt;/p&gt;

&lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/8215a60a-add0-4f38-9849-c4097bc25331&quot;&gt;

&lt;p&gt;And with that secondary index we can support queries like this:&lt;/p&gt;

   &lt;pre&gt;

   select data from bite where biteid=&#146;bite2&#146;

   &lt;/pre&gt;

&lt;h3&gt;Cassandra 1.2&lt;/h3&gt;

&lt;p&gt;In the DataStax Cassandra 1.2 docs, it says &#147;CQL3 transposes data partitions (sometimes called &amp;quot;wide rows&amp;quot;) into familiar row-based result sets, dramatically simplifying data modeling. &lt;/p&gt;

&lt;p&gt;I believe that means that all tables are stored the way that composite-keyed tables are stored. According to the docs, legacy tables from Cassandra 1.1 are supported in 1.2 and, if you want, you can still create Cassandra 1.1 style tables by using the &#147;compact storage&#148; attribute. For example, to create the Bite table with a the Cassandra 1.1 table model and a single primary key you&#146;d do this: &lt;/p&gt;

&lt;pre&gt;

   create table bite (
	id varchar PRIMARY KEY,
	feedid varchar,
	score bigint,
	data varchar
   ) with compact storage;

&lt;/pre&gt;

&lt;p&gt;And that&#146;s all I&#146;ve got on this topic.&lt;/p&gt;

&lt;h3&gt;Wrapping up...&lt;/h3&gt;

&lt;p&gt;I wrote this up to help myself understand how composite-keyed tables work in Cassandra, so I&#146;d love any feedback you might have and especially if you think I&#146;ve got concepts or terminology wrong. Thanks for reading.&lt;/p&gt;


&lt;p&gt;You can read more about Cassandra 1.1 tables on the Datastax site:&lt;br&gt;
&lt;a href=&quot;http://www.datastax.com/docs/1.1/ddl/column_family&quot;&gt;http://www.datastax.com/docs/1.1/ddl/column_family&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More about Cassandra 1.2 tables:&lt;br&gt;
&lt;a href=&quot;http://www.datastax.com/docs/1.2/ddl/table&quot;&gt;http://www.datastax.com/docs/1.2/ddl/table&lt;/a&gt;&lt;p&gt;

&lt;p&gt;More about Cassandra 1.1 legacy tables in Cassandra 1.2&lt;br&gt;
&lt;a href=&quot;http://www.datastax.com/docs/1.2/ddl/legacy_table&quot;&gt;http://www.datastax.com/docs/1.2/ddl/legacy_table&lt;/a&gt;&lt;p&gt;

&lt;p&gt;Also, I found these posts by Brian O&#146;Neill very helpful:&lt;br&gt;
http://brianoneill.blogspot.com/2012/09/composite-keys-connecting-dots-between.html &lt;br&gt;
http://brianoneill.blogspot.com/2012/10/cql-astyanax-and-compoundcomposite-keys.html&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;
   &lt;a href=&quot;http://wayin.com&quot;&gt;
      &lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/f624dfcd-2f04-406b-a2b2-f2bc1fa1afbe&quot;&gt;
   &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;And, if you want to build an engaging site for your customers, fans or constituents based on live tweets, photos and videos check out &lt;a href=&quot;http://www.wayin.com/products/wayin-hub&quot;&gt;Wayin Hub&lt;/a&gt; and follow &lt;a href=&quot;https://twitter.com/wayinhub&quot;&gt;@wayinhub&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;br&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/wip_feed_based_integration</guid>
    <title>WIP #6: Feed-based Integration</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/wip_feed_based_integration</link>
    <pubDate>Mon, 8 Oct 2012 10:38:43 +0000</pubDate>
    <category>Web Development</category>
    <category>atom</category>
    <category>feeds</category>
    <category>rss</category>
    <category>wip</category>
<atom:summary type="html">&lt;p&gt;&lt;i&gt;This is the sixth in my series of Web Integration Patterns. Check out the intro at this URL &lt;a href=&quot;http://rollerweblogger.org/roller/entry/web_integration_patterns&quot;&gt;http://rollerweblogger.org/roller/entry/web_integration_patterns&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;Synopsys&lt;/h3&gt;

&lt;p&gt;This pattern is about integrating web sites and applications by using standard feed formats to convey timely information, updates, status messages, events and other things from one web application to another.&lt;/p&gt;

&lt;h3&gt;Motivations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Take advantage of the many tools and services that can produce, consume, aggregate and build work-flows around feeds as a way to integrate web applications.&lt;/li&gt;
&lt;li&gt;Use a standards-based approach to ensure that that widest range of feed producers and consumers can be supported. Make it so everybody who needs can read your feeds.&lt;/li&gt;
&lt;/ul&gt;</atom:summary><description>&lt;p&gt;&lt;i&gt;This is the sixth in my series of Web Integration Patterns. Check out the intro at this URL &lt;a href=&quot;http://rollerweblogger.org/roller/entry/web_integration_patterns&quot;&gt;http://rollerweblogger.org/roller/entry/web_integration_patterns&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;Synopsys&lt;/h3&gt;

&lt;p&gt;This pattern is about integrating web sites and applications by using standard feed formats to convey timely information, updates, status messages, events and other things from one web application to another.&lt;/p&gt;

&lt;h3&gt;Motivations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Take advantage of the many tools and services that can produce, consume, aggregate and build work-flows around feeds as a way to integrate web applications.&lt;/li&gt;
&lt;li&gt;Use a standards-based approach to ensure that that widest range of feed producers and consumers can be supported. Make it so everybody who needs can read your feeds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Related Patterns&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Search Integration&lt;/li&gt;
&lt;li&gt;Web APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A feed is a list of entries, each with an timestamp, ID, title, content and metadata like categories and tags. Entries are arranged in reverse chronological order. The entries in a feed can represent just about anything from blog entries, Flickr photos, YouTube videos, source-code change sets or tasks in a change management system. A feed is an XML resource that is available at a URL. If you want updates, then you poll that URL, ideally using HTTP Conditional GET so that you only pull down the feed when it has been updated.&lt;/p&gt;

&lt;p&gt;Generally speaking there are two standard feed formats in use on the web: &lt;a href=&quot;http://cyber.law.harvard.edu/rss/rss.html&quot;&gt;RSS&lt;/a&gt; and &lt;a href=&quot;http://www.atomenabled.org/developers/syndication/atom-format-spec.php&quot;&gt;Atom&lt;/a&gt;, both are based on XML. Both use different element names, for example: what Atom calls &amp;quot;entries&amp;quot; RSS calls &amp;quot;items.&amp;quot; Because these standard formats are so widely supported, providing a feed is an effective way to share updates from your web site or application.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Elements of Atom&lt;/b&gt; (from my 2006 &lt;a href=&quot;http://rollerweblogger.org/roller/entry/tri_xml_2006_presentation&quot;&gt;presentation on Atom&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;
&lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/fa22fadc-9cf5-4bd6-8e2e-d6ce4e0a603f&quot; alt=&quot;Atom feed model&quot;&gt;
&lt;/p&gt;

&lt;p&gt;Another flavor of feeds is &lt;a href=&quot;http://activitystrea.ms/specs/json/schema/activity-schema.html&quot;&gt;ActivityStrea.ms&lt;/a&gt;, which is essentially a feed format with a &lt;a href=&quot;http://activitystrea.ms/specs/json/schema/activity-schema.html&quot;&gt;schema&lt;/a&gt; for representing about 70 different types of activities. These activities can be social network activities like &lt;i&gt;share&lt;/i&gt; or &lt;i&gt;friend&lt;/i&gt; and they can also be business activities like &lt;i&gt;assign&lt;/i&gt;, &lt;i&gt;resolve&lt;/i&gt; or &lt;i&gt;schedule&lt;/i&gt;. One advantage of using the ActivityStrea.ms standard is that it has both an &lt;a href=&quot;http://activitystrea.ms/specs/atom/1.0/&quot;&gt;Atom&lt;/a&gt; and a &lt;a href=&quot;http://activitystrea.ms/specs/json/1.0/&quot;&gt;JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Producing Feeds&lt;/h3&gt;

&lt;p&gt;Feed-based Integration is listed as a basic pattern because it can be very easy to implement. The ability to produce feeds is built-in to many different types of web applications from blog and wikis to continuous integration servers. If you are writing your own web application, you can choose to use XML tools to produce your feeds, a templating engine or a dedicated feed toolkit like &lt;a href=&quot;https://rometools.jira.com/secure/Dashboard.jspa&quot;&gt;ROME&lt;/a&gt;. You&#146;ll find plenty of XML tools and templating engines no matter what language you are using.&lt;/p&gt;

&lt;p&gt;Which type of feed should you produce? That depends. Atom is the most complete specification and is a true IETF standard, so often it is the right choice. To make the right decision, you have to consider who is going to be consuming your feeds. If your consumers prefer RSS, them give them that. If your consumers prefer JSON over XML, then consider ActivityStrea.ms in JSON flavor.&lt;/p&gt;

&lt;h3&gt;Consuming Feeds&lt;/h3&gt;

&lt;p&gt;You can use a wide variety of tools to parse and process feed data. For example, there are many web sites and services that can digest feeds and trigger other events and processing. Services such as &lt;a href=&quot;http://pipes.yahoo.com/pipes/&quot;&gt;Yahoo Pipes&lt;/a&gt; and &lt;a href=&quot;https://ifttt.com/dashboard&quot;&gt;If This Then That&lt;/a&gt; can read feeds, process each item and perform other actions based on item values. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Processing a feed with Yahoo Pipes&lt;/b&gt; (from &lt;a href=&quot;http://blog.earth2marsh.com/2009/09/prioritizing-podcasts-with-google.html&quot;&gt;Marsh Gardiner&amp;#39;s post&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;
&lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/62bbcf12-3c87-4964-8eab-6b5482c97e4f&quot; alt=&quot;Yahoo Pipes&quot;&gt;
&lt;/p&gt;

&lt;p&gt;You&#146;ll also find a variety of JavaScript widgets that can parse and display feeds in a website. Most web portal and software with a web dashboard features will include a component that can display feeds. And, of course, you can use a reader that supports RSS/Atom feeds, such as Google Reader or Feedly or FlipBoard, to monitor and read feeds.&lt;/p&gt;

&lt;p&gt;If you need to add RSS/Atom reading features to your own software, you can use standard XML parsing tools and, for most language, you&#146;ll find that there are open source libraries specifically designed for parsing feeds.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://manning.com/dmjohnson&quot;&gt;
&lt;img src=&quot;http://rollerweblogger.org/roller/mediaresource/62e51883-23ad-4c45-9182-fbe2085f33d3&quot; alt=&quot;book: RSS and Atom in Action&quot; align=&quot;right&quot;&gt;&lt;/a&gt;For more information in producing and consuming feeds, &lt;a href=&quot;http://manning.com/dmjohnson&quot;&gt;RSS and Atom in Action&lt;/a&gt;. Also, you might find my presentation on &lt;a href=&quot;http://rollerweblogger.org/roller/entry/tri_xml_2006_presentation&quot;&gt;Beyond Blogging: Atom Format and Protocol&lt;/a&gt; useful.&lt;/p&gt;

&lt;h3&gt;Beyond the basics&lt;/h3&gt;

&lt;p&gt;Feeds are a great way to do simple integrations, but there are limitations and there will be times you&#146;ll need to go beyond the basics with RSS and Atom feeds. Here&amp;#39;s an example. Normally, with feeds, clients have to repeatedly poll the feed URL for updates. This is annoying, and inefficient, even with HTTP Conditional GET. To address this problem, you can setup a &lt;a href=&quot;https://code.google.com/p/pubsubhubbub/&quot;&gt;PubSubHubub&lt;/a&gt; server that will subscribe to feeds and will then notify other subscribers instantly when updates are available, so that those other subscribers don&#146;t have to poll.&lt;/p&gt;

&lt;p&gt;Another problem is that, if you don&#146;t poll often enough, you might miss some updates and they may &#147;scroll&#148; off the bottom of the feed before you see them. Feed providers can address this problem by supporting &lt;a href=&quot;http://tools.ietf.org/html/rfc5005&quot;&gt;Feed Paging and Archiving&lt;/a&gt;, which allows clients to use next and previous links to &#147;page&#148; back to feed items that are no longer in the first page of the feed.&lt;/p&gt;

&lt;p&gt;One more beyond-the-basics item to mention is the related pattern Web APIs, which we&amp;#39;ll cover later. Web APIs are listed as a related pattern to Feed-based Integration. That&amp;#39;s because feeds have been used as the basis for several &amp;quot;Web APIs&amp;quot; or protocols. These protocols specify how to use HTTP POST, GET, PUT and DELETE and create, retrieve, update and delete web resources that are represented as feed entries. Examples are the IETF&amp;#39;s &lt;a href=&quot;http://www.atomenabled.org/developers/protocol/atom-protocol-spec.php&quot;&gt;Atom Protocol&lt;/a&gt;, Microsoft&amp;#39;s &lt;a href=&quot;http://www.odata.org/&quot;&gt;OData&lt;/a&gt; and Google&amp;#39;s &lt;a href=&quot;https://developers.google.com/gdata/docs/directory&quot;&gt;GData&lt;/a&gt; APIs.&lt;/p&gt;

&lt;p&gt;That&#146;s it for Feed-based Integration. In my next posts, we&amp;#39;ll move in to the Advanced Patterns.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/10_years_ago</guid>
    <title>10 years ago today</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/10_years_ago</link>
    <pubDate>Tue, 17 Apr 2012 07:41:07 +0000</pubDate>
    <category>Roller</category>
    <category>apacheroller</category>
    <category>blogging</category>
    <category>ibm</category>
    <category>java</category>
    <category>oracle</category>
<description>&lt;p&gt;&lt;img src=&quot;https://rollerweblogger.org/roller/mediaresource/d41c26ed-d162-4b37-b9a5-65fccc404303&quot; alt=&quot;O&amp;#39;Reilly logo&quot; align=&quot;right&quot;&gt;Ten years ago on this day, O&amp;#39;Reilly published an article that I wrote called &lt;a href=&quot;http://onjava.com/pub/a/onjava/2002/04/17/wblogosj2ee.html&quot;&gt;Building an Open Source J2EE Weblogger&lt;/a&gt;, the article that introduced the Roller weblogger (now known as &lt;a href=&quot;http://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt;) to the world. It changed my career and my life in a bunch of nice ways and 10 years later I&amp;#39;m still benefiting from my choice to create Roller and write that article. So you can get a taste of the times, here&amp;#39;s the intro:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://onjava.com/pub/a/onjava/2002/04/17/wblogosj2ee.html&quot;&gt;Building an Open Source J2EE Weblogger&lt;/a&gt;: As a Java developer, you should be aware of the tremendous wealth of open source development software that is available for your use -- even if you have no desire to release any of your own software as open source. In this article, I will introduce you to some of the most useful open source Java development tools by showing you how I used these tools to develop a complete database-driven Web application called Roller.&lt;/p&gt;

&lt;p&gt;Roller fits into the relatively new category of software called webloggers: applications that make it easy for you to maintain a weblog, also known as a blog -- a public diary where you link to recent reading on the Web and comment on items of interest to you.&lt;/p&gt;

&lt;p&gt;The Roller Web application allows you to maintain a Web site that consists of a weblog, an organized collection of favorite Web bookmarks, and a collection of favorite news feeds. You can define Web pages to display your weblog, bookmarks, and news feeds. By editing the HTML templates that define these pages, you have almost total control over the layout and appearance of these pages. Most importantly, you can do all of this without leaving the Roller Web application -- no programming is required.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I&amp;#39;ve written and talked about Roller and the history of Roller numerous times. If you&amp;#39;re interested in learning more about it here&amp;#39;s my most recent Roller presentation, which covers Roller history in some detail:&lt;/p&gt;


&lt;div style=&quot;width:425px;&quot; id=&quot;__ss_2573296&quot;&gt; &lt;strong style=&quot;display:block;margin:12px 0 4px;&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/snoopdave/whats-new-in-roller5&quot; title=&quot;Whats New In Roller5&quot; target=&quot;_blank&quot;&gt;Whats New In Roller5&lt;/a&gt;&lt;/strong&gt;  &lt;div style=&quot;padding:5px 0 12px;&quot;&gt; View more &lt;a href=&quot;http://www.slideshare.net/&quot; target=&quot;_blank&quot;&gt;presentations&lt;/a&gt; from &lt;a href=&quot;http://www.slideshare.net/snoopdave&quot; target=&quot;_blank&quot;&gt;snoopdave&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;


&lt;p&gt;These days, Roller isn&amp;#39;t really thriving as an open source project. Wordpress became the de facto standard blogging package and then micro-blogging took over the world. There are only a couple of active committers and most recent contributions have come via student contributions. Though IBM, Oracle and other companies still use it heavily, they do not contribute back to the project. If you&amp;#39;re interested in contributing to Roller or becoming part of the Apache Software Foundation, then &lt;a href=&quot;http://rollerweblogger.org/project/entry/roller_needs_you&quot;&gt;Roller needs YOU!&lt;/a&gt;.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/apache_rave_a_new_web</guid>
    <title>Apache Rave: A new social mashup engine!</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/apache_rave_a_new_web</link>
    <pubDate>Wed, 28 Mar 2012 08:54:00 +0000</pubDate>
    <category>Social Software</category>
<description>&lt;p&gt;Congrats to the Apache Rave team on reaching top-level project status.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OpenSocial API Blog: Announcing Apache Rave: The project started only a year ago, March 1 2011, when entering the Apache Incubator as a collaborative effort by individuals from a wide range of corporations, non-commercial organizations, and institutes from around the world and was seeded by code donations from The MITRE Corporation, Indiana University Pervasive Technology Institute, SURFnet, OSS Watch, Hippo, and numerous individual developers.&lt;/p&gt;

&lt;p&gt;Rave builds on open standards and leverages and aligns with other open source projects like Apache Shindig and Apache Wookie to deliver a lightweight, flexible, widget-based platform that easily scales across federated application integrations, social intranets, and multi-channel social communities with enhanced personalization and customized content delivery.&lt;/p&gt;
&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/amazon_cloud_half_a_million</guid>
    <title>Amazon cloud: half-a-million Linux servers</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/amazon_cloud_half_a_million</link>
    <pubDate>Sat, 17 Mar 2012 07:18:25 +0000</pubDate>
    <category>Web Development</category>
    <category>amazon</category>
    <category>cloud</category>
<description>&lt;p&gt;Interesting speculation on the make-up of Amazon&amp;#39;s cloud:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Amazon EC2 cloud is made up of almost half-a-million Linux servers | ZDNet : 
&lt;/p&gt;

&lt;p&gt;
We know that Linux on servers is big and getting bigger. We also knew that Linux, thanks to open-source cloud programs like Eucalyptus and OpenStack, was growing fast on clouds. What he hadn&#146;t know that Amazon&#146;s Elastic Compute Cloud (EC2), had close to half-a-million servers already running on a Red Hat Linux variant.
&lt;/p&gt;

&lt;p&gt;
Huang Liu, a Research Manager with Accenture Technology Lab with a Ph.D. in Electrical Engineering whose has done extensive work on cloud-computing, analyzed EC2&#146;s infrastructure and found that Amazon EC2 is currently made up of 454,400 servers.
&lt;/p&gt;

&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/jira_finally_gets_its_own</guid>
    <title>JIRA finally gets its own REST API</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/jira_finally_gets_its_own</link>
    <pubDate>Thu, 1 Mar 2012 09:20:30 +0000</pubDate>
    <category>Web Development</category>
    <category>jira</category>
    <category>json</category>
    <category>oslc</category>
    <category>rest</category>
<description>&lt;p&gt;JIRA&amp;#39;s got a real REST API now:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;REST easy with JIRA 5 | Atlassian Blogs: Now that JIRA 5 is out, let&#146;s talk about one of my favorite features of this new release, JIRA&#146;s new REST API. JIRA has supported remote APIs for many years with SOAP, XML-RPC, and JSON-RPC. However, telling developers that you support SOAP (and only SOAP) is like saying that you like writing applications with COBOL &#151; it&#146;s out of style. Today&#146;s cool kids gravitate towards REST. It&#146;s clean, simple, and highly portable across languages and frameworks.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;And checkout the &lt;a href=&quot;http://docs.atlassian.com/jira/REST/latest/&quot;&gt;nice looking API docs&lt;/a&gt;, which look like they were generated by &lt;a href=&quot;https://github.com/mnot/wadl_stylesheets/&quot;&gt;WADL-to-HTML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An alternative to Atlassian&amp;#39;s new API is the recently release &lt;a href=&quot;https://plugins.atlassian.com/plugins/Rational_OSLC_Adapter_for_JIRA.jar&quot;&gt;Rational OSLC Adapter for JIRA&lt;/a&gt;, which allows you to do more sophisticated integrations with JIRA including delegated UIs for issue creation and selection.&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/wip_widgets_and_gadgets</guid>
    <title>WIP: Widgets and Gadgets</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/wip_widgets_and_gadgets</link>
    <pubDate>Wed, 22 Feb 2012 07:52:11 +0000</pubDate>
    <category>Web Development</category>
    <category>asf</category>
    <category>gadgets</category>
    <category>widgets</category>
<atom:summary type="html">&lt;p&gt;&lt;i&gt;This is the fifth in my series of Web Integration Patterns. Check out the intro at this URL &lt;a href=&quot;http://rollerweblogger.org/roller/entry/web_integration_patterns&quot;&gt;http://rollerweblogger.org/roller/entry/web_integration_patterns&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;Synopsis&lt;/h3&gt; 

&lt;p&gt;
Allow other web sites and applications to integrate your site into their web pages by providing an embeddable user interface, commonly known as a Gadget or Widget, which allows users to view and interact with your site in the context of other sites.
&lt;/p&gt;

&lt;h3&gt;Motivations&lt;/h3&gt;
&lt;ul&gt;
   &lt;li&gt;By embedding Widgets in your site, you can make your site more useful and informative to your users. Users can access relevant information from other sites in the context of your web site.&lt;/li&gt;

   &lt;li&gt;By allow other sites to embed your Widgets, you can give your site and the services that it offers wider reach. Your users can access and interact with your services in the context of other sites.&lt;/li&gt;
&lt;/ul&gt;</atom:summary><description>&lt;p&gt;&lt;i&gt;This is the fifth in my series of Web Integration Patterns. Check out the intro at this URL &lt;a href=&quot;http://rollerweblogger.org/roller/entry/web_integration_patterns&quot;&gt;http://rollerweblogger.org/roller/entry/web_integration_patterns&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;Synopsis&lt;/h3&gt; 

&lt;p&gt;
Allow other web sites and applications to integrate your site into their web pages by providing an embeddable user interface, commonly known as a Gadget or Widget, which allows users to view and interact with your site in the context of other sites.
&lt;/p&gt;

&lt;h3&gt;Motivations&lt;/h3&gt;
&lt;ul&gt;
   &lt;li&gt;By embedding Widgets in your site, you can make your site more useful and informative to your users. Users can access relevant information from other sites in the context of your web site.&lt;/li&gt;

   &lt;li&gt;By allow other sites to embed your Widgets, you can give your site and the services that it offers wider reach. Your users can access and interact with your services in the context of other sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Related Patterns&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dashboards and Portals&lt;/li&gt;
&lt;li&gt;Social Network Plugins&lt;/li&gt;
&lt;li&gt;Delegated Pickers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
A Widget or Gadget is a piece of an application, a visual component or a chunk of user-interface  that can be added to some other application. On the web, Widgets are implemented using HTML and JavaScript, or sometimes Flash. Here we&amp;#39;ll discuss simple Widgets and leave discussion or more advanced usage of Widgets in Dashboards, Portals and Pickers for the more advanced patterns we&amp;#39;ll discuss later.
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Easy to &amp;quot;consume&amp;quot;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;
Widgets are easy, especially for widget &amp;quot;consumers&amp;quot; i.e. those who wish to add an existing Widget to their site. Adding a widget to your site or web application can be as easy as adding a couple of lines of HTML and JavaScript to the pages of your site. You can see some examples of Widgets in the two screen shots I&amp;#39;ve included here, below is the &lt;a href=&quot;http://wayin.com&quot;&gt;Wayin&lt;/a&gt; polling Widget, which shows my latest Wayins:
&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;

&lt;p&gt;

&lt;p&gt;
And below is the &lt;a href=&quot;http://flickr.com&quot;&gt;Flickr&lt;/a&gt; Widget, which shows a random selection of my photos from Flickr. You can see these two Widgets in action on the &lt;a href=&quot;http://rollerweblogger.org/roller&quot;&gt;front page of my blog&lt;/a&gt;. And, by the way, the Like, Tweet and +1 buttons on the right of this entry (when viewed on my blog site) are also Widgets.
&lt;/p&gt;

&lt;p style=&quot;text-align:center;&quot;&gt;

&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Relatively easy to create&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Developing an entirely new Widget more complicated than using one that already exists. Creating a new Widget typically requires writing HTML and JavaScript code and employing some interesting tricks. The simplest way to implement a Widget is to use an iframe, allowing a portion of one site to be embedded in another site.&lt;/p&gt;

&lt;p&gt;I&amp;#39;m not going to go into detail as there are already so many good resources on the web for learning about Widget crafting. For example, Alex Marandon wrote a &lt;a href=&quot;http://alexmarandon.com/articles/web_widget_jquery/&quot;&gt;pretty comprehensive blog post on creating Widgets&lt;/a&gt; covering  basic issues like how to ensure that your widget code does not interfere with the page hosting the Widget, how to dynamically load resources and how to bypass browsers&#146; single-origin policy using JSON-P.
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Widget and Gadget standards&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;
For simple Widgets you don&amp;#39;t really need any standards other than HTML and JavaScript. If you want to target specific web site Dashboards or Portals, like NetVibes, or enterprise web applications like Jive or Atlassian&amp;#39;s software development tools, then you&amp;#39;ll need to create your Widgets with specific APIs in mind. For example, &lt;a href=&quot;http://dev.netvibes.com/doc/&quot;&gt;NetVibes&lt;/a&gt; provides it&amp;#39;s own Widget API, while Jive and Atlassian support the &lt;a href=&quot;http://code.google.com/apis/opensocial/&quot;&gt;Open Social Gadget APIs&lt;/a&gt;. I&amp;#39;ll talk more about these issues when I cover the related patterns Dashboards, Portals and Social Network Plugins.
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Wrapping up...&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;That&amp;#39;s it for Widgets and Gadgets. Next up: Feed-based Integration.&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/twitter_moving_away_from_hashbang</guid>
    <title>Twitter moving away from hashbang URLs</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/twitter_moving_away_from_hashbang</link>
    <pubDate>Tue, 21 Feb 2012 13:02:28 +0000</pubDate>
    <category>Web Development</category>
<description>
&lt;p&gt;&lt;p&gt;This is such good news for the web that it gets its own blog entry. According to &lt;a href=&quot;http://danwebb.net/&quot;&gt;Dan Web&lt;/a&gt;, an engineer at Twitter:&lt;/p&gt;&lt;/p&gt;


&lt;p&gt;&lt;pre&gt;@timhaines plus, now I&amp;#39;m in charge of undoing twitters hashbang URLs I can &lt;br&gt;
    confirm that all the issues in that article are very real.&lt;br&gt;
@danwrong x months from now (when your project completes) Twitter will no &lt;br&gt;
    longer use hashbangs?&lt;br&gt;
@timhaines correct. All gone. It was a mistake for several reasons. &lt;br&gt;
    PushState or bust.&lt;/pre&gt;&lt;/p&gt;


&lt;p&gt;&lt;p&gt;The whole conversation is here: &lt;br&gt;
&lt;a href=&quot;http://storify.com/timhaines/hashbang-conversation&quot;&gt;http://storify.com/timhaines/hashbang-conversation&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Here&amp;#39;s an excellent chapter on HTML 5 history and PushState: &lt;a href=&quot;http://diveintohtml5.info/history.html&quot;&gt;http://diveintohtml5.info/history.html&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;And in case you&amp;#39;ve forgotten, here&amp;#39;s &lt;a href=&quot;http://www.tbray.org/ongoing/When/201x/2011/02/09/Hash-Blecch&quot;&gt;why hashbangs suck&lt;/a&gt;.&lt;/p&gt;&lt;/p&gt;

</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/the_meaning_on_the_facebook</guid>
    <title>The meaning of the Facebook like button</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/the_meaning_on_the_facebook</link>
    <pubDate>Sat, 11 Feb 2012 16:42:54 +0000</pubDate>
    <category>Social Software</category>
    <category>facebook</category>
<description>&lt;p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p&gt;far as I can tell, clicking theFacebook &amp;quot;Like&amp;quot; button means &amp;quot;please spam me endlessly&amp;quot;&lt;/p&gt;&amp;mdash; Dave Johnson (@snoopdave) &lt;a href=&quot;https://twitter.com/snoopdave/status/166208616825753600&quot; datetime=&quot;2012-02-05T17:16:36+00:00&quot;&gt;February 5, 2012&lt;/a&gt;&lt;/blockquote&gt;

&lt;/p&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/html5_microdata_and_schema_org</guid>
    <title>HTML5 Microdata and Schema.org</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/html5_microdata_and_schema_org</link>
    <pubDate>Thu, 9 Feb 2012 08:48:06 +0000</pubDate>
    <category>Web Development</category>
    <category>html5</category>
    <category>linkeddata</category>
    <category>wip</category>
<description>&lt;p&gt;Worth a read and related to my Web Integration Patterns post on &lt;a href=&quot;http://rollerweblogger.org/roller/entry/wip_embedded_properties_in_html&quot;&gt;Embedded Properties in HTML&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
HTML5 Microdata and Schema.org:HTML5 Microdata and Schema.org
On June 2, 2011, Bing, Google, and Yahoo! announced the joint effort Schema.org. When the big search engines talk, Web site authors listen. This article is an introduction to Microdata and Schema.org. The first section describes what HTML5, Microdata and Schema.org are, and the problems they have been designed to solve. With this foundation in place section 2 provides a practical tutorial of how to use Microdata and Schema.org using a real life example from the cultural heritage sector. Along the way some tools for implementers will also be introduced. Issues with applying these technologies to cultural heritage materials will crop up along with opportunities to improve the situation.&lt;/blockquote&gt;
</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/understanding_google_s_new_privacy</guid>
    <title>Understanding Google&amp;#39;s new privacy policy</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/understanding_google_s_new_privacy</link>
    <pubDate>Thu, 2 Feb 2012 13:39:11 +0000</pubDate>
    <category>Social Software</category>
    <category>google</category>
    <category>privacy</category>
<description>&lt;p&gt;Worth a read:&lt;/p&gt;
&lt;blockquote&gt;
Understanding Google&amp;#39;s new privacy policy: When Google changed its privacy policy last week, they made a strong effort to ensure that everyone knew that a change had occurred, but if you tried to figure out what had actually changed, you had to wade through a lot of buzzwords and legalese. Now the Electronic Frontier Foundation&amp;#39;s Rainey Reitman explains it in simple language.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/how_to_select_an_open</guid>
    <title>How to select an Open Source project</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/how_to_select_an_open</link>
    <pubDate>Tue, 31 Jan 2012 07:00:15 +0000</pubDate>
    <category>Open Source</category>
<description>&lt;p&gt;A reasonable check-list for selecting an open source project:&lt;/p&gt;

&lt;blockquote&gt;
How I select Open Source projects: An Open Source project is more than its set of tools.

But this post is not about what is better, github or ASF. Much has been said already about it (and too much bullshit). What really bugs me is that people seem to choose Open Source projects after the tools the projects use.

Here is my personal list after which I choose projects.&lt;p&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/ibm_abandons_lotus_symphony</guid>
    <title>IBM abandons Lotus Symphony</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/ibm_abandons_lotus_symphony</link>
    <pubDate>Tue, 31 Jan 2012 06:53:56 +0000</pubDate>
    <category>IBM</category>
    <category>openoffice</category>
<description>&lt;p&gt;Apparently 2001 called and asked for its office suite back.&lt;/p&gt;

&lt;blockquote&gt;
IBM to close down Symphony, its OpenOffice fork | ZDNet : This move can&#146;t come as too much of a surprise. In July 2011, IBM started donating its Symphony code to the Apache Foundation, which is now overseeing the remains of the OpenOffice project. I say &#147;remains&#148; because most of the development energy in the OpenOffice family has been going to the LibreOffice fork. Many users and Linux distributors, such as Ubuntu, have replaced OpenOffice with LibreOffice.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/scott_mcnealy_s_wayin_weighs</guid>
    <title>Scott McNealy&amp;#39;s Wayin weighs in on the election</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/scott_mcnealy_s_wayin_weighs</link>
    <pubDate>Thu, 26 Jan 2012 08:12:58 +0000</pubDate>
    <category>Social Software</category>
    <category>wayin</category>
<description>&lt;p&gt;Nice coverage of Wayin in CNN Money:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Scott McNealy&amp;#39;s Wayin weighs in on the election - Fortune Tech: Nearly 30 years after he co-founded Sun, and two years after the company sold to Oracle (ORCL), one of the tech industry&amp;#39;s most outspoken and colorful executives is back. Only instead of selling servers and software to businesses, he&amp;#39;s trying to get consumers, sports teams, and corporations -- and politicians -- to embrace a social media tool that&amp;#39;s a little hard to define: It is part polling app, part interactive media tool (think Twitter), and part enterprise service.&lt;/p&gt;

&lt;p&gt;The polling software at the heart of Wayin is super easy to use. It allows you to create basic questionnaires, on the web or using a mobile app, by selecting an image and asking a question. Pick a photo of the Fab Four, ask Who is your favorite Beatle?, and bingo, the answers from your followers will start trickling in immediately.&lt;/p&gt;&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/tim_bray_on_httpurlconnection</guid>
    <title>Tim Bray on HttpURLConnection</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/tim_bray_on_httpurlconnection</link>
    <pubDate>Mon, 23 Jan 2012 08:26:37 +0000</pubDate>
    <category>Java</category>
    <category>http</category>
<description>&lt;p&gt;Good information on a bad API. The comments are good too:&lt;/p&gt;
&lt;blockquote&gt;
HttpURLConnection&amp;#39;s Dark Secrets: If you&#146;re programming in the Java language and want to talk to a Web server, there are several libraries you can choose from. HttpURLConnection is one popular choice, and for Android programming, the engineering team has now officially suggested that you use it where possible.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/o_grady_on_dynamodb</guid>
    <title>O&amp;#39;Grady on DynamoDB</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/o_grady_on_dynamodb</link>
    <pubDate>Fri, 20 Jan 2012 07:57:12 +0000</pubDate>
    <category>Web Development</category>
    <category>amazon</category>
    <category>bigdata</category>
<description>&lt;p&gt;Great analysis, as usual, from Stephen O&amp;#39;Grady of Redmonk. Here&amp;#39;s the part about Dynamo that concerns me, the lock-in:&lt;/p&gt;
&lt;blockquote&gt;
Amazon DynamoDB: First Look: Like the initial wave of PaaS platforms, however, DynamoDB is available only through a single provider. Unlike Amazon&#146;s RDS, which is essentially compatible with MySQL, DynamoDB users will be unable to migrate off of the service seamlessly. The featureset can be replicated using externally available code &#150; via those projects that were originally inspired by DynamoDB, for example &#150; but you cannot at this time download, install and run DynamoDB locally.
&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/amazon_dymamodb</guid>
    <title>Amazon DymamoDB</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/amazon_dymamodb</link>
    <pubDate>Thu, 19 Jan 2012 07:51:54 +0000</pubDate>
    <category>Web Development</category>
    <category>amazon</category>
    <category>nosql</category>
<description>&lt;p&gt;Worth a read:&lt;/p&gt;
&lt;blockquote&gt;
Werner Vogels: Amazon DynamoDB is a fully managed NoSQL database service that provides fast performance at any scale. Today&#146;s web-based applications often encounter database scaling challenges when faced with growth in users, traffic, and data. With Amazon DynamoDB, developers scaling cloud-based applications can start small with just the capacity they need and then increase the request capacity of a given table as their app grows in popularity. Their tables can also grow without limits as their users store increasing amounts of data. Behind the scenes, Amazon DynamoDB automatically spreads the data and traffic for a table over a sufficient number of servers to meet the request capacity specified by the customer. Amazon DynamoDB offers low, predictable latencies at any scale.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/the_joys_of_solitude</guid>
    <title>The joys of solitude</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/the_joys_of_solitude</link>
    <pubDate>Mon, 16 Jan 2012 07:37:54 +0000</pubDate>
    <category>General</category>
<description>&lt;p&gt;Worth a read:&lt;/p&gt;
&lt;blockquote&gt;
The Rise of the New Groupthink - NYTimes.com: Solitude has long been associated with creativity and transcendence. &#147;Without great solitude, no serious work is possible,&#148; Picasso said. A central narrative of many religions is the seeker &#151; Moses, Jesus, Buddha &#151; who goes off by himself and brings profound insights back to the community.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/obama_administration_comes_out_against</guid>
    <title>Obama Administration Comes Out Against SOPA, PIPA</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/obama_administration_comes_out_against</link>
    <pubDate>Sat, 14 Jan 2012 14:59:33 +0000</pubDate>
    <category>General</category>
    <category>censorship</category>
    <category>politics</category>
<description>&lt;p&gt;I feel pretty strongly about this. I won&amp;#39;t vote for a politician who backs SOPA or PIPA:&lt;/p&gt;
&lt;blockquote&gt;
Obama Administration Comes Out Against SOPA, PIPA | TPM Idea Lab:Even more promising for critics of the bills, the Administration came down firmly against one of the most vehemently opposed portions of the bills &#151; the part that would give the government the power to force Internet Service Providers to stop loading overseas webpages accused of piracy. Under the original versions of SOPA and PIPA, ISPs would be required to change their Doman Name System settings to block sites accused of piracy, a measure that critics said would essentially break the Internet and make it more insecure.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/nyc_gets_a_software_engineering</guid>
    <title>NYC gets a Software Engineering High School</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/nyc_gets_a_software_engineering</link>
    <pubDate>Fri, 13 Jan 2012 15:44:58 +0000</pubDate>
    <category>General</category>
    <category>education</category>
<description>&lt;p&gt;Worth a read:&lt;/p&gt;
&lt;blockquote&gt;
New York City gets a Software Engineering High School - Joel on Software:This fall New York City will open The Academy for Software Engineering, the city&#146;s first public high school that will actually train kids to develop software. The project has been a long time dream of Mike Zamansky, the highly-regarded CS teacher at New York&#146;s elite Stuyvesant public high school. It was jump started when Fred Wilson, a VC at Union Square Ventures, promised to get the tech community to help with knowledge, advice, and money.&lt;/blockquote&gt;</description>  </item>
  <item>
    <guid isPermaLink="true">https://rollerweblogger.org/roller/entry/lockdown_the_coming_war_on</guid>
    <title>The coming war on general-purpose computing</title>
    <dc:creator>Dave Johnson</dc:creator>
    <link>https://rollerweblogger.org/roller/entry/lockdown_the_coming_war_on</link>
    <pubDate>Wed, 11 Jan 2012 16:58:15 +0000</pubDate>
    <category>Open Source</category>
    <category>copyright</category>
    <category>drm</category>
    <category>sopa</category>
<description>&lt;p&gt;Cory Doctorow&amp;#39;s keynote to the Chaos Computer Congress. Worth a read:&lt;/p&gt;
&lt;blockquote&gt;
Lockdown: The coming war on general-purpose computing - Boing Boing&lt;/blockquote&gt;</description>  </item>
</channel>
</rss>