Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://feeds.feedburner.com/WisselNet

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <rss version="2.0"
  3. xmlns:dc="http://purl.org/dc/elements/1.1/"
  4. xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  5. xmlns:admin="http://webns.net/mvcb/"
  6. xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  7. xmlns:content="http://purl.org/rss/1.0/modules/content/"
  8. xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  9. xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  10. xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  11. xmlns:atom="http://www.w3.org/2005/Atom"
  12. >
  13. <channel>
  14. <title>wissel.net</title>
  15. <description>Usability - Productivity - Business - The web - Singapore &amp; Twins</description>
  16. <lastBuildDate>Tue, 21 Mar 2017 01:43:50 +0000</lastBuildDate>
  17. <link>https://stephan.wissel.net/blog</link>
  18. <language>en</language>
  19. <atom:link href="https://stephan.wissel.net/stw/wisselblog.nsf/stories.rss" rel="self" type="application/rss+xml" />
  20. <image>
  21. <title>wissel.net</title>
  22. <url>https://stephan.wissel.net/blog/rss.gif</url>
  23. <link>https://stephan.wissel.net/blog</link>
  24. </image>
  25. <item>
  26. <title>Agile Outsourcing</title>
  27. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-AJDEWL?Open</link>
  28. <description><![CDATA[ The problem
  29. Outsourcing is a "special" animal. Typically the idea is to save cost by letting a service provider execute work. The saving cost happens because the service provider is supposed to be able to perform this actions at scale. Increasingly ... ]]></description>
  30. <dc:creator>Stephan H Wissel</dc:creator>
  31. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-AJDEWL?Open</comments>
  32. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-AJDEWL?Open</guid>
  33. <content:encoded><![CDATA[ <h2>The problem</h2>
  34. <a href="http://www.investopedia.com/terms/o/outsourcing.asp">Outsourcing</a> is a "special" animal. Typically the idea is to save cost by letting a service provider execute work. The saving cost happens because the service provider is supposed to be able to perform this actions at scale. Increasingly outsourcing deals are motivated by a skill squeeze. So instead of maintaining in-house expertise, rely on the vendors to keep the light on.<br />
  35. This is where the trouble starts. Negotiations on outsourcing contracts revolves around price (drive it down) and the <a href="https://en.wikipedia.org/wiki/Service-level_agreement">SLA</a> (add as many 9 behind the comma as possible). The single outcome of such contracts is extreme risk aversion. For illustration here is the impact of SLA levels :
  36. <table>
  37. <thead>
  38. <tr><th>SLA</th><th>Total annual Downtime</th></tr>
  39. </thead>
  40. <tbody>
  41. <tr><td>98%</td><td>7 days, 6h, 12min</td></tr>
  42. <tr><td>99%</td><td>3 days, 15h, 36min</td></tr>
  43. <tr><td>99.9%</td><td>8h, 45min, 36sec</td></tr>
  44. <tr><td>99.99%</td><td>52min, 34sec</td></tr>
  45. <tr><td>99.999%</td><td>5min, 16sec</td></tr>
  46. <tr><td>99.9999%</td><td>32 sec</td></tr>
  47. </tbody>
  48. </table>
  49. The fixation on SLA has a clinical term: <a href="https://en.wikipedia.org/wiki/Obsessive%E2%80%93compulsive_disorder">OCD</a>. Any change is considered as dangerous as someone holding a knife at your throat and asked you to dance.<br />
  50. Looking at some of the figures (I can't share) I would claim that short of highly parallel (and expensive) transaction system anything above 99.9% is wishful thinking. That doesn't deter negotiators to aim for a "look how many 9th I got" trophy. (While the Buddha reminds us: one cause of suffering is to close your eyes to reality). Expensive SLA violation clauses let outsourcers freeze all system, since any change (read: patches, upgrades, enhancements) is <a href="http://www.statuscast.com/application-downtime-according-to-idc-gartner-and-others/">rightly identified</a> as grave risk (to the profits).<br />
  51. So all sorts of processes and checks get implemented to vet any change request and in practise avoid them.<br />
  52. This usually leads to a lot of bureaucracy and glacial progress. As a result discontent, especially on the use of non-transactional system grows: Stuff like outdated eMail clients, lack of mobile support etc. etc.<br />
  53. The relation between oursourcer and oursourcee grows, inevitably,  challenging over time. Does it have to be that way?
  54. <h2>Some fresh thinking</h2>
  55. Just move to cloud might not be the answer (or everybody would be there, it's <a href="http://www.networkworld.com/article/3020235/cloud-computing/and-the-cloud-provider-with-the-best-uptime-in-2015-is.html">such a nice place</a>). So what could be done? Here are some thoughts:
  56. <ul>
  57. <li>Kiss goodby the wholesale SLA agreement. Classify systems based on business impact. A booking system for an airline surly deserves three nines (I doubt that four would make sense), while a website can live with one nine (as long as it distributed over the year)</li>
  58. <li>Take a page from the PaaS offerings: each element of the environment has a measurement and a price. So the outsourcing provider can offer ala card services instead of freezing the environment. A catalogue entry could be "Running a current and patched DB/2", another entry could be "Run a legacy IIS, version xx"</li>
  59. <li>Customer and provider would agree on an annual catalogue value, based on the starting environment and any known plan at the time</li>
  60. <li>The catalogue would allow to decommission unneeded system and replace them with successors without much hassle (out with PHP, in with node.js)</li>
  61. <li>Automate, Automate, Automate - An outsourcer without DevOps (Puppet, Chef and tight monitoring) didn't get the 2017 message</li>
  62. <li>Transparency: Running systems over processes, Customer satisfaction over unrealistic SLA, Automation over documentation (I hear the howling), Repeatable procedures over locked down environments</li>
  63. </ul>
  64. What do you think? ]]></content:encoded>
  65. <pubDate>Wed, 08 Feb 2017 11:00:05 +0000</pubDate>
  66. <slash:comments>1</slash:comments>
  67. <category>Software</category>
  68. </item>
  69. <item>
  70. <title>SAML and the Command Line</title>
  71. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-AJ4J37?Open</link>
  72. <description><![CDATA[ One of the best kept secrets of Connections Cloud S1 is the Traveler API. The API allows interactions that are missing from the Admin UI, like deleting a specific device or implementing an approval workflow.
  73. Unfortunately the API only offers authentication ... ]]></description>
  74. <dc:creator>Stephan H Wissel</dc:creator>
  75. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-AJ4J37?Open</comments>
  76. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-AJ4J37?Open</guid>
  77. <content:encoded><![CDATA[ One of the best kept secrets of Connections Cloud S1 is the <a href="https://www-10.lotus.com/ldd/dominowiki.nsf/dx/IBM_Notes_Traveler_Administration_API">Traveler API</a>. The API allows interactions that are missing from the Admin UI, like deleting a specific device or implementing an approval workflow.<br />
  78. Unfortunately the API only offers authentication via  SAML, OAuth or BasicAuth are missing. So any application interacting with the API needs to do <a href="https://www.google.com.sg/search?q=SAML+Dance+Connections+Cloud">The SAML Dance</a>. That's annoying when you have an UI to use, and a formidable challenge when you have a command line application, like a cron Job running unsupervised at interval.<br />
  79. One lovely step in the process: the IBM IdP returns a HTML page with a hidden form containing the SAML assertion result to be posted back to the application provider. Quite interesting, when your application provider is a command line app. Let's get to work.<br />
  80. The script is written in <a href="https://nodejs.org">node.js</a> and uses <a href="https://www.npmjs.com/package/request">request</a> and <a href="https://www.npmjs.com/package/fast-html-parser">fast-html-parser</a> npm package. The first step is to load the login form (which comes with a first set of cookies)<br />
  81. <pre class="brush: js">
  82. var requestOptionsTemplate = {
  83.    headers: {
  84.        'Origin': 'https://api.notes.ap.collabserv.com/api/traveler/',
  85.        'User-Agent': 'ancy CommandLine Script',
  86.        'Connection': 'keep-alive',
  87.        'Cache-Control': 'max-age=0',
  88.        'Upgrade-Insecure-Requests': 1
  89.    },
  90.    'method': 'GET'
  91. };
  92.  
  93. function scLoginPart1() {
  94.    console.log('Authenticating to SmartCloud ...');
  95.    var requestOptions = Object.assign({}, requestOptionsTemplate);
  96.    requestOptions.url = 'https://apps.na.collabserv.com/manage/account/dashboardHandler/input';
  97.    request(requestOptions, scLoginPart2);
  98. }
  99. </pre>
  100. <br />
  101. The function calls the URL where the login form can be found. The result gets delivered to the function <code>scLoginPart2</code>. That function makes use of a global configuration variable <code>config</code> that was created through <code>const config = require("./config.json")</code> and contains all the credentials we need. Step2 submits the form and hands over to Step3.
  102. <br />
  103. <pre class="brush: js">
  104. function scLoginPart2(err, httpResponse, body) {
  105.    if (err) {
  106.        return console.error(err);
  107.    }
  108.    // Capture cookies
  109.    var outgoingCookies = captureCookies(httpResponse);
  110.    var requestOptions = Object.assign({}, requestOptionsTemplate);
  111.    requestOptions.headers.Cookie = outgoingCookies.join('; ');
  112.    requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
  113.    requestOptions.method = 'POST';
  114.    requestOptions.url = 'https://apps.ap.collabserv.com/pkmslogin.form';
  115.    requestOptions.form = {
  116.        'login-form-type': 'pwd',
  117.        'error-code': '',
  118.        'username': config.smartcloud.user,
  119.        'password': config.smartcloud.password,
  120.        'show_login': 'showLoginAgain'
  121.    }
  122.    request(requestOptions, scLoginPart3);
  123. }
  124.  
  125. function captureCookies(response) {
  126.    var incomingCookies = response.headers['set-cookie'];
  127.    var outgoingCookies = [];
  128.    if (incomingCookies) {
  129.        incomingCookies.forEach(function(cookie) {
  130.            outgoingCookies.push(cookie.split(';')[0]);
  131.        });
  132.    }
  133.    // Array, allows for duplicate coolie names
  134.    return outgoingCookies;
  135. }
  136. </pre>
  137. <br />
  138. Part 3 / 4 finally collect all the cookies we need, so to turn attention to getting the API token in step 5
  139. <br />
  140. <pre class="brush: js">
  141. function scLoginPart3(err, httpResponse, body) {
  142.    if (err) {
  143.        console.error('Login failed miserably');
  144.        return console.error(err);
  145.    }
  146.    // Login returns not 200 but 302
  147.    // see https://developer.ibm.com/social/2015/06/23/slight-changes-to-the-form-based-login/
  148.    if (httpResponse.statusCode !== 302) {
  149.        return console.error('Wrong status code received: ' + httpResponse.statusCode);
  150.    }
  151.  
  152.    var outgoingCookies = captureCookies(httpResponse);
  153.    var redirect = httpResponse.headers.location;
  154.  
  155.    // This is the 3rd request we need to make to get finally all cookies for app.na
  156.    var requestOptions = Object.assign({}, requestOptionsTemplate);
  157.    requestOptions.headers.Cookie = outgoingCookies.join('; ');
  158.    requestOptions.url = redirect;
  159.    request(requestOptions, scLoginPart4);
  160. }
  161.  
  162. function scLoginPart4(err, httpResponse, body) {
  163.    if (err) {
  164.        console.error('Login redirect failed miserably');
  165.        return console.error(err);
  166.    }
  167.    var cookieHarvest = captureCookies(httpResponse);
  168.    // Now we have some cookies in app, we need the SAML dance for api.notes
  169.    scLoginPart5(cookieHarvest)
  170. }
  171. </pre>
  172. <br />
  173. In Part 5 we first request the URL with actual data (devices in our case), but get another SAML dance step, since we have <code>apps.na</code> vs <code>api.notes</code> in the URL ]]></content:encoded>
  174. <pubDate>Mon, 30 Jan 2017 13:41:02 +0000</pubDate>
  175. <slash:comments>0</slash:comments>
  176. <category>NodeJS</category>
  177. <category>JavaScript</category>
  178. </item>
  179. <item>
  180. <title>GIT deploy your static sites - Part 1</title>
  181. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-AHJ78J?Open</link>
  182. <description><![CDATA[ When you, in principal, like the idea to serve SPA from the http server, you will encounter the pressing question: where do babies come from how to get your application deployed onto the http server? This applies to nodeJS applications too, but that is part ... ]]></description>
  183. <dc:creator>Stephan H Wissel</dc:creator>
  184. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-AHJ78J?Open</comments>
  185. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-AHJ78J?Open</guid>
  186. <content:encoded><![CDATA[ When you, in principal, like the idea to <a href="/blog/d6plinks/SHWL-AHHBPB">serve SPA from the http server</a>, you will encounter the pressing question: <strike>where do babies come from</strike> how to get your application deployed onto the http server? This applies to nodeJS applications too, but that is part of another story for another time.<br />
  187. On <a href="https://console.ng.bluemix.net/">Bluemix</a> that's easy: just use a <a href="https://hub.jazz.net/docs/deploy/#example">Pipeline</a>.<br />
  188. For mere mortal environments there are several options:
  189. <ul>
  190. <li>Just FTP them - insecure unless you use sftp/scp. Big pain here: deleting obsolete files</li>
  191. <li>Setup rsync. When done with a ssh certificate can be reasonably automated. Same pain applies: deleting obsolete files</li>
  192. <li>Use a <a href="https://git-scm.com/">GIT</a> based deployment. This is what I will discuss further</li>
  193. </ul>
  194. I like a repository based deployment since it fits nicely into a development based workflow. The various git gui tools provide insight what has changed between releases and if things go wrong, you can roll back to a previous version or you can wipe data and reestablish them from the repository. Designing the flow, I considered the following constraints:
  195. <ul>
  196. <li>The repositories would sit on the web server</li>
  197. <li>Typically a repository would sit in <code>.git</code> inside the site directory. While you could protect that with access control, I decided I don't want to have it in separate directories</li>
  198. <li>When pushing to the <code>master</code> branch, the site should get updated, not on any other branch. You can extend my approach to push other branches to other sites - so you get a test/demo/staging capability</li>
  199. <li>Setting up a new site should be fast and reliable (including https - but that's part 2)</li>
  200. </ul>
  201. The "secret" ingredients here are <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks">git-hooks</a>, in specific the <code>post-receive</code>. Hooks, in a nutshell are shell scripts that are triggered by events that happen to a git environment. I got inspired by <a href="http://nicolasgallagher.com/simple-git-deployment-strategy-for-static-sites/">this entry</a> but wanted to automate the setup. ]]></content:encoded>
  202. <pubDate>Thu, 12 Jan 2017 04:26:22 +0000</pubDate>
  203. <slash:comments>0</slash:comments>
  204. <category>nginx</category>
  205. <category>WebDevelopment</category>
  206. </item>
  207. <item>
  208. <title>Serving Single Page Applications with Domino</title>
  209. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-AHHBPB?Open</link>
  210. <description><![CDATA[ Single Page Applications (SPA) are all the rage. They get developed with AngularJS, ReactJS or {insert-your-framework-of-choice}. Those share a few communialities:
  211.  
  212. the application is served by a static web server
  213. data is provided via an API, typically ... ]]></description>
  214. <dc:creator>Stephan H Wissel</dc:creator>
  215. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-AHHBPB?Open</comments>
  216. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-AHHBPB?Open</guid>
  217. <content:encoded><![CDATA[ <a href="https://en.wikipedia.org/wiki/Single-page_application">Single Page Applications</a> (SPA) are all the rage. They get developed with AngularJS, ReactJS or {insert-your-framework-of-choice}. Those share a few communialities:
  218. <ul>
  219. <li>the application is served by a static web server</li>
  220. <li>data is provided via an API, typically reading/writing JSON via REST or graph</li>
  221. <li>authentication is often long lasting (remember me...) based on <a href="https://jwt.io/">JWT</a></li>
  222. <li>authentication is highly flexible: login with {facebook|google|linkedin|twitter} or a corporate account. Increasingly 2 factor authentication is used (especially <a href="http://eur-lex.europa.eu/eli/reg/2016/679/oj">in Europe</a>)</li>
  223. </ul>
  224. How does Domino fit into the picture with its integrated http stack, authentication and database? The answer isn't very straight forward. Bundling components creates ease of administration, but carries the risk that new technologies are implemented late (or not at all). For anything internet facing that's quite some risk. So here is what I would do:<br />
  225. <img src="https://wissel.net/blog/Images/SHWL-AHHBNS/$File/DominoAPIServer.jpg" border="0" alt="Red/Green Zone layout for Domino" /><br /> ]]></content:encoded>
  226. <pubDate>Wed, 11 Jan 2017 08:14:51 +0000</pubDate>
  227. <slash:comments>3</slash:comments>
  228. <category>IBM Notes</category>
  229. <category>XPages</category>
  230. </item>
  231. <item>
  232. <title>Lessons from Project OrangeBox</title>
  233. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-AH945X?Open</link>
  234. <description><![CDATA[ Project OrangeBox, the Solr free search component, was launched after the experiments with Java8, Vert.x and RxJava in TPTSNBN concluded. With a certain promise we were working on a tight dead line and burned way more midnight oil than I would have wished ... ]]></description>
  235. <dc:creator>Stephan H Wissel</dc:creator>
  236. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-AH945X?Open</comments>
  237. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-AH945X?Open</guid>
  238. <content:encoded><![CDATA[ <p>Project OrangeBox, the <a href="http://lucene.apache.org/solr/">Solr</a> free search component, was launched after the experiments with Java8, <a href="http://vertx.io/">Vert.x</a> and <a href="https://github.com/ReactiveX/RxJava">RxJava</a> in <a href="#" title="The project that shall not be named" style="color: black; font-weight: normal">TPTSNBN</a> concluded. With <a href="https://twitter.com/edbrill/status/799733973870059520">a certain promise</a> we were working on a tight dead line and burned way more midnight oil than I would have wished for.</p>
  239. <p>Anyway, I had the opportunity to work with great engineers and we shipped as promised. There are quite some lesson to be learned, here we go (in no specific order):</p>
  240. <ul>
  241. <li><b>Co-locate</b><br />
  242. The Verse team is spread over the globe: USA, Ireland, Belarus, China, Singapore and The Philippines. While this allows for 24x7 development, it also poses a substantial communications overhead. We made the largest jumps in both features and quality during and after co-location periods. So any sizable project needs to start and be interluded with co-location time. Team velocity will greatly benefit</li>
  243. <li><b>No holy cows</b><br />
  244. For VoP we slaughtered the "Verse is Solr" cow. That saved the Domino installed base a lot of investments in time and resources. Each project has its "holy cows": Interfaces, tool sets, "invaluable, immutable code", development pattern, processes. You have to be ready to challenge them by keeping a razor sharp focus on customer success. Watch out for Prima donnas (see next item)</li>
  245. <li><b>No <a href="https://www.google.com.sg/search?q=define+primadonna">Prima Donnas</a></b><br />
  246. As software engineers we are very prone to perceive our view of the world as the (only) correct one. After all we create some of it. In a team setting that's deadly. Self reflection and <a href="http://www.planetofsuccess.com/blog/2011/developing-empathy-walk-a-mile-in-someone%E2%80%99s-shoes/">empathy</a> are as critical to the success as technical skills and perseverance.<br />
  247. <a href="http://amzn.to/2ixejLi">Robert Sutton</a>, one of my favourite Harvard authors, expresses that <a href="http://amzn.to/2j2ljD1">a little bolder</a>.<br />
  248. In short: A team can only be bigger than the sum of its members, when the individuals see themselves as members and are not hovering above it</li>
  249. <li><b>Unit test are overrated</b><br />
  250. I hear howling, read on. Like "A journey of <a href="http://www.bbc.co.uk/worldservice/learningenglish/movingwords/shortlist/laotzu.shtml">a thousand miles</a> begins with a single step" you can say: "Great software starts with a Unit Test". Begins, not: "Great software consists of Unit Tests". A great journey that only has steps ends tragically in death by starvation, thirst or evil events.<br />
  251. Same applies to your test regime: You start with Unit tests, write code, pass it on to the next level of tests (module, integration, UI) etc. So unit tests are a "<a href="https://en.wikipedia.org/wiki/Sine_qua_non">conditio sine qua non</a>" in your test regime, but in no way sufficient</li>
  252. <li><b>Test pyramid and good test data</b><br />
  253. Starting with unit tests (we used <a href="http://junit.org/">JUnit</a> and <a href="http://easymock.org/">EasyMock</a>), you move up to module tests. There, still written in JUnit, you check the correctness of higher combinations. Then you have API test for your REST API. Here we used <a href="https://www.getpostman.com/">Postman</a> and its node.js integration <a href="https://www.getpostman.com/docs/newman_intro">Newman</a>.<br />
  254. Finally you need to test end-to-end including the UI. For that <a href="http://www.seleniumhq.org/">Selenium</a> rules supreme. Why not e.g. <a href="http://phantomjs.org/">PhantomJS</a>? Selenium drives real browsers, so you can (automate) test against all rendering engines, which, as a fact of the matter, behave unsurprisingly different.<br />
  255. One super critical insight: You need a <b>good set</b> of diverse test data, both expected and unexpected inputs in conjunction with the expected outputs. A good set of <b>fringe data</b> makes sure you catch challenges and border conditions early.<br />
  256. Last not least: Have performance tests from the very beginning. We used both <a href="http://www-03.ibm.com/software/products/en/performance">Rational Performance Tester</a> (RPT) and <a href="http://jmeter.apache.org/">Apache JMeter</a>. RPT gives you a head start in creating tests, while JMeter's XML file based test cases were easier to share and manipulate. When you are short of test infrastructure (quite often the client running tests is the limiting factor) you can offload JMeter tests to <a href="https://www.blazemeter.com/">Blazemeter</a> or <a href="https://flood.io/">Flood.io</a></li>
  257. <li><b>Measure, measure, measure</b><br />
  258. You need to know where your code is spending its time in. We employed a number of tools to get good metrics. You want to look at averages, min, max and standard deviations of your calls. David even wrote a specific plugin to see the native calls (note open, design note open) or Java code would produce (This will result in future Java API improvements). The two main tools (besides watching the network tab in the browser) were <a href="https://newrelic.com/">New Relic</a> with deep instrumentation into our Domino server's JVM and <a href="http://jamonapi.sourceforge.net/">JAMon</a> collecting live statistics (which you can query on the Domino console using <code>show stats vop</code>. Making measurements a default practise during code development makes your life much easier later on</li>
  259. <li><b>No Work without ticket</b><br />
  260. That might be the hardest part to implement. Any code item needs to be related to a ticket. For the search component we used Github Enterprise, pimped up with
  261. <a href="https://www.zenhub.com/">Zenhub</a>.<br />
  262. A <b>very</b> typical flow is: someone (analyst, scrum master, offering manager, project architect, etc.) "owns" the ticket system and tickets flow down. Sounds awfully like <a href="http://www.waterfall2006.com/">waterfall</a> (and it is). Breaking free from this and turn to "the tickets are created by the developers and are the actual standup" greatly improves team velocity. This doesn't preclude creation of tickets by others, to fill a backlog or create and extend user stories. Look for the middle ground.<br />
  263. We managed to get Github tickets to <a href="http://marketplace.eclipse.org/content/github-mylyn-connector">work with Eclipse</a> which made it easy to create tickets on the fly. Once you are there you can visualize progress using <a href="http://www.wissel.net/blog/d6plinks/SHWL-8CLNBW">Burn charts</a>
  264. </li>
  265. <li><b>Agile</b><br />
  266. "Standup meeting every morning 9:30, no exception" - isn't agile. That's process strangling velocity. Spend some time to rediscover <a href="http://alistair.cockburn.us/Rediscovering+the+Heart+of+Agile">the heart of Agile</a> and implement that.<br />
  267. Typical traps to avoid:
  268. <ul>
  269. <li>use ticket (closings) as (sole) metric. It only discourages the us of the ticket system as ongoing documentation</li>
  270. <li>insist on process over collaboration. A "standup meeting" could be just a Slack channel for most of the time. No need to spend time every day in a call or meeting, especially when the team is large</li>
  271. <li>Code is final - it's not. Refactoring is part of the package - including refactoring the various tests</li>
  272. <li>Isolate teams. If there isn't a lively exchange of progress, you end up with silo code. Requires mutual team respect</li>
  273. <li>Track "percent complete". This lives on the fallacy of 100% being a fixed value. Track units of work left to do (and expect that to eventually rise during the project)</li>
  274. <li>One way flow. If the people actually writing code can't participate in shaping user stories or create tickets, you have waterfall in disguise</li>
  275. <li>Narrow user definitions and stories: I always cringe at the Scrum template for user stories: "As a ... I want ... because/in order to  ...". There are two fallacies: first it presumes a linear, single actor flow, secondly it only describes what happens if it works. While it's a good start, adopting more complete use cases (the big brother of user stories) helps to keep the stories consistent. Go learn about <a href="http://amzn.to/2iCBDtM">Writing Effective Use Cases</a>. The agile twist: A use case doesn't have to be complete to get started. Adjust and complete it as it evolves. Another little trap: The "users" in the user stories need to include: infrastructure managers, db admins, code maintainer, software testers etc. Basically anybody touching the app, not just final (business) users</li>
  276. <li>No code reviews: looking at each other's code increases coherence in code style and accellerates bug squashing. Don't fall for the trap: productivity drops by 50% if 2 people stare at one screen - just the opposite happens</li>
  277. </ul>
  278. </li>
  279. <li><b>Big screens</b><br />
  280. While co-located we <strike>squatted in</strike> booked conference rooms with whiteboard, postit walls and projectors. Some of the most efficient working hours were two or three pairs of eyes walking through code, both in source and debug mode. During quiet time (developers need ample of that. The <a href="http://amzn.to/2iCLMXc">Bose solution</a> isn't enough), 27" or more inches of screen real estate boost productivity. At my home office I run a dual screen setup with more than one machine running (However, I have to admit: some of the code was written perched into a cattle class seat travelling between Singapore and the US)</li>
  281. <li><b>Automate</b><br />
  282. We used both <a href="https://jenkins.io/" title="Jenkins is the tool of choice for the core Verse team">Jenkins</a> and <a href="https://travis-ci.org/" title="ISSC uses Travis, mostly because it also can build on macOS">Travis</a> as our automation platform. The project used <a href="http://maven.apache.org/">Maven</a> to keep the project together. While Maven <a href="https://en.wikipedia.org/wiki/The_Moon_Is_a_Harsh_Mistress">is a harsh mistress</a> spending time to provide all automation targets proved invaluable.<br />
  283. You have to configure your test regime carefully. Unit test should not only run on the CI environment, but on a developers workstation - for the code (s)he touches. A full integration test for VoP on the other hand, runs for a couple of hours. That's the task better left to the CI environment. Our Maven tasks included generating the (internal) website and the JavaDoc.<br />
  284. Lesson learned: setting up a full CI environment is quite a task. Getting the repeatable datasets in place (especially when you have time sensitive tests like "provide emails from the last hour") can be tricky. Lesson 2: you will need more compute than expected, plan for parallel testing
  285. </li>
  286. <li><b>Ownership</b><br />
  287. David owned performance, Michael the build process, Raj the Query Parser, Christopher the test automation and myself the query strategy and core classes. It didn't mean: being the (l)only coder, but feeling responsible and taking the lead in the specific module. With the sense of ownership at the code level, we experienced a number of refactoring exercises, to the benefit of the result, that would never have happened if we followed <a href="https://www.youtube.com/watch?v=qYodWEKCuGg">Code Monkey</a> style an analyst's or architect's blueprint.
  288. </li>
  289. </ul>
  290. As usual YMMV ]]></content:encoded>
  291. <pubDate>Tue, 03 Jan 2017 01:48:36 +0000</pubDate>
  292. <slash:comments>3</slash:comments>
  293. <category>IBM Notes</category>
  294. <category>Software</category>
  295. <category>Continuous integration</category>
  296. </item>
  297. <item>
  298. <title>The totally inofficial guide to Verse on Premises</title>
  299. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-AH22CV?Open</link>
  300. <description><![CDATA[ Now that CNGD8ML is upon us, it is story time. Read about the why, who, what and what to watch out for.
  301.    
  302.    To successfully deploy Verse, make sure to carefully read and implement the installation instructions. The availability of Verse makes Domino the ... ]]></description>
  303. <dc:creator>Stephan H Wissel</dc:creator>
  304. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-AH22CV?Open</comments>
  305. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-AH22CV?Open</guid>
  306. <content:encoded><![CDATA[ <p>Now that <a href="http://www-01.ibm.com/support/docview.wss?uid=swg24043176">CNGD8ML</a> is upon us, it is story time. Read about the why, who, what and what to watch out for.</p>
  307.    <p></p>
  308.    <p>To successfully deploy Verse, make sure to carefully read and implement <a href="https://www.ibm.com/support/knowledgecenter/SS4RQV_1.0.0/admin/topics/vop_configuring_server.html"><b>the installation instructions</b></a>. The availability of Verse makes Domino the most versatile eMail platform around, offering you the choice of: Notes Client, Outlook, POP2, IMAP4, iNotes, Verse, iOS, Android. Anywhay, here we go:</p>
  309.    <h2>The back story</h2>
  310.    <p>Verse on premises was a long (out)standing promise to the IBM customer base. Not everybody is ready to embrace the cloud, but interested in <a href="https://twitter.com/search?f=tweets&vertical=default&q=%23newwaytowork&src=typd">the new way to work</a>. In SmartCloud Notes, the backend for Verse in the Cloud, all search is powered by <a href="http://lucene.apache.org/solr/">Apache SOLR</a>. If Verse got delivered as is, that would have required substantial hardware and skill investments for the on-premises customers.</p>
  311.    <p> So I made a bet with <a href="https://www.linkedin.com/in/michaelguyalexander">Michael Alexander</a>, whom I worked with on TPTSNBN, that we could use standard Domino capabilities, not requiring Solr. Based on prototypes with <a href="http://vertx.io/">vert.x</a> and Java8 we gained confidence and got the go ahead to build the search component as OSGi plug-in (in Java6). So the search part (not the UI or other functionality) is on me.</p>
  312.    <h2>The team(s)</h2>
  313.    <p>There were two distinct teams working on the delivery of Verse on Premises (VoP): The core Verse team, that owns UI, functionality and features for both cloud and on premises and the search plugin team responsible to replace the Solr capabilities with native Domino calls. <br />The former is rather large, distributed between the US, Ireland and China. The later was led by the distinguished engineer <a href="https://www.linkedin.com/in/david-byrd-401668a">David Byrd</a> and just a few core coding members: David, Michael, <a href="https://www.linkedin.com/in/cheltzel">Christopher</a>, <a href="https://sg.linkedin.com/in/raj-j-rajendran-226a9456">Raj</a> and myself.<br /> We were supported by a team of testers in Belarus and the Philippines. The test teams wrote hundreds of <a href="http://junit.org/">JUnit</a> and
  314.        <a href="https://www.getpostman.com/">Postman</a> tests, just for the search API.</p>
  315.    <h2>The Orangebox</h2>
  316.    <p>Each project needs a good <a href="#" title="A nice one: TPTSNBN = The Project That Shall Not Be Named">code name</a>. The original Verse code name was <a href="https://en.wikipedia.org/wiki/Sequoia_National_Park ">Sequoia</a>, which is reflected in the name of the plugins for core and UI functionality.</p>
  317.    <p>The search component, not being part of RealVerse&trade;, needed a different name. In an initial high level diagram, outlining the architecture for management, the search component was drawn as an orange box. Since we "just" had to code "the orange box". The name stuck and led to our code name "Project OrangeBox" (PoB).
  318.        <br /><img src="/blog/Images/SHWL-AH22Z2/$File/OrangeBox.png" border="0 " alt="The inofficial Project Orange Box Logo" />
  319.        <br /> You <a href="http://jd.benow.ca/ " title="Don 't do this, it is most likely illegal" rel="nofollow">can find</a> Orangebox and POB in multiple places (including notes.ini variables and https calls the browser makes).  So now you know where it is coming from.</p> ]]></content:encoded>
  320. <pubDate>Fri, 30 Dec 2016 12:17:00 +0000</pubDate>
  321. <slash:comments>15</slash:comments>
  322. <category>IBM Notes</category>
  323. </item>
  324. <item>
  325. <title>Domino meets RXJava</title>
  326. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-ADR8JM?Open</link>
  327. <description><![CDATA[ Verse on premises (VoP) is nearing its second beta release and fellow Notes experts are wondering if they need to install Apache Solr as part of the VoP deployment. There was a lengthy, high quality discussion and quite some effort evaluating alternatives. In ... ]]></description>
  328. <dc:creator>Stephan H Wissel</dc:creator>
  329. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-ADR8JM?Open</comments>
  330. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-ADR8JM?Open</guid>
  331. <content:encoded><![CDATA[ Verse on premises (VoP) is nearing its second beta release and fellow Notes experts are wondering if they need to install <a href="http://lucene.apache.org/solr/">Apache Solr</a> as part of the VoP deployment. There was a lengthy, high quality discussion and quite some effort evaluating alternatives. In conclusion it was decided to deliver the subset of Solr capabilities needed for VoP as series of OSGi plugins to the Domino server. The project was formed out of the experience with ProjectCastle, which continues as Project OrangeBox to deliver these plugins. In VoP you might encounter one or the other reference to PoB, so now you know where it comes from.<br />
  332. One of the design challenges to solve was to emulate the facet results of the Solr search engine. I build some prototypes and finally settled on the use of <a href="https://github.com/ReactiveX/RxJava">RxJava</a>.<br />
  333. RxJava is a member of the <a href="http://reactivex.io/">ReactiveX</a> programming family, which is designed around the Observer pattern, iterators and functional programming. Check out the <a href="http://reactivex.io/">main site</a> to get into the groove.<br />
  334. The task at hand is to convert something Domino (a ViewNavigator, a DocumentCollection or a Document) into something that emits subscribable events. I started with turning a document into an NotesItem emitter. Purpose of this was the creation of lighweight Java objects that contain the items I'm interested in. Since Domino's Java has <a href="http://www-01.ibm.com/support/docview.wss?uid=swg21097861">special needs</a> and I couldn't use the <a href="https://github.com/OpenNTF/org.openntf.domino">ODA</a>, special precaution was needed.<br />
  335. There are <a href="https://github.com/ReactiveX/RxJava/wiki/Creating-Observables">plenty of methods</a> to create an Observable and on first look <a href="http://reactivex.io/documentation/operators/create.html">Create</a> looks most promising, but it left the question of recycling open. Luckily there is the <a href="http://reactivex.io/documentation/operators/using.html">Using</a> method that creates a companion object that lives along the Observable and gets explicitly called when the Observable is done. To create the NotesItem emitting Observable I settled on the <a href="http://reactivex.io/documentation/operators/from.html">From</a> method with an <a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html">Iterable</a> as source. The moving parts I had to create were <code>class DocumentSource implements Iterable&lt;Item&gt;</code> and  <code>class ItemIterator implements Iterator&lt;Item&gt;</code><br />
  336. Why Reactive? In a nutshell: a source emits data and any number of subscribers can subscribe to. Between the emission and subscription any number of filters, modifiers and aggregators can manipulate the data emitted. Since each of them lives in its own little class, testing and composition become very easy. Let's look at an example:<br />
  337. <code>docSource.getItemStream(session).filter(nameFilter).map(toPobItem).map(nameMapper).subscribe(new ItemAdder());</code><br />
  338. You almost can read this aloud: "<i>The source emits a stream of items, they get filtered by Name, then converted into another Java object (PobItem) and renamed before added to the subscriber.</i>". In a different case you might want to collect all entities (users, groups, roles) that have access to a document, you migh create a "readerAuthorFilter". The individual classes are very easy to test. E.g. the Name filter looks like this:<br />
  339. <pre class="brush: java">
  340. // requiredFields is is a Collection&lt;String&gt; of NotesItem names to include or exclude
  341. ItemNameFilter nameFilter = new ItemNameFilter(requiredFields, ItemNameFilter.FilterMode.INCLUDE);
  342.  
  343. public class ItemNameFilter implements Func1&lt;Item, Boolean&gt; {
  344.  
  345.    public enum FilterMode {
  346.        INCLUDE, EXCLUDE;
  347.    }
  348.  
  349.    private final Logger      logger      = Logger.getLogger(this.getClass().getName());
  350.    private final Set&lt;String&gt; itemNameSet = new HashSet&lt;String&gt;();
  351.    private final FilterMode  filterMode;
  352.  
  353.    /**
  354.     * Flexible include or exclude
  355.     *
  356.     * @param itemNames
  357.     *            Collection of Names to include or exclude
  358.     * @param filterMode
  359.     *            INCLUDE or EXCLUDE
  360.     */
  361.    public ItemNameFilter(Collection&lt;String&gt; itemNames, FilterMode filterMode) {
  362.        this.filterMode = filterMode;
  363.        this.updateItemNames(itemNames);
  364.    }
  365.  
  366.    public ItemNameFilter(Collection&lt;String&gt; itemNames) {
  367.        this.filterMode = FilterMode.INCLUDE;
  368.        this.updateItemNames(itemNames);
  369.    }
  370.  
  371.    private void updateItemNames(Collection&lt;String&gt; itemNames) {
  372.        this.itemNameSet.addAll(itemNames);
  373.    }
  374.  
  375.    @Override
  376.    public Boolean call(Item incomingItem) {
  377.        // Include unless proven otherwise
  378.        boolean result = true;
  379.        try {
  380.            String itemName = incomingItem.getName();
  381.            boolean inList = this.itemNameSet.contains(itemName.toLowerCase());
  382.            result = (inList && this.filterMode.equals(FilterMode.INCLUDE));
  383.        } catch (NotesException e) {
  384.            this.logger.log(Level.SEVERE, e);
  385.            result = false;
  386.        }
  387.        return result;
  388.    }
  389. }
  390. </pre><br />  ]]></content:encoded>
  391. <pubDate>Tue, 13 Sep 2016 05:33:43 +0000</pubDate>
  392. <slash:comments>2</slash:comments>
  393. <category>IBM Notes</category>
  394. </item>
  395. <item>
  396. <title>Metawork, nobody is capable but all participate grudgingly</title>
  397. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-A9VVSG?Open</link>
  398. <description><![CDATA[ This article is a translation/paraphrase of Professor Gunter Dueck's original post titled DD265: Metawork – keiner kann’s, aber alle machen ärgerlich mit (Mai 2016). Professor Dueck's philosophy resonates with me, so I'd like to make his thoughts available to ... ]]></description>
  399. <dc:creator>Stephan H Wissel</dc:creator>
  400. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-A9VVSG?Open</comments>
  401. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-A9VVSG?Open</guid>
  402. <content:encoded><![CDATA[ This article is a translation/paraphrase of Professor Gunter Dueck's original post titled <a href="http://www.omnisophie.com/dd265-metawork-keiner-kanns-aber-alle-machen-aergerlich-mit-mai-2016/">DD265: Metawork – keiner kann’s, aber alle machen ärgerlich mit (Mai 2016)</a>. Professor Dueck's <a href="/blog/d6plinks/SHWL-8MGKRK">philosophy</a> resonates with me, so I'd like to make his thoughts available to a wider audience. Bear with my Gerlish. Remarks in brackets aren't part of the original text and are either my comment, extension or explanation. Here we go:<br /><br />
  403. Metawork is your own effort  to organize work (your's and other's), not performing the actual effort. It is about coordinating your contributions, more often than not, across multiple projects. This includes managing decisions (through eMail) and  the communicate with all stakeholders. E.g. you can use efficient (Dueck used the word "fertile", but I'm not sure if that has the same resonance in English) meetings to establish the approach how to structure and execute working together. Over time a corporate culture emerges where a common good metawork forms the enabler for efficient execution of the core work (we'll learn another term for this
  404. just below).<br /><br />
  405.  
  406. In reality, however, there are quarrels in meetings, about who does what. Conflicts surface, everyone speaks their minds unfiltered, meetings drag on and on. People get a grudge and are annoyed and are left with the feeling to have wasted valuable time, they won't get back. Dueck checked the web, what it has to say about metawork. His favorite place is the the <a href="http://www.urbandictionary.com/define.php?term=metawork">Urban Dictionary</a>  where ordinary people contribute to difficult definitions and provide lots of suggestions. The best of them are the odd ones.<br /><br />
  407.  
  408. You rant online to be overburdened with unproductive responsibilities, unable to get anything done. People share that in a development project staffed with eight people only two of them code. The rest warms seats in the meeting room and is first in line for promotion if the project is successful. What a mess!<br /><br />
  409.  
  410. Hmmm. So your own work is productive, anything else a distraction. Not thinking about what your project members see as their "productive work". An example: If the developers miss a deadline, it generates a lot of distraction for the rest of the team. "Everything would be perfect if the coders would work properly! We have to integrate into SAP, everybody is waiting. What a cluster f**k (that's the closest cultural equivalent to "Supergau" I could think of)" - The two developers retort: "You could have contributed code, instead of babbling in all that meeting, we would be done by now"<br /><br />
  411.  
  412. This a clear indicator, Dueck sees in all corporations, that the different project members have no understanding of the tasks of their fellow members. If they do know them, they doubt the importance or usefulness. One's own work is important, anything else is a distraction. Others only interrupt. Then they quarrel in in meetings<br /><br />
  413.  
  414. Why oh why? All are well trained for their own tasks and complete them quite well. However roughly none have been educated on metawork: how to get organized and collaborate. They do some of it every day, limping along without having or wanting to learn about it. It never had been a topic. They bitch the whole day about the drag of metawork without being able to fully grasp it, lacking a word for it, not aware of the term metawork. Managers and project leaders follow the prevalent methodologies and press forward. More often than not, they aren't aware of metawork. The manage or lead as "their own work", but hardly spend a thought on the work as a whole<br /><br />
  415.  
  416. Even when managers would know how to coordinate well and fuse the parts to a whole, how to deal with unknowns and avoid conflict - it falls short when their reports have no clue what is metawork?<br /><br />
  417.  
  418. When team members only spend half of their time with "their own work (e.g. programming)" and  are irate about the "stolen" time spend in meetings otherwise, they haven't understood the very nature of work - or metawork is done mind-boggling bad.<br /><br />
  419.  
  420. Metawork is about the principles and foundation of performing work. Those who haven't given it a thought, bungle in each project, wondering how it could work. Every conflict is new, different and unique. Each project has its own singular surprises. What a madhouse! Lots of literature reenforces that point of view.<br />
  421. However that's because one only focuses on their own tasks at hand, and never learn to pay respect of the significant other contributions.<br /><br />
  422.  
  423. Dueck suggested in his book „Verständigung im Turm zu Babel“ (Communicate in the tower of Babel) and his blog to contrast meta communication and mesa communication. „Mesa“ is greek meaning „inside“, „meta“ is like „and beyond“. In the context of work  „mesawork“ would be the individual task at hand and „metawork“ anything beyond that. Dueck sees it over and over again: Nobody is really good at meta communication, anybody communicates off their chests. Similarly we are good at mesawork but bemoan the complexity of the world, since we can't relate to metawork.<br /><br />
  424.  
  425. Shall we leave it that way? Half of our time being experts, half of it clueless N00bs? Isn't the balance tipping towards cluelessness, since the need for metawork is raising in a increasingly complex world? How about you? Happy to continue only fretting? ]]></content:encoded>
  426. <pubDate>Thu, 12 May 2016 23:41:28 +0000</pubDate>
  427. <slash:comments>9</slash:comments>
  428. <category>After hours</category>
  429. </item>
  430. <item>
  431. <title>Mach Dich auf die Socken!</title>
  432. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-A9SKBY?Open</link>
  433. <description><![CDATA[ A common requirement in corporate systems is "let me know when something is going on". In Notes we use "On document creation or update" triggered agents to process such events. To let external systems know about such a change R8 introduced the web service ... ]]></description>
  434. <dc:creator>Stephan H Wissel</dc:creator>
  435. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-A9SKBY?Open</comments>
  436. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-A9SKBY?Open</guid>
  437. <content:encoded><![CDATA[ A common requirement in corporate systems is "let me know when something is going on". In Notes we use "On document creation or update" triggered agents to process such events. To let external systems know about such a change R8 introduced the web service client. This works well in distributed system, but requires quite some work on both ends. In a recent case I had to optimize the communication between Domino and a task running on the same machine. The existing solution was polling the Domino API in short intervals for updates. Something I would call <a href="https://www.youtube.com/watch?v=basofea2UEs">donkey mode</a>. Sockets to the rescue. A few lines of Java in a triggered agent puts an end to donkey mode and provides the receiving end with all it needs in time:<br />
  438. <pre 'brush: java'>
  439. import java.io.BufferedReader;
  440. import java.io.DataOutputStream;
  441. import java.io.IOException;
  442. import java.io.InputStreamReader;
  443. import java.net.Socket;
  444.  
  445. import lotus.domino.AgentBase;
  446. import lotus.domino.AgentContext;
  447. import lotus.domino.Database;
  448. import lotus.domino.Document;
  449. import lotus.domino.DocumentCollection;
  450. import lotus.domino.NotesException;
  451. import lotus.domino.Session;
  452.  
  453. import com.issc.castle.domino.Utils;
  454.  
  455. public class JavaAgent extends AgentBase {
  456.  
  457. public static String sockethost = "127.0.0.1";
  458. public static int socketport = 1234;
  459.  
  460. public void NotesMain() {
  461. Session session = null;
  462. AgentContext agentContext = null;
  463. Database db = null;
  464. DocumentCollection dc = null;
  465. Document doc = null;
  466.  
  467. // The socket elements
  468. DataOutputStream out = null;
  469. BufferedReader in = null;
  470. Socket socketClient = null;
  471. try {
  472. // Get the Notes parts
  473. session = getSession();
  474. agentContext = session.getAgentContext();
  475. db = agentContext.getCurrentDatabase();
  476. dc = agentContext.getUnprocessedDocuments();
  477.  
  478. // Get the socket
  479. socketClient = new Socket(sockethost, socketport);
  480. in = new BufferedReader(new InputStreamReader(socketClient.getInputStream()));
  481. out = new DataOutputStream(socketClient.getOutputStream());
  482.  
  483. doc = dc.getFirstDocument();
  484. while (doc != null) {
  485. Document nextDoc = dc.getNextDocument(doc);
  486. this.signalOneDocument(doc, in, out);
  487. Utils.shred(doc);
  488. doc = nextDoc;
  489. }
  490.  
  491. // Mark them done
  492. dc.updateAll();
  493. } catch (Exception e) {
  494. e.printStackTrace();
  495. } finally {
  496. Utils.shred(doc, dc, db, agentContext, session);
  497. // Close them
  498. try {
  499. if (out != null) {
  500. out.close();
  501. }
  502. if (in != null) {
  503. in.close();
  504. }
  505. if (socketClient != null) {
  506. socketClient.close();
  507. }
  508. } catch (IOException e) {
  509. e.printStackTrace();
  510. }
  511. }
  512. }
  513.  
  514. private void signalOneDocument(final Document doc, final BufferedReader in, final DataOutputStream out) {
  515. try {
  516. String notesURL = doc.getNotesURL();
  517. out.writeBytes(notesURL);
  518. out.writeBytes("|");
  519. } catch (NotesException e) {
  520. e.printStackTrace();
  521. } catch (IOException e) {
  522. e.printStackTrace();
  523. }
  524.  
  525. }
  526. }
  527. </pre><br />
  528. No libraries to load, the only utility function used is <code>Utils.shred()</code> which is a error wrapped recycle call.
  529. <br /> As usual YMMV<br />
  530. (Bad German pun in the title) ]]></content:encoded>
  531. <pubDate>Mon, 09 May 2016 14:46:15 +0000</pubDate>
  532. <slash:comments>0</slash:comments>
  533. <category>IBM Notes</category>
  534. <category>Java</category>
  535. </item>
  536. <item>
  537. <title>Annotations to supercharge your vert.x development</title>
  538. <link>https://stephan.wissel.net/blog/d6plinks/SHWL-A8M22R?Open</link>
  539. <description><![CDATA[ ProjectCastle is well under way. Part of it, the part talking to Domino, is written in Java8 and vert.x. With some prior experience in node.js development vert.x will look familiar: base on event loop and callbacks, you develop in a very similar way. The big ... ]]></description>
  540. <dc:creator>Stephan H Wissel</dc:creator>
  541. <comments>https://stephan.wissel.net/blog/d6plinks/SHWL-A8M22R?Open</comments>
  542. <guid isPermaLink="true">https://stephan.wissel.net/blog/d6plinks/SHWL-A8M22R?Open</guid>
  543. <content:encoded><![CDATA[ ProjectCastle is well under way. Part of it, the part talking to Domino, is written in Java8 and <a href="http://vertx.io">vert.x</a>. With some prior experience in node.js development vert.x will look familiar: base on event loop and callbacks, you develop in a very similar way. The big differences: vert.x runs on the JVM8, it is by nature of the JVM multi-threaded, features an event bus and is polyglot - you can develop in a mix of languages: Java, JavaScript, Jython, Groovy etc.<br />
  544. This post reflects some of the approaches I found useful developing with vert.x in Java. There are 3 components which are core to vert.x development:
  545. <ul>
  546.   <li><h4>Verticle</h4>
  547. A unit of compute running with an event loop. Usually you start one Verticle (optional with multiple instances) as your application, but you might want/need to start additional ones for longer running tasks. A special version is the worker verticle, that runs from a thread pool to allow execution of blocking operations</li>
  548.   <li><h4>EventBus</h4>
  549. The different components of your application message each other via the EventBus. Data send over the EventBus can be a String, a JsonObject or a buffer. You also can send any arbitrary Java class as message once you have defined a codec for it
  550. </li>
  551.   <li><h4>Route</h4>
  552. Like in node.js a vert.x web application can register routes and their handlers to react on web input under various conditions. Routes can be defined using URLs, HTTP Verbs, Content-Types ( for POST/PUT/PATCH operations)</li>
  553. </ul>
  554. Ideally when defining a route and a handler, a verticle or a potential message for the EventBus, all necessary code stays contained in the respective source code file. The challenge here is to register the components when the application starts. Your main Verticle doesn't know what components are in your application and manually maintain a loader code is a pain to keep in sync (besides leading to merge conflicts when working in a team).<br />
  555. Java annotations to the rescue! If you are new to annotations, go and check out <a href="">this tutorial</a> to get up to speed. For my project I defined three of them, with one being able to be applied multiple times.
  556. <h3>CastleRequest</h3>
  557. A class annotated with CastleRequest registers its handler with the EventBus, so the class can be sent over the EventBus and get encoded/decode appropriately. A special value for the annotation is "self" which indicates, that the class itself implements the <a href="http://vertx.io/docs/apidocs/io/vertx/core/eventbus/MessageCodec.html">MessageCodec</a> interface<br />
  558. <pre 'brush: java'>
  559. @Documented
  560. @Retention(RetentionPolicy.RUNTIME)
  561. @Target({ElementType.TYPE})
  562. public @interface CastleRequest {
  563.  // We use value to ease the syntax
  564.  // to @CastleRequest(NameOfCodec)
  565.  // Special value: self = class implements the MessageCodec interface
  566.  String value();
  567. }
  568. </pre>
  569. <h3>CastleRoute</h3>
  570. This annotation can be assigned multiple times, so 2 annotation interfaces are needed<br />
  571. <pre 'brush: java'>
  572. @Documented
  573. @Repeatable(CastleRoutes.class)
  574. @Retention(RetentionPolicy.RUNTIME)
  575. @Target({ElementType.TYPE})
  576. public @interface CastleRoute {
  577.  String route();
  578.  String description();
  579.  String mimetype() default "any";
  580.  String method() default "any";
  581. }
  582. </pre><br />
  583. and the repeatability annotation (new with Java8):<br />
  584. <pre 'brush: java'>
  585. @Documented
  586. @Retention(RetentionPolicy.RUNTIME)
  587. @Target({ElementType.TYPE})
  588. public @interface CastleRoutes {
  589.  CastleRoute[] value();
  590. }
  591. </pre>
  592. <h3>CastleVerticle</h3>
  593. Classes marked with this annotation are loaded as verticles. They can implement listeners to the whole spectrum of vert.x listening capabilities<br />
  594. <pre 'brush: java'>
  595. @Documented
  596. @Retention(RetentionPolicy.RUNTIME)
  597. @Target({ElementType.TYPE})
  598. public @interface CastleVerticle {
  599.  String type() default "worker";
  600.  int instances() default 0;
  601.  boolean multithreaded() default false;
  602. }
  603. </pre> ]]></content:encoded>
  604. <pubDate>Sat, 02 Apr 2016 00:01:06 +0000</pubDate>
  605. <slash:comments>0</slash:comments>
  606. <category>vert.x</category>
  607. </item>
  608. </channel>
  609. </rss>
  610.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//feeds.feedburner.com/WisselNet

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda