<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>David Codes</title>
    <description>My thoughts and rants on development and IT subjects
</description>
    <link>https://david-codes.hatanian.com/</link>
    <atom:link href="https://david-codes.hatanian.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 02 Feb 2026 18:51:35 +0000</pubDate>
    <lastBuildDate>Mon, 02 Feb 2026 18:51:35 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>AWS costs every programmer should know</title>
        <description>&lt;p&gt;The title for this blog post is a direct reference to &lt;a href=&quot;https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html&quot;&gt;Latency Numbers Every Programmer Should Know&lt;/a&gt;. There are several versions of those numbers available now, and I could not find the original author with certainty. Some people attribute the original numbers to &lt;a href=&quot;https://twitter.com/jeffdean&quot;&gt;Jeff Dean&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When working on a project that will reach a certain scale, you need to balance several concerns. What assumptions am I making and how do I confirm them? How can I get to market quickly? Will my design support the expected scale?&lt;/p&gt;

&lt;p&gt;One of the issues associated with scale, is the cost of your infrastructure. Cloud providers allow you to provisions thousands of CPUs and store terabytes of data at the snap of a finger. But that comes at a cost, and what is negligible for a few thousand users might become a burning hole in your budget when you reach millions of users.&lt;/p&gt;

&lt;p&gt;In this article, I’m going to list reference numbers I find useful to keep in mind when considering an architecture. Those numbers are not meant for accurate budget estimation. They’re here to help you decide if your design makes sense or is beyond what you could ever afford. So consider the orders of magnitude and relative values rather than the absolute values.&lt;/p&gt;

&lt;p&gt;Also consider that your company may get discounts from AWS, and those can make a massive difference.&lt;/p&gt;

&lt;h1 id=&quot;compute&quot;&gt;Compute&lt;/h1&gt;

&lt;p&gt;What’s the cost of a CPU these days? I used the wonderful &lt;a href=&quot;https://www.ec2instances.info/&quot;&gt;ec2instances.info&lt;/a&gt; interface to extract the median price of a vCPU.&lt;/p&gt;

&lt;p&gt;You can get the source data out of &lt;a href=&quot;https://github.com/powdahound/ec2instances.info/blob/master/www/instances.json&quot;&gt;their Github repo&lt;/a&gt;. I copied it and processed it using a python script &lt;a href=&quot;https://github.com/dhatanian/aws-ec2-costs&quot;&gt;that you can find on Github&lt;/a&gt;. All prices are for the eu-west-1 region.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Median monthly cost&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1 modern vCPU (4 AWS ECUs)&lt;/td&gt;
      &lt;td&gt;58 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;With 1 year convertible reservation (all up front)&lt;/td&gt;
      &lt;td&gt;43 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;With 3 years convertible reservation (all up front)&lt;/td&gt;
      &lt;td&gt;30 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;With spot pricing (estimated)&lt;/td&gt;
      &lt;td&gt;30 $/month&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;
I estimated spot pricing based on anecdotal data I got from various sources. As the prices vary within a day and I could not find a reliable data source for it.&lt;/p&gt;

&lt;p&gt;AWS represents the computing power of its machines in Elastic Compute Units, and 4 ECUs represent more or less the power of a modern CPU. So the prices above are for one CPU or core, not one instance.&lt;/p&gt;

&lt;p&gt;Here’s the price of 1 ECU in $ per hour across all instance types I looked at:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2019-06-09-aws-costs-every-programmer-should-now.md/ecu-memory-cost.png&quot; alt=&quot;Price of 1 ECU in $ per hour&quot; style=&quot;margin:auto; display:block;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And here’s how on-demand compares with 1 year and 3 year reservations (both convertible, upfront payments):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2019-06-09-aws-costs-every-programmer-should-now.md/ondemand-vs-reserved.png&quot; alt=&quot;How many more reserved instances can you pay for the same price as on-demand&quot; style=&quot;margin:auto; display:block;&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;storage&quot;&gt;Storage&lt;/h1&gt;

&lt;p&gt;So you want low latency, high throughput and are planning to store everything in Redis? Then on top of those CPU costs, you’ll need to pay for RAM.&lt;/p&gt;

&lt;p&gt;I used the same approach to extract the median price of 1GB of RAM on EC2. Elasticache is more or less twice as expensive for on-demand but prices drop quite quickly when looking at reserved instances.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Median monthly cost&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1 GB RAM&lt;/td&gt;
      &lt;td&gt;10 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1 GB RAM 1 year convertible reservation (all up front)&lt;/td&gt;
      &lt;td&gt;8 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1 GB RAM 3 years convertible reservation (all up front)&lt;/td&gt;
      &lt;td&gt;5 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SSD&lt;/td&gt;
      &lt;td&gt;0.11 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hard Disk&lt;/td&gt;
      &lt;td&gt;0.05 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S3&lt;/td&gt;
      &lt;td&gt;0.02 $/month&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S3 Glacier&lt;/td&gt;
      &lt;td&gt;0.004 $/month&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;While this is the pure storage cost, you also need to look at the usage patterns for your data. How much CPU will you need to run that in-memory database 24/7?&lt;/p&gt;

&lt;p&gt;Same for S3: how much will you pay in writing/reading requests? I’ve seen workloads where the storage cost on S3 was negligible but the cost of writing a lot of objects in S3 made the team write their own filesystem on top of S3.&lt;/p&gt;

&lt;h1 id=&quot;bandwidth&quot;&gt;Bandwidth&lt;/h1&gt;

&lt;p&gt;A few &lt;a href=&quot;https://news.ycombinator.com/item?id=20138409&quot;&gt;comments on HackerNews&lt;/a&gt; pointed out that I left the bandwidth costs out. Indeed if you are serving data to end users, or need cross-region replication, you need to look into those costs as well.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Type of data transfer&lt;/th&gt;
      &lt;th&gt;Cost of transferring 1GB&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;EU/US region to any other region&lt;/td&gt;
      &lt;td&gt;0.02 $/GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;APAC region to any other region&lt;/td&gt;
      &lt;td&gt;0.09 $/GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;EU/US region to Internet&lt;/td&gt;
      &lt;td&gt;0.05 $/GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;APAC region to Internet&lt;/td&gt;
      &lt;td&gt;0.08 $/GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Between two AZs in the same region&lt;/td&gt;
      &lt;td&gt;0.01 $/GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Inside the same AZ&lt;/td&gt;
      &lt;td&gt;Free&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Sun, 09 Jun 2019 00:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2019/06/09/aws-costs-every-programmer-should-now.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2019/06/09/aws-costs-every-programmer-should-now.html</guid>
        
        
      </item>
    
      <item>
        <title>Advent of Code 2018</title>
        <description>&lt;p&gt;As a kid, my parents would buy &lt;a href=&quot;https://en.wikipedia.org/wiki/Advent_calendar&quot;&gt;advent calendars&lt;/a&gt; for my siblings and I before Christmas. If you have never seen one, an advent calendar counts down the days from December 1st to Christmas day or Christmas eve.&lt;/p&gt;

&lt;p&gt;A typical advent calendar would countain a chocolate per day (bear with me, we’ll be talking about code soon). Although these days you could get anything in those calendars: perfume, protein balls, cereal bars…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2018-12-19-advent-of-code.md/adventcalendar.jpg&quot; alt=&quot;An example of an advent calendar, courtesy of Wikimedia&quot; style=&quot;margin:auto; display:block;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This year in addition to that typical advent calendar, I am taking part in &lt;a href=&quot;https://adventofcode.com&quot;&gt;Advent Of Code&lt;/a&gt;. Created and run by &lt;a href=&quot;http://was.tl/&quot;&gt;Eric Wastl&lt;/a&gt;, Advent of Code has been running since 2015. The website offers a new coding problem each day over the 25 days from December 1st to Christmas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2018-12-19-advent-of-code.md/adventofcode.png&quot; alt=&quot;The Advent Of Code man page, listing the riddles you&apos;ve already solved and the upcoming ones&quot; style=&quot;margin:auto; display:block;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Each problem follows the same general structure :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;a description of the problem, with some background story (usually around helping Santa’s elves to save Christmas)&lt;/li&gt;
  &lt;li&gt;some examples of input data and expected result&lt;/li&gt;
  &lt;li&gt;an input file that needs to be parsed and processed to get the answer&lt;/li&gt;
  &lt;li&gt;a second part for the problem, that you can only access when you’ve solved the first part. Very often, the second part looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&apos;s make the input data 100 times bigger&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what a problem looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Below the message, the device shows a sequence of changes in frequency (your puzzle input).
 A value like +6 means the current frequency increases by 6;
 a value like -3 means the current frequency decreases by 3.

For example, if the device displays frequency changes of +1, -2, +3, +1, 
then starting from a frequency of zero, the following changes would occur:

Current frequency  0, change of +1; resulting frequency  1.
Current frequency  1, change of -2; resulting frequency -1.
Current frequency -1, change of +3; resulting frequency  2.
Current frequency  2, change of +1; resulting frequency  3.

In this example, the resulting frequency is 3.

Here are other example situations:

+1, +1, +1 results in  3
+1, +1, -2 results in  0
-1, -2, -3 results in -6

Starting with a frequency of zero, what is the resulting frequency 
after all of the changes in frequency have been applied?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the &lt;a href=&quot;https://adventofcode.com/2018/day/1&quot;&gt;first problem&lt;/a&gt; of the advent of code 2018, and also the easiest. Subsequent problems get harder.&lt;/p&gt;

&lt;p&gt;The input data is &lt;strong&gt;different for each participant&lt;/strong&gt;. This amazes me. Eric had to come up with an original problem, and then write a generator to provide different input to each participant (70 000 people solved the first problem)!&lt;/p&gt;

&lt;p&gt;Clearly Advent of Code is a successful formula. To the point that Eric had to ask people to &lt;a href=&quot;https://www.reddit.com/r/adventofcode/comments/a1qovy/please_dont_spam_requests_to_aoc/&quot;&gt;stop sending spammy requests to track their leaderboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Personally I like it because it allows me to play with technologies that I don’t get to use every day. This year I tried to solve it in Scala, although I might finish it in Go. It’s also a good way to review concepts such as algorithmic complexity, breadth-first versus depth-first or &lt;a href=&quot;https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm&quot;&gt;Dijkstra’s algorithm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reading other people’s point of views on Reddit or on my company Slack, we all have different reasons to take part, and enjoy different types of problems. Some of my colleagues enjoy the parsing of the input file, while like me enjoy when the problem challenges me to think about algorithmic complexity and find clever ways to solve issues with large datasets.&lt;/p&gt;

&lt;p&gt;If you’re bored during the winter holidays, or want to try out a new language, do check out Advent of Code. And if you’re curious, &lt;a href=&quot;https://github.com/dhatanian/adventofcode&quot;&gt;my solutions to the problems is on Github&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Wed, 19 Dec 2018 20:38:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2018/12/19/advent-of-code.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2018/12/19/advent-of-code.html</guid>
        
        
      </item>
    
      <item>
        <title>Postmortems: failure done right</title>
        <description>&lt;p&gt;A postmortem is a document in which a team reflects on a recent outage and think of ways to prevent it from happening again. After an outage or security breach, the team gathers and writes a document discussing the event and what they’ve learnt from it.&lt;/p&gt;

&lt;p&gt;I am fascinated by these documents and read any postmortem that I encounter. They are a great way for a team to reflect on their practices and improve upon them. Because postmortems address the failure of a system managed by the team, writing them is also an exercise in humility.&lt;/p&gt;

&lt;p&gt;Every time I read a postmortem, I think “can something similar happen in my project?”. There is always something to learn from other people’s postmortems. We all have been in situations where we forgot something or made mistakes that we would have never thought of. It is also a good reminder that we are fallible. By reading other postmortems we learn from other people’s mistakes and we reflect on ours.&lt;/p&gt;

&lt;p&gt;Some companies use the name RCA, or Root Cause Analysis for such documents. I prefer the term “postmortem”, because RCA sounds like the work stops at identifying a cause. The value of the process is rather in discussing the solutions and lessons learned rather than focusing on the cause of the issue.&lt;/p&gt;

&lt;h2 id=&quot;how-to-write-a-postmortem-document&quot;&gt;How to write a postmortem document&lt;/h2&gt;

&lt;h3 id=&quot;gather-the-team&quot;&gt;Gather the team&lt;/h3&gt;

&lt;p&gt;After an outage, gather the team in a room to edit the postmortem together. Make sure you include everyone who was involved in detecting the problem and implementing the solution. If there were a lot of teams involved, get at least a representative of each team.&lt;/p&gt;

&lt;p&gt;If you cannot gather everyone together (remote workers, people too busy), you can collaboratively edit a document on Google Docs or something similar.&lt;/p&gt;

&lt;h3 id=&quot;describe-the-outage&quot;&gt;Describe the outage&lt;/h3&gt;

&lt;p&gt;Start with an introduction describing in one paragraph what the issue was, what its impact has been, and when it occurred.&lt;/p&gt;

&lt;p&gt;Then write up a precise timeline of the events. Include any meaningful even such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;An alert was received&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;An action was taken&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;An action was completed&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;An engineer made an important realisation on the problem at hand&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Something was communicated externally&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Impact started/stopped being noticeable by users&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;discuss-causes-and-learnings&quot;&gt;Discuss causes and learnings&lt;/h3&gt;

&lt;p&gt;Once this is done, you have a good basis to discuss causes and learnings. Start by having a look at what went right. Did your escalation process work correctly? Did an engineer follow one of your operations playbooks with success? If so, do mention it here. It is an acknowledgement of how much the team has already accomplished to improve the system’s resiliency.&lt;/p&gt;

&lt;p&gt;Then have a look at what did not go well. Explore each issue, and look for the causes. What made things difficult? Why did this system fail, or why was this alert ignored? There is usually more than one cause, so do not stop at the first one you find. Iterate on those causes to get a better understanding of what caused the problem. Use a technique such as &lt;a href=&quot;https://en.wikipedia.org/wiki/5_Whys&quot;&gt;5 Why’s&lt;/a&gt; to achieve this.&lt;/p&gt;

&lt;h3 id=&quot;agree-on-actions&quot;&gt;Agree on actions&lt;/h3&gt;

&lt;p&gt;Finally, list out all the actions that need to be taken to prevent similar problems from occurring. You will probably want to patch your code to fix the issue you found, but you should consider other actions, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Improving documentation on solving this kind of issue&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Adding an alert to surface an issue earlier&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Improving the incident response process if necessary&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Communicating the postmortem to other teams so that they learn from it&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reviewing the system for similar vulnerabilities that would not have been found yet&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Planning for a repetition of the incident to ensure that the steps taken work&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like any action plan, name an owner for each of the actions, to make sure they’re dealt with. You should also agree on a date to check on those actions’ progress.&lt;/p&gt;

&lt;h2 id=&quot;what-is-a-good-postmortem&quot;&gt;What is a good postmortem?&lt;/h2&gt;

&lt;h3 id=&quot;precise&quot;&gt;Precise&lt;/h3&gt;

&lt;p&gt;The most important quality of a good postmortem is being factual. In particular, the timeline should be as precise as possible and describe all the events from the first symptoms to the resolution. Be concise and stay away from vagueness. Take a statement such as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;the latency increased after deploying the new version&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be replaced with a more precise version, such as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;the 99th percentile latency grew from 300ms to 1.2s within 3 minutes of the deployment, and &lt;strong&gt;stayed&lt;/strong&gt; that way during 12 minutes before going down to the nominal value of 300-400ms&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;blameless&quot;&gt;Blameless&lt;/h3&gt;

&lt;p&gt;Second, a postmortem is blameless. It does not judge, it describes the events and the actions taken to solve it. If a postmortem is not blameless, people cannot be candid about the errors they made. With a blameful postmortem, you will not reach the right conclusion. This is a tricky exercise. By the time the outage is over, people are exhausted. To make sure that a postmortem does not blame anybody, it is better to write it after everyone had a good night’s sleep.&lt;/p&gt;

&lt;h3 id=&quot;facilitated&quot;&gt;Facilitated&lt;/h3&gt;

&lt;p&gt;An external facilitator can be very helpful. They will gather the facts and ask questions to people involved in the outage. This will result in a more factual, blameless postmortem. The worst option is for the manager of the people involved to lead the postmortem meeting! For a lot of people, it is harder to discuss openly a mistake they made in front of the person who assesses their performance and will decide on their salary bump in a couple of months. Postmortem meetings lead by managers will always be less transparent that ones led by independent facilitators.&lt;/p&gt;

&lt;h3 id=&quot;constructive&quot;&gt;Constructive&lt;/h3&gt;

&lt;p&gt;Postmortems and their quality are a reflection of your team and company’s dynamic. A team who feels safe owning their mistakes will write candid, transparent postmortems. A team worried about deflecting blame and securing their next performance review will not. Teams where members do not feel safe to speak their mind will provide a biased version of the postmortem and will avoid addressing the actual causes. Fix the team issues, and the postmortems will improve by themselves.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Today, more companies are transparent and publish their postmortems on their blog. This is an extraordinary occasion to see the challenges faced by other engineers. You can see how Google, Facebook, Microsoft, or smaller startups address issues. We should acclaim companies and engineers who publish such documents. It takes a lot of humility to admit failure and expose mistakes for the world to see.&lt;/p&gt;

&lt;p&gt;The resource section below contains examples of interesting postmortems published by famous companies. Take the time to read a couple of these, they read like a thriller and I guarantee you will learn a lot!&lt;/p&gt;

&lt;p&gt;I hope this article was helpful to you. Do get in touch at &lt;a href=&quot;https://twitter.com/dhatanian&quot;&gt;@dhatanian&lt;/a&gt; if you want to discuss systems resiliency or share interesting postmortems with me!&lt;/p&gt;

&lt;h2 id=&quot;some-examples-of-postmortems&quot;&gt;Some examples of postmortems&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://engineering.heroku.com/blogs/2017-02-15-filesystem-corruption-on-heroku-dynos/&quot;&gt;2017-02-15 Heroku users experience elevated error rates due to improper versioning of the code that upgrades containers&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/blog/summary-of-windows-azure-service-disruption-on-feb-29th-2012/&quot;&gt;2012-02-29 Generation certificate code that was not written to handle leap days make VM creation on Azure fail&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://about.gitlab.com/2017/02/10/postmortem-of-database-outage-of-january-31/&quot;&gt;2017-01-31 After several erroneous manipulations on the database, Gitlab wipe their production database and discover that backups are not available&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.google.com/document/d/1ScqXAdb6BjhsDzCo3qdPYbt1uULzgZqPO8zHeHHarS0/preview?sle=true&amp;amp;hl=en&amp;amp;forcehl=1#heading=h.dfbilqgnc5sf&quot;&gt;Another database-related postmortem at Gitlab&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://codurance.com/2015/07/14/learning-from-our-failures/&quot;&gt;Not exactly a postmortem, but an article about how my colleague Franziska learned from her failure after taking down our company blog by mistake&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;additional-resources-on-postmortems&quot;&gt;Additional resources on postmortems:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://landing.google.com/sre/book/chapters/postmortem-culture.html&quot;&gt;The postmortem culture at Google, part of the Google SRE book that I highly recommend&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://landing.google.com/sre/book/chapters/postmortem.html&quot;&gt;An example of postmortem on a fake Shakespeare service, from the same book&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://danluu.com/postmortem-lessons/&quot;&gt;Postmortem lessons by Dan Luu&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/danluu/post-mortems&quot;&gt;A list of Postmortems by the same author&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 05 Mar 2017 10:10:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2017/03/05/postmortems-failure-done-right.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2017/03/05/postmortems-failure-done-right.html</guid>
        
        
      </item>
    
      <item>
        <title>What I wish I knew when I started as a software developer</title>
        <description>&lt;p&gt;Having worked in the software engineering field for 6 years, I sometimes pause and consider how much I have learned since I started my career. One thing that strikes me is how many things I would want to tell just-out-of-college me if I had the occasion.&lt;/p&gt;

&lt;p&gt;While I don’t have that capability, I hope that the few items below will be useful to people on their way out of college and into the software industry.&lt;/p&gt;

&lt;h1 id=&quot;keep-on-learning&quot;&gt;Keep on learning&lt;/h1&gt;

&lt;p&gt;You just obtained your diploma, are overwhelmed by a sense of accomplishment and now feel the desire to prove yourself to your new colleagues? That is great, congratulations and good luck, but keep in mind that this is just the beginning of your learning.&lt;/p&gt;

&lt;p&gt;You will find soon that you need to keep learning new technologies, new methods, new techniques, new languages. Start as soon as possible, because learning in an unsupervised way (no syllabus, no teacher, no final exam) is a skill that you need to hone.&lt;/p&gt;

&lt;p&gt;Learn things outside your comfort zone. If you work on a dynamic language, learn a statically typed one. Use VIM or Emacs if you are used to an IDE, etc.&lt;/p&gt;

&lt;h2 id=&quot;find-a-mentor&quot;&gt;Find a mentor&lt;/h2&gt;

&lt;p&gt;I just said that there are no teachers after college, but that is not entirely true. Nobody will sit in a classroom with you and go over a predefined lesson. But it is still extremely beneficial to find one more experienced person that can guide you.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/23215733-the-software-craftsman&quot;&gt;Sandro’s book “The Software Craftsman”&lt;/a&gt; describes how a mentor is beneficial to a new software developer at great lengths.&lt;/p&gt;

&lt;p&gt;Depending on the company you are working with, you might be provided with a mentor, or someone will come to you and spontaneously offer their help. Sometimes you will need to look for them. If you cannot find them amongst your colleagues, look for them in community events for example.&lt;/p&gt;

&lt;h2 id=&quot;ask-questions&quot;&gt;Ask questions&lt;/h2&gt;

&lt;p&gt;I did not know what a framework was when I got out of college. Looking up the definition of Wikipedia did not help either, and for several long months I was too afraid to show my ignorance and ask.&lt;/p&gt;

&lt;p&gt;Do not repeat my mistake. You are not expected to arrive at your first job knowing everything. Ask, don’t be afraid to show you do not know something.&lt;/p&gt;

&lt;p&gt;Most of the time, you will find out that either at least one other person in the team had the same question, or people have slightly different definitions of the term you are asking about. Even if you are the only one asking the question, you will not be the only one learning something in return.&lt;/p&gt;

&lt;h1 id=&quot;get-good-practices-early&quot;&gt;Get good practices early&lt;/h1&gt;

&lt;p&gt;These good practices will be the foundation upon which you will build your career. Get them as early as possible.&lt;/p&gt;

&lt;h2 id=&quot;leverage-existing-libraries-and-frameworks&quot;&gt;Leverage existing libraries and frameworks&lt;/h2&gt;

&lt;p&gt;I barely knew how to properly use a library when I got out of college. As I said earlier I did not even know what a framework was.&lt;/p&gt;

&lt;p&gt;As juniors, we have a tendency to re-invent the wheel. Our custom-made solutions will usually be of less quality than a library on which hundreds people have collaborated over several years. Try to re-use what is already available.&lt;/p&gt;

&lt;p&gt;On the other hand, understand what are the tradeoffs of a given library. Is it still maintained? It is easy to use? Does it require huge changes in your application’s architecture? Make sure that you isolate that library, so that replacing it does not mean making changes all over your code.&lt;/p&gt;

&lt;h2 id=&quot;but-still-understand-the-fundamentals&quot;&gt;But still understand the fundamentals&lt;/h2&gt;

&lt;p&gt;Libraries are convenient, because they remove complexity. However, take the time to understand a bit of how they work under the hood.&lt;/p&gt;

&lt;p&gt;While your web framework will handle HTTP requests for you, you need to get what &lt;a href=&quot;https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol&quot;&gt;HTTP&lt;/a&gt; is and how to conveys information between two computers. Similarly, take the time to understand the basis of &lt;a href=&quot;https://en.wikipedia.org/wiki/Domain_Name_System&quot;&gt;DNS&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Transport_Layer_Security&quot;&gt;TLS&lt;/a&gt;, and other low-level protocols. If your application uses &lt;a href=&quot;https://en.wikipedia.org/wiki/OAuth&quot;&gt;OAuth&lt;/a&gt; or &lt;a href=&quot;https://www.owasp.org/index.php/Session_Management_Cheat_Sheet&quot;&gt;session-based authentication&lt;/a&gt;, you need to know it and you need to know what the consequences are. &lt;a href=&quot;https://github.com/alex/what-happens-when&quot;&gt;This page&lt;/a&gt; is a good starting point.&lt;/p&gt;

&lt;h2 id=&quot;assume-the-mistake-is-yours&quot;&gt;Assume the mistake is yours&lt;/h2&gt;

&lt;p&gt;You will face unexplainable bugs. Sometimes even &lt;a href=&quot;https://en.wikipedia.org/wiki/Heisenbug&quot;&gt;heisenbugs&lt;/a&gt;, that disappear when you debug them. In despair, you might be tempted to reject the fault to something else than your code. Always start with the assumption that it is an issue with your code, &lt;em&gt;not&lt;/em&gt; the library, the OS or anything else.&lt;/p&gt;

&lt;p&gt;This is a lesson in humility. There is a much higher chance that your barely tested, 2 days old code is buggy than a battle-hardened library that has been around for years. As your debugging skills and your bug-hunter instinct improve over time, you will come to that conclusion anyway.&lt;/p&gt;

&lt;p&gt;Sometimes, it is indeed a bug in another software component than yours. However flagrant the bug, however careless the offending code seemed, remember of how many bugs your code was littered to being with, and don’t hold it against the authors of the faulty code.&lt;/p&gt;

&lt;h2 id=&quot;test-test-test&quot;&gt;Test, test, test&lt;/h2&gt;

&lt;p&gt;I think that automated testing and test-driven development are not practiced enough in software engineering courses. Those are amongst the most important skills in software development today, and I wish I was made aware of that earlier in my career.&lt;/p&gt;

&lt;p&gt;Learn how to write tests and how to maintain them. Pair with someone who can teach you test driven development. If you cannot find someone like that amongst your colleagues look at a &lt;a href=&quot;http://slack.softwarecraftsmanship.org/&quot;&gt;software craftsmanship community&lt;/a&gt; next to where you live.&lt;/p&gt;

&lt;h2 id=&quot;clean-code-principles&quot;&gt;Clean code principles&lt;/h2&gt;

&lt;p&gt;Learn how to write clean code from the start. &lt;a href=&quot;https://www.amazon.co.uk/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882&quot;&gt;The book by Robert C Martin&lt;/a&gt; is an excellent way to start. There is also plenty of content online (just check other articles on the &lt;a href=&quot;https://www.codurance.com/publications&quot;&gt;Codurance blog&lt;/a&gt;). Take the time to read some of that content and apply it. Ask your mentor or colleagues for feedback.&lt;/p&gt;

&lt;h1 id=&quot;programming-is-a-social-activity&quot;&gt;Programming is a social activity&lt;/h1&gt;

&lt;p&gt;Depending how much programming practice you’ve had before starting your career, you might still believe that a programmer’s day is spent sitting alone in front of their editor. You need to understand as soon as possible how much you win by working with others.&lt;/p&gt;

&lt;h2 id=&quot;pair-programming&quot;&gt;Pair programming&lt;/h2&gt;

&lt;p&gt;This is a very important skill to develop. Get used to coding with someone else, passing the keyboard from one person to the other from time to time. Practice this both with people more skilled than you, and with beginners.&lt;/p&gt;

&lt;h2 id=&quot;being-good-at-writing-code-is-not-even-half-the-job&quot;&gt;Being good at writing code is not even half the job&lt;/h2&gt;

&lt;p&gt;You need to learn how to work with users or Product Owners, ask questions about features and manage expectations.&lt;/p&gt;

&lt;p&gt;You will discover that sometimes, the answer will not be to add more code to your project: either the feature is not needed, or it can be implemented with the current version of the app, or there is another application that provides 80% of the functionality.&lt;/p&gt;

&lt;h2 id=&quot;dont-be-clever&quot;&gt;Don’t be “clever”&lt;/h2&gt;

&lt;p&gt;Being clever is rewarded in college. You may have been encouraged to optimize a for loop, or write one less line of code by replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i=i+1&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i++&lt;/code&gt; in the line before.&lt;/p&gt;

&lt;p&gt;Being creative and clever to solve a hard, complex problem is good. But do not use clever micro-optimizations that make your code less readable.&lt;/p&gt;

&lt;p&gt;One of the greatest challenges as a beginner software engineer is to make your code readable. Your teammates need to be able to understand your code and evolve it without your help. Clever optimizations often go against that by making the code’s intent less obvious. Unlearn this “academic” reflex.&lt;/p&gt;

&lt;p&gt;But understand it is a tradeoff, and readable code is more important that clever code. Boring code is good.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://codurance.com/careers/&quot;&gt;We are hiring software craftspeople and apprentices! More information here.&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 03 Oct 2016 10:10:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2016/10/03/what-i-wish-i-knew-earlier.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2016/10/03/what-i-wish-i-knew-earlier.html</guid>
        
        <category>craftsmanship</category>
        
        <category>practices</category>
        
        <category>productivity</category>
        
        
      </item>
    
      <item>
        <title>Isolating integration tests and mocking dependencies with Spring Boot</title>
        <description>&lt;p&gt;Integration tests can be slow and unreliable because they depend on too many components in the system. Up to a certain point, this is unavoidable: integration tests are here to validate how each part of your system plays with other internal or external components.&lt;/p&gt;

&lt;p&gt;We can, however, improve some integration tests by only spinning up the required dependencies, instead of the whole system. Let’s imagine an application that depends on a database, a third-party REST API and a message queue:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2016-01-31-isolating-integration-tests-with-the-spring-framework.md/my-application.png&quot; alt=&quot;Google Apps configuration screen&quot; title=&quot;Our application with 3 dependencies&quot; style=&quot;margin:auto;display:block;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Assume now that we would like our integration test to validate a behavior that only includes calls to the REST API but no call to the database or the message queue. To give a concrete example, let’s assume we want to check that our REST client is correctly configured to time out after 3 seconds.&lt;/p&gt;

&lt;p&gt;All we need for this is a small &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Controller&lt;/code&gt; that will mock the REST API by waiting before returning an answer to the REST client. The wait time will be passed as a parameter in the query string.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Profile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;restTemplateTimeout&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/test&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DelayedWebServerController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/delayRestTemplate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;answerWithDelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;@RequestParam&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;waitTimeMs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitTimeMs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitTimeMs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Delayed Result&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;What is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Profile&lt;/code&gt; annotation used for? If we inject this controller into our standard application context, this has several drawbacks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The test will be slow: we only need to start one controller, not the whole thing&lt;/li&gt;
  &lt;li&gt;Our controller will be picked up by Spring and injected into every other integration test, slowing down each integration test and maybe stepping on another test’s toes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better alternative would be to spin up a minimal Spring Boot application exposing only our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DelayedWebServerController&lt;/code&gt;. We will also tell Spring Boot to scan only the packages we are interested in, and to exclude persistence-related auto-configuration since we do not need it to spin up a controller. This is done in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; class like this one:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Profile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;restTemplateTimeout&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@EnableAutoConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceAutoConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HibernateJpaAutoConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ComponentScan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basePackages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my.application.resttemplate.timeout&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DelayedWebServerConfiguration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//The class is empty and only used to support the annotations&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The Spring context configuration can get quite confusing, let us look at the annotations one after the other:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Profile&lt;/code&gt;: This tells Spring that this configuration should only be used then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;restTemplateTimeout&lt;/code&gt; profile is active. Further in this article, we will see how we enable this profile for a specific integration test. It is this annotation that prevents the configuration to be picked up by other unrelated integration tests. Note that our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DelayedWebServerController&lt;/code&gt; is identically annotated.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Configuration&lt;/code&gt;: Standard annotation to tell Spring that this is a context configuration class.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@EnableAutoConfiguration&lt;/code&gt;: Here we disable some of the Spring Boot “magic” that we do not need for our specific test&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ComponentScan&lt;/code&gt;: We speed up the Spring Boot application startup by only scanning one package instead of the whole project. Any Spring-annotated class that is outside of this package will not be picked up by Spring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how the integration test looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@RunWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SpringJUnit4ClassRunner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@WebIntegrationTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;server.port:0&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@SpringApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DelayedWebServerConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ActiveProfiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;restTemplateTimeout&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RestTemplateShould&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Rule&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExpectedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thrown&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;${local.server.port}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RestTemplate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;restTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;throw_timeout_if_response_lasts_more_than_two_seconds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;thrown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ResourceAccessException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;thrown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;expectCause&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instanceOf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SocketTimeoutException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;callEndpointWithDelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_not_throw_timeout_if_response_lasts_less_than_two_seconds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;callEndpointWithDelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;callEndpointWithDelay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delayMs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;restTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getForObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/test/delayRestTemplate?waitTimeMs=&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delayMs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Of course, all those classes are stored in our test source folder (usually &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/test/java&lt;/code&gt;) since they are not required for production.&lt;/p&gt;

&lt;p&gt;Let us have a look again at the annotations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@RunWith&lt;/code&gt;: The test will use the Spring Junit runner who will take care of creating the Spring context for us.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@WebIntegrationTest&lt;/code&gt;: Tells Spring that this is an integration test running a web application, otherwise by default Spring will not run an HTTP server in test mode. We also set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server.port&lt;/code&gt; to a value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; so that Spring Boot choose a random port for the HTTP server to listen to. This allows to have several tests running in parallel, or to have another version of the application running in the background.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@SpringApplicationConfiguration&lt;/code&gt;: We tell Spring where it will find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DelayedWebServerConfiguration&lt;/code&gt; class we created before.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ActiveProfiles&lt;/code&gt;: Enables the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;restTemplateTimeout&lt;/code&gt; profile, otherwise the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Controller&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; will be filtered out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We now have an integration test running with a limited set of dependencies instead of the whole application. What if we wanted to go further and add mocks into the game? This may be required when a dependency does not have a dev environment or that it is too complicated to call from a developer’s workstation. In that case, we can add those mocks to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; class and they will be injected into the test’s Spring context.&lt;/p&gt;

&lt;p&gt;Here is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; example where we inject a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomerService&lt;/code&gt; mocked by Mockito instead of the default one:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Profile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;validationTests&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@EnableAutoConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceAutoConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HibernateJpaAutoConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ComponentScan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basePackages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.application.controller&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;my.application.actions&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValidationEndToEndConfiguration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Bean&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerService&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;customerService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CustomerService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this approach, we can make our integration tests more resilient. For slow or unreliable dependencies, it is more efficient to have the developers run their integration tests against a mocked version. However, do not forget that in the end your application will have to integrate with the real system, not the mocked one. For this reason, it makes sense to have the continuous integration server run the tests against the real system at the very least every day.&lt;/p&gt;
</description>
        <pubDate>Sun, 31 Jan 2016 00:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2016/01/31/isolating-integration-tests-with-the-spring-framework.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2016/01/31/isolating-integration-tests-with-the-spring-framework.html</guid>
        
        
      </item>
    
      <item>
        <title>New job at Codurance</title>
        <description>&lt;p&gt;I started on October 2015 a new job at &lt;a href=&quot;http://codurance.com&quot;&gt;Codurance&lt;/a&gt; in London. Codurance is a software consultancy centered around the principles of Software Craftsmanship.&lt;/p&gt;

&lt;p&gt;The co-founders are Sandro Mancuso and Mashooq Badar, both very active in the Software Craftsmanship community. Sandro even wrote a book, &lt;a href=&quot;http://www.amazon.co.uk/Books/dp/0134052501&quot;&gt;The Software Craftsman : Professionalism, Pragmatism and Pride&lt;/a&gt;. It highlights his views on how a good software engineer should take his career in his hands. I highly recommend it to anyone who works in software engineering.&lt;/p&gt;

&lt;p&gt;I first heard of Codurance through Sandro’s conference at Devoxx France 2014. I then read his book and found that a lot of the principles embodied in his view of the software craftsman resonated very strongly in me. I applied during the summer to work at Codurance and went through the hiring process.&lt;/p&gt;

&lt;p&gt;I was already very happy to receive an email from Sandro and Mashooq saying that my résumé had passed the first filter and that I was invited to write a simple program to show my development skills. I wrote the program, paying as much attention as I could to tests and design, and submitted my code. Two weeks later, I received a review of my code, along with an invitation for a Skype call with Mashooq and Sandro. Happy, I proceeded to read the code review, and I became blank : the review had 4 positive items and 25 items were listed in the “improvements” section. I honestly thought my submission was the best work I could have done, and they found 25 things that I missed ! Even worse, I could not find a single improvement I disagreed with.&lt;/p&gt;

&lt;p&gt;Clearly, the bar at Codurance was higher than I expected, and I dreaded that the Skype interview would show that I did not have the required skills. One week later, I had this Skype call with Mash and Sandro, and they made me immediately comfortable. We had a talk about design principles, they asked questions about SOLID and checked if I understood all their feedback. I was a bit stressed but eventually the interview went well, and I was extremely happy to receive an offer from Codurance later in the day.&lt;/p&gt;

&lt;p&gt;I have now been working at Codurace for one month, and I can say the atmosphere is really different there. People here are passionate about software and good design, they take pleasure in sharing their findings with their colleagues and we gather quite often to talk about anything related to software. I do not spend a day without learning something new.&lt;/p&gt;

&lt;p&gt;Codurance is a new step for me, and I’m proud to be part of such a great company.&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Nov 2015 00:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2015/11/20/new-job-at-codurance.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2015/11/20/new-job-at-codurance.html</guid>
        
        
      </item>
    
      <item>
        <title>Configuring an enterprise app sucks</title>
        <description>&lt;p&gt;There has been quite a lot of discussion over the last years about how configuration should be managed as code, be it software settings or infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For infrastructure, all the cool kids now use Docker, Vagrant or some other tool that allows you to store your infra’s configuration as text files. You can find more on &lt;a href=&quot;http://kief.com/infrastructure-as-code-versus-automation.html&quot;&gt;this blog post&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;For software settings, it is now commonly accepted that anything that is not specific to the environment (secrets, IP addresses, ports) should be stored along with the code in the source code repository. Some teams even store environment-specific settings in the same repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But when one configures an enterprise application, be it an open-source one or something purchased from a vendor, or even a Software As A Service solution, one is guaranteed to end up with all those levers, buttons and knobs to adjust and fiddle with. But that makes sense, enterprise applications need to be tweaked according to each company’s specificities. So the best option is to shove all the configuration possibilities in an maze of screens, tabs, checkboxes and helpful tooltips, right?&lt;/p&gt;

&lt;p&gt;I think that is wrong. This way of configuring software is a guarantee that something bad will happen at some point. Even collaboration tools such as Google Apps, who thrive on usability, provide administrators with overly complex settings options hidden deep withing a web interface. Identity and Access management tools such as OpenAM, IBM Tivoli Identity Manager and others suffer from the same plague.&lt;/p&gt;

&lt;p&gt;Here’s what the advanced configuration screen for Google Apps looks like. All those tabs and options are only for the GMail section, which gives you an idea of the overall complexity:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2015-11-19-configuring-an-enterprise-app-sucks.md/gmail.png&quot; alt=&quot;Google Apps configuration screen&quot; title=&quot;Google Apps configuration screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And this how you configure a token-based authentication on OpenAM. An admin needs to go through dozens of those screens filled with fields and not make a single mistake. We are talking about an application that centralizes authentication, mistakes can make all the applications in your information system inaccessible:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2015-11-19-configuring-an-enterprise-app-sucks.md/openam.png&quot; alt=&quot;OpenAM OATH configuration&quot; title=&quot;OpenAM OATH configuration&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Those configuration screens are wrong on a lot of levels:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;It is hard to look for a specific setting&lt;/strong&gt;. You often have to search several screens and sections, each of those potentially relevant to your subject. Some configuration interfaces provide a search tool to allow you to find the screen where you can configure a given setting, which gives you an idea of how hard it can be to navigate the interface in the first place.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keeping track of the changes performed on the configuration is complicated&lt;/strong&gt;. Any serious enterprise application will provide an audit track, unfortunately the track is usually a log of atomic events such as “Administrator Michael changed setting X to value A”. There rarely is a way to have an overview of the complete configuration at the moment of this change. Did this change make sense at the time? If some setting Y was dependent on the setting X, was Y also correctly set?&lt;/li&gt;
  &lt;li&gt;For any use case but the most trivials, the administrators of those applications will want to setup and maintain several environments. At the very least there will be one pre-production environment where admins test new settings before enabling them in production. Usually, the configuration is done in the test environment through a mazelike user interface, and each step is documented along with screenshots in a change document. Once the change is validated (i.e. when the business is happy with the feature), the change document is used to reproduce the same steps in production. This means that:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;It is hard to keep environments in sync&lt;/strong&gt;. If an environment’s configuration drifts from the configuration, there’s no easy way to detect it, and it can invalidate your future tests.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;The process of moving from one environment to another is error prone&lt;/strong&gt;. An admin who fails to check a box to check will break production without knowing it.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;The process is slow&lt;/strong&gt;. Updating all the settings for a single change can take from several minutes up to hours for very complex change. During that time, the application is in some kind of untested, probably-still-works state. Or it was taken offline for the time of the maintenance, which means that user are now blocked and waiting for the change to complete.&lt;/li&gt;
      &lt;li&gt;Since the process is error prone, you can be sure that at some point, something will go wrong and you will need to rollback your change. Guess what? &lt;strong&gt;Rolling back is at least as slow&lt;/strong&gt; as performing the change, if not worst. So the application remains available for a longer time.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to configure our enterprise applications the same way we manage other software settings or modern infrastructure: we need to move from an imperative configuration administration composed of manual steps like this:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Change setting A to value X&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;to a declarative style that basically says:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Here&apos;s a complete description of the configuration I expect, now make it happen&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The good news is that we already have the tools for this: uploading the configuration as a text file in a simple human-readable format such as json or yaml would be enough. This solves all the issues we’ve seen:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Looking for a specific setting? Use the search feature of your preferred text editor, or even the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; command line tool!&lt;/li&gt;
  &lt;li&gt;The application can let administrators track changes on its configuration by providing a source control system similar to Git. You would know who changed a given line, when, and could in one click have an immediate view of the resulting configuration after this change. Here is how a change on a configuration file would be viewed in GitHub:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/2015-11-19-configuring-an-enterprise-app-sucks.md/config-diff.png&quot; alt=&quot;A change on a configuration file as viewed on GitHub&quot; title=&quot;A change on a configuration file as viewed on GitHub&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Moving from one environment to another becomes trivial: it is a matter of transferring the configuration file to the new environment, which is much faster and less error prone. Since the settings history is kept, rolling back to the previous state becomes trivial: just tell the application to revert back to the previous version of the settings.&lt;/li&gt;
  &lt;li&gt;As a plus, your configuration is easy to export and backup. This would allow competing platforms to let you import the configuration from your previous solution when you migrate. Want to migrate from CloudFileServer™ to SAASEnterpriseFileManager™? Just import your old configuration and be ready to start in minutes!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As software engineers, we’ve progressed a lot in the field of ad hoc developments, we have industrialized the way we deploy our applications and manage our servers, we keep most of the configuration safely stored and tracked in source repositories. But when we provide platforms to our customers in the form of enterprise and/or SAAS applications, we still let then manage those like we’re in the 90’s. It is time to provide them with a proper way to manage their IT solutions.&lt;/p&gt;
</description>
        <pubDate>Thu, 19 Nov 2015 09:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2015/11/19/configuring-an-enterprise-app-sucks.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2015/11/19/configuring-an-enterprise-app-sucks.html</guid>
        
        
      </item>
    
      <item>
        <title>Falsehood articles - everything you have been told is a lie</title>
        <description>&lt;p&gt;I wrote last year &lt;a href=&quot;/2014/09/10/why-you-should-not-try-to-deal-with.html&quot;&gt;an article about dates&lt;/a&gt;, and how complicated it can get. I have updated it recently to included two excellent articles from Noah Sussman, a tester at Etsy who explains “falsehoods” about dates : thing we might hold for true, but that aren’t. For example :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Years have 365 days.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yeah this one is easy, we have leap years with one more day.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A week (or a month) always begins and ends in the same year.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, another obvious one, but I’ve seen this error before&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Human-readable dates can be specified in universally understood formats such as 05/07/11&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one is interesting. In France it would translate as the 5th of July while in the US it would be the 7th of May.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;One minute on the system clock has exactly the same duration as one minute on any other clock&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wrong, as can be expected&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ok, but the duration of one minute on the system clock will be pretty close to the duration of one minute on most other clocks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are cases where clocks can go &lt;strong&gt;really&lt;/strong&gt; adrift. But it’s weird to imagine that even the duration of a minute could be very different from one clock to another. But the coup de grâce comes right after.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Fine, but the duration of one minute on the system clock would never be more than an hour.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one puzzled me. I couldn’t imagine how a clock would suddenly make minutes last hours. Or, as Noah writes it :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can’t be serious.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He then gives an example of a clock for which some minutes would actually last hours :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There was a fascinating bug in older versions of KVM on CentOS. Specifically, a KVM virtual machine had no awareness that it was not running on physical hardware. This meant that if the host OS put the VM into a suspended state, the virtualized system clock would retain the time that it had had when it was suspended. E.g. if the VM was suspended at 13:00 and then brought back to an active state two hours later (at 15:00), the system clock on the VM would still reflect a local time of 13:00. The result was that every time a KVM VM went idle, the host OS would put it into a suspended state and the VM’s system clock would start to drift away from reality, sometimes by a large margin depending on how long the VM had remained idle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;There was a cron job that could be installed to keep the virtualized system clock in line with the host OS’s hardware clock. But it was easy to forget to do this on new VMs and failure to do so led to much hilarity. The bug has been fixed in more recent versions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Funny right ? I find that kind of stuff very interesting. There are other such falsehood posts on other subjects, which I recommend reading :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I think this is the oldest one, Patrick McKenzie’s &lt;a href=&quot;http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/&quot;&gt;Falsehoods Programmers Believe About Names&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The one which is quoted at the beginning of this post, Noah Sussman’s &lt;a href=&quot;http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time&quot;&gt;Falsehoods programmers believe about time&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A lot of people wanted to contribute to this list, so Noah compiled their falsehoods in a second post : &lt;a href=&quot;http://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time&quot;&gt;More falsehoods programmers believe about time; “wisdom of the crowd” edition&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Matthias Wesmann’s &lt;a href=&quot;http://wiesmann.codiferes.net/wordpress/?p=15187&amp;amp;lang=en&quot;&gt;Falsehoods programmers believe about geography&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;C. Scyphers’ &lt;a href=&quot;http://www.cscyphers.com/blog/2012/06/28/falsehoods-programmers-believe-about-gender/&quot;&gt;Falsehoods Programmers Believe About Gender&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 30 Jul 2015 00:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2015/07/30/falsehood-articles-everything-you-ve-been-told-is-a-lie.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2015/07/30/falsehood-articles-everything-you-ve-been-told-is-a-lie.html</guid>
        
        
      </item>
    
      <item>
        <title>Using HTTP sessions with Google Cloud Endpoints</title>
        <description>&lt;p&gt;I like how easy &lt;a href=&quot;https://cloud.google.com/appengine/docs/java/endpoints/&quot;&gt;Google Cloud Endpoints&lt;/a&gt; makes it to create a REST API on Google App Engine. Granted, there are other solutions in the Java world (I also used RestX and Spring) but I still like the simplicity of Google Cloud Endpoints.&lt;/p&gt;

&lt;p&gt;However, one problem with Cloud Endpoints is the authentication : by default it only supports Google’s OAuth2 authentication protocol. Because my application uses sessions for other purposes, I wanted to authenticate the user based on his session. Endpoints authentication is actually extensible through an almost undocumented class called &lt;a href=&quot;https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Authenticator&quot;&gt;Authenticator&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Authenticator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Another issue is that this authenticator does not have access to the session either… The Endpoints infrastructure strips off the cookies and session information before injecting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpServletRequest&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authenticator.authenticate()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The solution I found is to send the session id in a header. In App Engine, sessions are stored in memcache and the datastore, so I can extract those sessions easily. Here’s how it works :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Authenticator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DatastoreService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datastoreService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DatastoreServiceFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDatastoreService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MemcacheService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memcacheService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MemcacheServiceFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getMemcacheService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpServletRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;HttpSession&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpServletRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jSessionId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpServletRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getHeader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-MYAPP-JSESSIONID&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jSessionId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getUserFromSessionInDatastore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jSessionId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;toEndpointsUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OAuth2UserService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;USER_SESSION_PROPERTY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;toEndpointsUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getUserFromSessionInDatastore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jSessionId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entityId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_ahs&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jSessionId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memcacheId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cacheduser/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entityId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;SessionDataInMemcache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memcacheService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memcacheId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isExpired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Entity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datastoreService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;KeyFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_ah_SESSION&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entityId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EntityNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expires&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_expires&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expires&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Blob&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_values&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valuesMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ByteArrayInputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteArrayInputStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ObjectInputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objectInputStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ObjectInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byteArrayInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;valuesMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objectInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;readObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ClassNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valuesMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OAuth2UserService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;USER_SESSION_PROPERTY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setExpirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;memcacheService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memcacheId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sessionDataInMemcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EndpointUser&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EndpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;//You can put here additional attributes if you wish to&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;toEndpointsUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyAppUser&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;EndpointUser&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EndpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//If you have extra attributes in your session, put them here&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SessionDataInMemcache&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Serializable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

	    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getExpirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setExpirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;expirationTime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expirationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

	    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isExpired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expirationTime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
	    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this code, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authenticator&lt;/code&gt; gets the session id from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-MYAPP-JSESSIONID&lt;/code&gt; HTTP header. It then tries to fetch and deserialize the session info from memcache. If this fails, the session is retrieved from the datastore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; : this only works correctly if you invalidate the session when the user logs out and then logs back in. If one given session can be associated with more than one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyAppUser&lt;/code&gt;, or if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyAppUser&lt;/code&gt; object changes during the session’s life (for example if you update attributes) then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SessionDataInMemcache&lt;/code&gt; object will be out of sync. This was not a problem for my application.&lt;/p&gt;

&lt;p&gt;Then all I need to do is configure jQuery to include the session ID as a header when it makes AJAX calls, here’s how it’s done :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Cookies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;JSESSIONID&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajaxSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;X-MYAPP-JSESSIONID&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cookies&lt;/code&gt; class here is provided by a small &lt;a href=&quot;https://github.com/ScottHamper/Cookies&quot;&gt;Cookies.js&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;All we have to do now is to configure our Cloud Endpoints API. Note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticators&lt;/code&gt; parameter on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Api&lt;/code&gt; annotation.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Api&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;myapi&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;v1&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authenticators&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LandingPage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ApiMethod&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloMessage&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ForbiddenException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EndpointUser&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HelloMessage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EndpointUser&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;checkUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ForbiddenException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpointUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ForbiddenException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;User must be authenticated&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpointUser&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EndpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ForbiddenException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Expected user with opco information&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OAuth2EndpointsAuthenticator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EndpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpointUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it ! We are now ready to roll with a Google Cloud Endpoints service protected by Java’s standard HTTP sessions.&lt;/p&gt;
</description>
        <pubDate>Sat, 06 Jun 2015 00:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2015/06/06/using-http-sessions-with-google-cloud-endpoints.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2015/06/06/using-http-sessions-with-google-cloud-endpoints.html</guid>
        
        
      </item>
    
      <item>
        <title>New blog on Jekyll and Github pages</title>
        <description>&lt;p&gt;I finally got tired of Blogger’s very slow and overcomplicated interface, so when I bought the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hatanian.com&lt;/code&gt; domain name I decided to also migrate to something lighter.&lt;/p&gt;

&lt;h1 id=&quot;setting-up&quot;&gt;Setting up&lt;/h1&gt;

&lt;p&gt;I wanted something that would accept markdown, display code nicely, accept git versioning. &lt;a href=&quot;http://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt; was a great fit, and since it works well on Github Pages, well I got hosting for free !&lt;/p&gt;

&lt;p&gt;I won’t explain how Jekyll and Github Pages work together, since Github has &lt;a href=&quot;https://help.github.com/articles/using-jekyll-with-pages/&quot;&gt;a very clear&lt;/a&gt; tutorial already.&lt;/p&gt;

&lt;p&gt;By now, my new and empty Jekyll blog was hosted on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dhatanian.github.io&lt;/code&gt; which is the default domain for Github Pages. I wanted it to be hosted on my new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hatanian.com&lt;/code&gt; domain, so I first followed &lt;a href=&quot;https://help.github.com/articles/setting-up-a-custom-domain-with-github-pages/&quot;&gt;Github’s instructions on how to use a custom domain&lt;/a&gt;. Then it hit me : how will I add TLS, since this is not supported by Github Pages ?&lt;/p&gt;

&lt;p&gt;Luckily Cloudflare has a &lt;a href=&quot;https://www.cloudflare.com/plans&quot;&gt;free plan&lt;/a&gt; that includes TLS (which they call SSL, weirdly). This was really a two-clicks setup, Cloudflare is perfectly integrated with Godaddy and in a few minutes I had a blog serving its (still empty) content over HTTPS. It is not perfect as the connection is only encrypted between the browser and Cloudflare, but not between Cloudflare and Github Pages. However it’s good enough for a private blog !&lt;/p&gt;

&lt;p&gt;It felt a bit strange to simply give Cloudflare access to my DNS settings on Godaddy and see that they were able to generate a certificate for my domain. Then again it’s logical : a lot of certificate authorities check the domain ownership through the DNS entries.&lt;/p&gt;

&lt;h1 id=&quot;first-tweaks&quot;&gt;First tweaks&lt;/h1&gt;

&lt;p&gt;Anyway I now had a fresh new blog that I needed to fill in. I fixed up the default theme a little bit by jutifying the text by default and moving the navigation bar to the left of the screen.&lt;/p&gt;

&lt;p&gt;Jekyll’s default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; is the list of blog posts. I don’t think that’s a great landing page and I prefer people to arrive on my latest post, so I used &lt;a href=&quot;https://gist.github.com/nimbupani/1421828&quot;&gt;this great hack by nimbupani&lt;/a&gt; to get it right :&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/nimbupani/1421828.js&quot;&gt;&lt;/script&gt;

&lt;h1 id=&quot;migrating-bloggers-content&quot;&gt;Migrating Blogger’s content&lt;/h1&gt;

&lt;p&gt;This was quite a piece of cake. Blogger allows you to export a XML file that contains all your blog posts and the associated comments (we’ll talk about comments later).&lt;/p&gt;

&lt;p&gt;I then used &lt;a href=&quot;http://import.jekyllrb.com/docs/blogger/&quot;&gt;Jekyll’s Blogger import gem&lt;/a&gt;, and it generated each blog post in html format, even keeping some code formatting.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags where still linking to Blogger though, so I downloaded them manually (there were’nt so many) and put them on Github Pages along with the articles.&lt;/p&gt;

&lt;p&gt;For some reason, Blogger set fixed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height&lt;/code&gt; attributes for the images. This resulting in weird-looking image that were shrinked when viewed on small screens. Removing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height&lt;/code&gt; attributes from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags fixed the issue.&lt;/p&gt;

&lt;h1 id=&quot;setting-up-a-workflow&quot;&gt;Setting up a workflow&lt;/h1&gt;

&lt;p&gt;When I started working with images, I was faced with a problem : where do I put images associated with a given blog post ? I would prefer to store them next to the post, so I started creating dedicated folders for each post, with each time the html/md file along with the associated images. But Jekyll started to complain :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Error:  invalid byte sequence in UTF8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Jekyll actually tries to process every file that is in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; folder, even ones that are clearly binaries. There is no way do define exclusions. The Jekyll documentation recommends that images and other assets be placed in a dedicated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assets&lt;/code&gt; directory, but I did not want images from all my posts mixed together.&lt;/p&gt;

&lt;p&gt;I eventually settled for an intermediate solution : each post will have an associated folder in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assets&lt;/code&gt; directory. My folder structure now looks like this :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\david-codes
|--\_posts
|  |--- 2015-05-16-new-blog-on-jekyll-and-github-pages.md
|
|--\assets
   |--\posts
      |--\2015-05-16-new-blog-on-jekyll-and-github-pages.md
	     |--- image.png
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not super convenient since images and posts are separated, but at least it’s easy to find all the images for a given post.&lt;/p&gt;

&lt;p&gt;Inside the post, a link to an image would look like this :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;assets/posts/2015-05-16-new-blog-on-jekyll-and-github-pages.md/image.png&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which obviously is not really convenient to type and depends too much on the article’s name. So I used a liquid tag :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;/assets/{{ page.path | replace:&apos;\_posts&apos;,&apos;posts&apos; }}/image.png&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(ignore the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\&lt;/code&gt; character which is here to fix &lt;a href=&quot;https://github.com/atom/language-gfm/issues/44&quot;&gt;a very annoying issue in the atom editor&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I then created an atom snippet, that’s a piece of code that I can generate quickly. The Cson configuration is like this :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&apos;.text.html, .source.gfm&apos;:
  &apos;jekyll.asset&apos;:
    &apos;prefix&apos;: &apos;img&apos;
    &apos;body&apos;: &apos;/assets/ {{ page.path | replace:\&apos;\_posts\&apos;,\&apos;posts\&apos; }} /${1:image}.png$2&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now when I type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-TAB&lt;/code&gt; the full liquid tag is automatically generated for me.&lt;/p&gt;

&lt;p&gt;Finally, I wrote a quick shell script to create an article and the associated assets folder :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;title=$1
slug=`echo $title | tr &apos;[:upper:]&apos; &apos;[:lower:]&apos; | sed -E &apos;s/[^a-z0-9]+/-/g&apos;`
date_slug=`date +&quot;%Y-%m-%d&quot;`
file=&quot;$date_slug-$slug.md&quot;

echo &quot;Creating post $file&quot;
post_file=&quot;\_posts/$file&quot;
touch post_file
mkdir assets/posts/$file
echo &quot;---&quot; &amp;gt;&amp;gt; $post_file
echo &quot;layout: post&quot; &amp;gt;&amp;gt; $post_file
echo &quot;title: $title&quot; &amp;gt;&amp;gt; $post_file
echo &quot;author: David Hatanian&quot; &amp;gt;&amp;gt; $post_file
echo &quot;---&quot; &amp;gt;&amp;gt; $post_file
echo &quot;&quot; &amp;gt;&amp;gt; $post_file

atom $post_file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;adding-comments&quot;&gt;Adding comments&lt;/h1&gt;
&lt;p&gt;Adding comments is absolutely trivial with &lt;a href=&quot;https://disqus.com&quot;&gt;Disqus&lt;/a&gt;. This free service generates by default a comment thread per URL of your blog. You just have to insert some HTML and Javascript at the end of your page.&lt;/p&gt;

&lt;p&gt;Because my posts can have several URLs (the latest post is also the root of the site), I slightly modified the Disqus initialization code to include a custom identifier, which is the post id extracted by a Liquide tag :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;disqus_thread&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;

var disqus_shortname = &apos;david-codes&apos;;
var disqus_identifier = &apos;{{ page.id }}&apos;; //Here&apos;s the trick

(function() {
	var dsq = document.createElement(&apos;script&apos;); dsq.type = &apos;text/javascript&apos;; dsq.async = true;
	dsq.src = &apos;//&apos; + disqus_shortname + &apos;.disqus.com/embed.js&apos;;
	(document.getElementsByTagName(&apos;head&apos;)[0] || document.getElementsByTagName(&apos;body&apos;)[0]).appendChild(dsq);
})();
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I needed to import comments from Blogger to Disqus. Disqus has a very nice import system but it required comments in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wxr&lt;/code&gt; format (from Wordpress). Fortunately, a kind soul wrote a Google App Engine appengine &lt;a href=&quot;https://blogger2wordpress.appspot.com/&quot;&gt;blogger2wordpress&lt;/a&gt; service to convert the Blogger format to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wxr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first import went well, but because the URLs had changed from Blogger to Github Pages, Disqus wouldn’t load the old comment threads in my blog. I reuploaded the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wxr&lt;/code&gt; after manually specifying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disqus_identifier&lt;/code&gt; on each thread :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;item&amp;gt;
  &amp;lt;title&amp;gt;How to provide seamless Single Sign On on Google Apps&amp;lt;/title&amp;gt;
  &amp;lt;link&amp;gt;http://david-codes.blogspot.com/2014/07/how-to-provide-seamless-single-sign-on.html&amp;lt;/link&amp;gt;
  &amp;lt;!-- Add this to identify the Disqus thread --&amp;gt;
  &amp;lt;dsq:thread_identifier&amp;gt;/2014/07/03/how-to-provide-seamless-single-sign-on&amp;lt;/dsq:thread_identifier&amp;gt;
  &amp;lt;pubDate&amp;gt;Thu, 03 Jul 2014 04:18:00 -0700&amp;lt;/pubDate&amp;gt;
  &amp;lt;dc:creator&amp;gt;David Hatanian&amp;lt;/dc:creator&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, I had a nice commenting interface, with my old comments loaded !&lt;/p&gt;

&lt;h1 id=&quot;redirecting-from-blogger-to-github-pages&quot;&gt;Redirecting from Blogger to Github Pages&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;http://blogtimenow.com/blogging/automatically-redirect-blogger-blog-another-blog-website/&quot;&gt;This post by Suriya Prakash&lt;/a&gt; explains well how to setup a redirection from Blogger.&lt;/p&gt;

&lt;p&gt;I adapted it that way :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;b:if cond=&apos;!data:mobile&apos;&amp;gt;
    &amp;lt;!-- posts --&amp;gt;
    &amp;lt;div class=&apos;blog-posts hfeed&apos;&amp;gt;

      &amp;lt;b:include data=&apos;top&apos; name=&apos;status-message&apos;/&amp;gt;

      &amp;lt;data:defaultAdStart/&amp;gt;
      &amp;lt;b:loop values=&apos;data:posts&apos; var=&apos;post&apos;&amp;gt;
		&amp;lt;b:if cond=&apos;data:numPosts != 1&apos;&amp;gt;
          &amp;lt;script&amp;gt;location.href=&amp;amp;quot;https://david-codes.hatanian.com/allposts&amp;amp;quot;&amp;lt;/script&amp;gt;
        &amp;lt;/b:if&amp;gt;
       &amp;lt;script&amp;gt;
           var d=&amp;amp;#39;&amp;lt;data:blog.url/&amp;gt;&amp;amp;#39;;
           d=d.replace(/.*\/\/[^\/]*/, &amp;amp;#39;&amp;amp;#39;);
			var postPage = d.replace(/\/201[3-5]\/[0-1][0-9]\//,&amp;amp;#39;&amp;amp;#39;);
           var timestamp=&amp;amp;#39;&amp;lt;data:post.timestampISO8601/&amp;gt;&amp;amp;#39;;
         console.log(&amp;amp;quot;Post page : &amp;amp;quot;+postPage);
         console.log(&amp;amp;quot;Timestamp : &amp;amp;quot;+timestamp);
		var dateUrl = timestamp.replace(/T.+/,&amp;amp;#39;&amp;amp;#39;).replace(/-/g,&amp;amp;#39;/&amp;amp;#39;);
         var redirectUrl = &amp;amp;quot;https://david-codes.hatanian.com/&amp;amp;quot;+dateUrl+&amp;amp;quot;/&amp;amp;quot;+postPage;
		location.href = redirectUrl

		&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also had to :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;disable the mobile-specific theme&lt;/li&gt;
  &lt;li&gt;change my theme to the usual static one because otherwise it would always redirect to the latest post. Note : change the theme before inserting custom code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One small issue was that the post files name were not exactly right after migration from Blogger. In Jekyll, a post starts with a date in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-mm-dd&lt;/code&gt; format, for example this post is :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2015-05-16-new-blog-on-jekyll-and-github-pages.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This post has the following URL :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2015/05/16/new-blog-on-jekyll-and-github-pages.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But for some posts the date prefix was incorrect. For example the file name would be :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2014-07-22-google-apis-checking-scopes-contained.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But the URL would be off by one day :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2014/07/21/google-apis-checking-scopes-contained.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was not on all posts. Because I mainly blogged from Vietnam and India, I guess this was a timezone issue. The solution was to remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date&lt;/code&gt; header from the migrated articles :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
layout: post
title: &apos;&quot;Cloud Platform Orchestrator&quot; - An App Engine/Compute Engine demo&apos;
date: &apos;2014-05-06T05:05:00.004-07:00&apos; --&amp;gt; REMOVE THIS
author: David Hatanian
tags:
modified_time: &apos;2014-05-06T05:05:55.659-07:00&apos;
thumbnail: http://4.bp.blogspot.com/--WuW40qT3vM/U2jQBN5c0lI/AAAAAAAAInU/BIKePHu2JTk/s72-c/createexecution.png
blogger_id: tag:blogger.com,1999:blog-5219665179084602082.post-8680951713691172637
blogger_orig_url: http://david-codes.blogspot.com/2014/05/cloud-platform-orchestrator-app.html
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion#&lt;/h1&gt;

&lt;p&gt;That’s all what is needed to move to Jekyll on Github pages ! The process was quite smooth, now I need to tweak a bit this default theme :-)&lt;/p&gt;
</description>
        <pubDate>Sat, 16 May 2015 00:00:00 +0000</pubDate>
        <link>https://david-codes.hatanian.com/2015/05/16/new-blog-on-jekyll-and-github-pages.html</link>
        <guid isPermaLink="true">https://david-codes.hatanian.com/2015/05/16/new-blog-on-jekyll-and-github-pages.html</guid>
        
        
      </item>
    
  </channel>
</rss>
