Rerun: My favorites pictures of 2017

In preparation for the WTA’s Northwest Exposure photo contest, I was reviewing my favorite pictures from this year, just like I did last year.

But there are just too many this year. I’ve been to so many gorgeous places, gone on so many amazing trips, I just have a really hard time reducing my set of photos to just a top 5. With a lot of heartrending, I think I can manage to cut it to a top 12.

Camp Schurman, in front of Mount Rainier.

Camp Schurman, in front of Mount Rainier.

Climbers on the Emmons route to the summit of Mount Rainier.

Climbers on the Emmons route to the summit of Mount Rainier.

Sunrise at Camp Schurman, Mount Rainier.

Sunrise at Camp Schurman, Mount Rainier.

Mountaineers descending Silver Star Mountain.

Mountaineers descending Silver Star Mountain.

Mount Adams and Mount Hood, seen from Mount Rainier.

Mount Adams and Mount Hood, seen from Mount Rainier.

Mountaineers on the Emmons Glacier of Mount Rainier.

Mountaineers on the Emmons Glacier of Mount Rainier.

Camp near Dome Peak.

Camp near Dome Peak.

View from the "Sidewalk" of Dome Peak, with climbers on the Dome Glacier.

View from the “Sidewalk” of Dome Peak, with climbers on the Dome Glacier.

Mountaineers and penitentes at sunrise on Mount Rainier.

Mountaineers and penitentes at sunrise on Mount Rainier.

On the Winthrop glacier on Mount Rainier.

On the Winthrop glacier on Mount Rainier.

Summit block of Mount Hood at sunrise.

Summit block of Mount Hood at sunrise.

Exploding with sunlight near the summit of Mount Hood.

Exploding with sunlight near the summit of Mount Hood.

Share
Posted in Pictures | Leave a comment

Print This Print This   Email This Email This

Comparison between GPSMAP 64st and InReach Explorer+

We’ve been shopping around for a handheld GPS, since many climbs require the ability to retrace our route. Since I have an InReach SE, it made sense to check out the InReach Explorer+, which combines a satellite messenger with topographic maps. We also tried out the GPSMAP 64st. Both devices are by Garmin.

We took both up on our August climb of Mount Rainier. The InReach Explorer+ is nice, since it means we only have to carry one device, instead of a messenger and something with topographic maps. Unfortunately, in terms of map detail and accuracy, it is quite far behind the GPSMAP 64st.

Here are our tracks, from just before the Disappointment Cleaver to somewhere on the Emmons or Winthrop glaciers, as seen in Google Earth. First, the track recorded by the GPSMAP 64st:

Track recorded by GPSMAP 64st

Track recorded by GPSMAP 64st

And here is the track recorded by the InReach Explorer+:

Track recorded by InReach Explorer+

Track recorded by InReach Explorer+


You can see that the track on the InReach is much less uniform, and there are a few points that are actually quite far away.

In terms of detail that can be discerned on the display, the InReach also can’t compete with the GPSMAP 64st. Here is the topographic map around Camp Muir, first on the GPSMAP 64st:

Topographic map on GPSMAP 64st

Topographic map on GPSMAP 64st

And here is the same map location on the InReach Explorer+:

Topographic map on InReach Explorer+

Topographic map on InReach Explorer+

You can’t even see Muir Peak, to the east of the camp!

I think we’ll return the InReach Explorer+ and keep the GPSMAP 64st. We just have to figure out a way to keep the latter from turning on accidentally and draining the battery.

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Kaspersky SafeMoney, LastPass, and Yubikey?

I’m using Kaspersky SafeMoney to redirect requests to websites that deserve a bit more security to a sandboxed browser. I don’t have any extensions in it, except for LastPass. I think it makes sense to still get the benefits of a strong master password and different strong passwords for each site that I don’t have to remember.

Unfortunately, when I added Yubikey to the mix, LastPass started asking me for the Yubikey every time the secure browser launches.

The option to “trust this browser for 30 days” doesn’t seem to do anything anymore. Has anyone got this to work?

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Second Mount Rainier Climb

On August 4 and 5, Jenny and I climbed Mount Rainier again, this time with Tatiana, Daniel, and Ian. I’m going to write a longer trip report again, but here are a couple of 360-degree pictures from the summit, and a 360-degree video from crossing a crevasse using a ladder.

My second time on the crater rim of Mount Rainier:

Dan, Jenny, Tatiana, Ian and I, on the summit of Washington’s highest mountain:

Crossing a crevasse using a ladder:

It was another great trip!

Share
Posted in Pictures | Leave a comment

Print This Print This   Email This Email This

swagger-codegen 2.3.0 Breaks Inheritance?

For a hack day at work, I’ve been trying to integrate our code generator into the online Swagger editor and generator. Of course, I’ve been trying to use the latest version, which appears to be 2.3.0-SNAPSHOT (although I just saw there’s also a 3.0.0 branch).

Unfortunately, it seems like inheritance (which is kind of a big deal for actual software systems) is fundamentally broken in 2.3.0.

Here’s my sample spec in YAML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
swagger: "2.0"
info:
  description: "This is a sample server Petstore server.  You can find out more about     Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).      For this sample, you can use the api key `special-key` to test the authorization     filters."
  version: "1.0.0"
  title: "Swagger Petstore"
  termsOfService: "http://swagger.io/terms/"
  contact:
    email: "apiteam@swagger.io"
  license:
    name: "Apache 2.0"
    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "petstore.swagger.io"
basePath: "/v2"
tags:
- name: "pet"
  description: "Everything about your Pets"
  externalDocs:
    description: "Find out more"
    url: "http://swagger.io"
- name: "store"
  description: "Access to Petstore orders"
- name: "user"
  description: "Operations about user"
  externalDocs:
    description: "Find out more about our store"
    url: "http://swagger.io"
schemes:
- "http"
paths:
  /pet/:
    get:
      tags:
      - "pet"
      summary: "Find pet by ID"
      description: "Returns a single pet"
      operationId: "getPet"
      produces:
      - "application/xml"
      - "application/json"
      responses:
        200:
          description: "successful operation"
          schema:
            $ref: "#/definitions/Pet"
        400:
          description: "Invalid ID supplied"
        404:
          description: "Pet not found"
definitions:
  Pet:
    discriminator: petType
    required:
      - name
      - petType # required for inheritance to work
    properties:
      name:
        type: string
      petType:
        type: string
  Cat:
    allOf:
      - $ref: '#/definitions/Pet' # Cat has all properties of a Pet
      - properties: # extra properties only for cats
          huntingSkill:
            type: string
            default: lazy
            enum:
              - lazy
              - aggressive
  Dog:
    allOf:
      - $ref: '#/definitions/Pet' # Dog has all properties of a Pet
      - properties: # extra properties only for dogs
          packSize:
            description: The size of the pack the dog is from
            type: integer

Cat and Dog extend Pet. Very simple.

The older swagger editor, which reports a version number of 2.10.4 for the swagger-api/swagger-editor GitHub project, understands this and generates the expected code:

swagger-editor 2.10.4 models

swagger-editor 2.10.4 models

1
2
public class Cat extends Pet {
...

The latest editor, unfortunately, does not!

Broken Swagger Editor 2.3.0 models

Broken Swagger Editor 2.3.0 models

1
public class Cat {

You gotta be kidding, right?

Share
Posted in Code Pranger, Programming, Ramblings | Leave a comment

Print This Print This   Email This Email This

The Uneven World of Two-Factor Authentication

I’d like to say I began to adopt two-factor authentication pretty early. I’ve had it enabled for Google for a long time, I jumped on it when Amazon and Dropbox made it available, I enjoy it on several of my banking accounts, of course.

At work, I got a Yubikey a few months ago, a hardware token that is used in combination with a password. It’s the “have” part of the “something you know, something you have, and something you are” philosophy of protecting secrets. A fingerprint or iris scan would be “something you are.”

With all of the recent hacks and ransomware attacks, I decided to beef up security at home as well. I ordered two Yubikeys, a primary one and one to lock away as a backup. As primary Yubikey I chose the Yubikey NEO, which has NFC and can be used with a phone. As backup, I chose the cheap Yubikey FIDO U2F key.

Unfortunately, the results are a mixed bag.

Yubikey for LastPass requires LastPass Premium ($12 a year, plus taxes). I signed up, then found out that LastPass Premium does not seem to support the cheap Yubikey FIDO U2F key. So I couldn’t use that one as a backup for LastPass.

KeePass on Windows has a plug-in that allows a Yubikey to provide one-time pads (OTPs). Again, this doesn’t work on the cheapest key. Also, it’s a bit cumbersome, since it requires at least three presses of the Yubikey button, for three separate OTPs. Initially, I had also configured this for 2nd slot on the Yubikey, which meant I had to hold the button for three seconds, three times (the 2nd slot is triggered by a 3-second press; the first slot is triggered by a simple press of the button).

I also used KeePassDroid on my phone, and that app plain and simply does not support OTPs at all. I could have programmed the NEO for a static password, though.

There is a newer app, KeePass2Android, that does apparently support getting OTPs from a Yubikey NEO, but I haven’t tried it yet.

LastPass on Android does support the NEO, but with NFC, you cannot switch which slot is going to be used. By default, it’s always slot 1. That means I can’t use NFC for both LastPass and KeePass2Android, because both would be using the same slot, and that doesn’t work.

So, I have hardware two-factor authentication in a bunch of places now, but I’m not sure I’ve added that much security. I strengthened some of my most important passwords, though. Maybe that “something you know” part is still most important after all.

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Mount Rainier Go/No-Go Decision Matrix

This is the matrix we used for making go/no-go decisions for our Mount Rainier summit bids:

Weather Scoring

Weather Scoring

Decision Matrix: 5 or lower means "go," 6 or 7 means "cautious go," and 8 or higher means "no-go" for us.

Decision Matrix: 5 or lower means “go,” 6 or 7 means “cautious go,” and 8 or higher means “no-go” for us.

It’s copied from the excellent route briefs the Mount Rainier climbing rangers have released:

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Mount Rainier Trip Summary

Trailhead to camp:

  • Leave at trailhead: 6:30 AM @ 5,400 feet
  • Arrive at camp: 12:30 PM @ 10,000 feet
  • 6 hours, 3.9 miles (6.3 km), 4,600 feet gain (1400 m, or about 394 building floors)

Camp to summit:

  • Leave at camp: 1:30 AM @ 10,000 feet
  • Arrive at summit: 8:30 PM @ 14,411 feet
  • 7 hours, 3.3 miles (5.3 km), 4,411 feet gain (1344 m, or about 378 building floors)

Summit to camp:

  • Leave at camp: 9:15 AM
  • Arrive at summit: 14:15 PM
  • 5 hours, 3.3 miles (5.3 km)

Camp to trailhead:

  • Leave at camp: 3:00 PM
  • Arrive at trailhead: 7:00 PM
  • 4 hours, 3.9 miles (6.3 km)

Round-trip:

  • 22 hours, 14.4 miles (23 km), 9,011 feet gain (2747 m, or about 772 building floors)
Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Mount Rainier Trip Report

On June 24 and 25, 2017, Jenny and I and our friend Vicki climbed Mount Rainier.

Jenny and I drove out on Friday afternoon and picked up our wilderness camping permit for Camp Muir. We had dinner at the Paradise visitor center (a first for me), and then drove down to Cougar Rock campground to sleep. We drank half a gallon of water, peed like crazy, but got about 7.5 hours sleep in and felt very hydrated in the morning.

Jenny at our camp site on Friday, excited about her fanny pack.

Jenny at our camp site on Friday, excited about her fanny pack.

At 4 AM, we packed up our tent and drove back to Paradise. Once there, we finished packing our backpacks and waited for Vicki to arrive, who was on time. At 6:30, we started hiking up towards Camp Muir, just as planned.

Jenny says "we go there" on Saturday morning.

Jenny says “we go there” on Saturday morning.

There was snow all the way down to Paradise, and the summer route with the switchbacks past Panorama Point had not been cleared yet, which meant we had to ascend the steep slope just below the toilet at Panorama Point (which actually exists; I had never seen it before, since I had always taken the High Skyline trail). That was the only steep part, though. After that, we were on the long but relatively gentle Muir snowfield. It took us about 6 hours to get to Camp Muir. At around 11, we heard from our three skier companions who were going to make up the second rope team.

Jenny and Vicki on the hike up, still close to Paradise.

Jenny and Vicki on the hike up, still close to Paradise.

Jenny, Vicki, and some other folks heading up the steep slope to Panorama Point.

Jenny, Vicki, and some other folks heading up the steep slope to Panorama Point.

Jenny and Vicki near Pebble Creek, the beginning of the Muir Snowfield.

Jenny and Vicki near Pebble Creek, the beginning of the Muir Snowfield.

Heading to basecamp, with a heavy backpack (picture by Jenny).

Heading to basecamp, with a heavy backpack (picture by Jenny).

At Camp Muir, we set up our tents and were pretty out of breath, due to the thin air. Vicki hadn’t slept much the night before, and took a nap in her tent under the beating sun. Jenny and I spent about 3 hours melting snow for water, producing about 2 gallons. We also received about another gallon in donations from climbers who were departing Camp Muir. In the middle of this task, we also ate some chicken with rice and teriyaki chicken freeze-dried meals. Then we went to sleep. Or tried to. It was hot and loud. Our earplugs were very useful.

Line of tents at Camp Muir. Ours are the second and third from the left.

Line of tents at Camp Muir. Ours are the second and third from the left.

Taking a breather, after having set up our tent (picture by Jenny).

Taking a breather, after having set up our tent (picture by Jenny).

At Camp Muir, melting snow (picture by Vicki).

At Camp Muir, melting snow (picture by Vicki).

We hadn’t heard from the skiers, and we were getting worried that something had happened and that they had turned around, but there wasn’t really anything we could do. Either they’d show up or not. Around 4:30 PM, I believe, I managed to call them on the two-way radio, and around 5 PM, perhaps, they were at our camp and setting up their tents. They weren’t too happy with our plan to get up at 11 PM and start hiking at midnight, so we agreed on an alpine start half an hour later.

Jenny and I got up at 11 PM nonetheless and got our rope and gear ready. I put an alpine butterfly into the middle of my 40-meter rope, two alpine butterflies about 6 or 7 meters to the side from there, and then coiled up the rest of the rope, to be carried by the climbers at both ends. Fortunately, the glacier rope is really light. Vicki, Jenny and I were ready to go at half past midnight, as agreed, but the other team took until a little after 1 AM.

Jenny putting on her gaiters and crampons just before midnight.

Jenny putting on her gaiters and crampons just before midnight.

Ready to go at half past midnight!

Ready to go at half past midnight!

We followed a chain of headlamps in the distance over the Cowlitz Glacier towards the Cathedral Gap, crossed over onto the Ingraham Glacier, and headed up towards the Ingraham Flats. It was dark, our path only lit by our headlamps, and while we were pretty sure there were no crevasses on the boot path, I still swept my gaze left and right while I was in the lead.

Around 11,300 feet, the path turned to the north and towards the Disappointment Cleaver. Some hand lines were attached, but we didn’t really need them. Ahead of us, there was a 3-person rope team that had used the entire length of a 30-meter rope, with 13 meters or so between climbers. They were relatively slow, and occasionally dislodged little rocks.

About half-way up the cleaver, they sat down to adjust their rope spacing. With their headlamps shining straight into my eyes, I had trouble finding the route for a second, and when we spotted it, we realized I had ascended too far. We swapped leads, since Vicki was right around the correct spots, and she lead most of the way to the summit.

Approximate route, from http://mountrainierclimbing.blogspot.com/2017/06/disappointment-cleaver-6252017.html

Approximate route, from http://mountrainierclimbing.blogspot.com/2017/06/disappointment-cleaver-6252017.html

On top of the Disappointment Cleaver, our second rope team told us that, unfortunately, one of the climbers had altitude sickness. We polled every single person if it was okay for Vicki, Jenny and me to continue. I felt a bit bad, but the Disappointment Cleaver route had been pretty simple up to that point, and there were plenty of other climbers around who could help. On the Emmons route, we all would have had to descend, to make sure the second rope team doesn’t get in trouble on the way down (and to make sure we don’t get in trouble by ourselves on the way up).

Mount  Adams from above the Disappointment Cleaver (picture by Phil).

Mount Adams from above the Disappointment Cleaver (picture by Phil).

The three of us continued by ourselves, and at around 12,800 feet arrived at what was arguably the most interesting feature of the route. First, we observed the sunrise, painting the mountain in a warm, orange glow. Then we followed a long detour towards the north to avoid a serac, to a point where we could actually see Mount Ruth, and almost see Camp Schurman. On our left, there was the steep serac; on our right, there were crevasses. We carefully but quickly made our way through this fascinating area (not many pictures of this, but here’s a video of our descent through the same stretch).

Sunrise at 12,800 feet (picture by Jenny).

Sunrise at 12,800 feet (picture by Jenny).

Vicki, leading our rope team at sunrise (picture by Jenny).

Vicki, leading our rope team at sunrise (picture by Jenny).

I was bringing up the rear at this time. Behind me is another team (picture by Jenny).

I was bringing up the rear at this time. Behind me is another team (picture by Jenny).

Icefall above us at about 12,800 feet (picture by Vicki).

Icefall above us at about 12,800 feet (picture by Vicki).

There were pickets and carabiners, but we decided to not use them, since it would slow us down. This was probably a mistake. We either should have clipped into the fixed protection, or we should have unroped. Oh well… we didn’t fall.

After the detour, we steadily headed up on the mountain to about 13,000 feet, at which point the switchbacks became steeper. A bit higher still, maybe at 13,500 feet, we switched leads again, and I lead our team to the crater rim at 14,000 feet.

Jenny and Vicki on a switchback near 13,000 feet.

Jenny and Vicki on a switchback near 13,000 feet.

Re-applying sunscreen. Still got a bit burned (picture by Jenny).

Re-applying sunscreen. Still got a bit burned (picture by Jenny).

It was getting pretty hot. I'm pretty sure the dark peak in the foreground on the left is Mount Ruth.

It was getting pretty hot. I’m pretty sure the dark peak in the foreground on the left is Mount Ruth.

The trail behind us above 13,000 feet.

The trail behind us above 13,000 feet.

Little Tahoma appearing very little from the upper mountain.

Little Tahoma appearing very little from the upper mountain.

Little Tahoma from the upper mountain (picture by Vicky).

Little Tahoma from the upper mountain (picture by Vicky).

Ascending Mount Rainier, at about 13,000 feet:

It was a remarkable sight. The crater was much larger than I had thought, the size of several football fields. There was no wind. It was absolutely tranquil. We dropped our packs and unroped, ate and drank a bit, and then headed across the crater to the Columbia Crest, Mount Rainier’s true summit. We reached it at 8:29 AM, after a bit under seven and a half hours of hiking. 9 AM would have been our turn-around time.

"I made it!" (Picture by Jenny)

“I made it!” (Picture by Jenny)

Vicki, relieved at the crater (picture by Jenny).

Vicki, relieved at the crater (picture by Jenny).

Jenny and I shortly after having reached the crater at 14,000 feet (picture by Vicki).

Jenny and I shortly after having reached the crater at 14,000 feet (picture by Vicki).

In the crater of Mount Rainier, at 14,000 feet:

The views were incredible. We could see the Olympics and Mount Olympus clearly. Baker and Shuksan were there. Glacier Peak. The Stuart range. Mount Adams. Mount Hood and Mount Jefferson. And Mount St. Helens. Absolutely beautiful.

Jenny has reached the Columbia Crest!

Jenny has reached the Columbia Crest!

Mount Adams, as seen from Columbia Crest (picture by Vicki).

Mount Adams, as seen from Columbia Crest (picture by Vicki).

Jenny, Vicki and I on the summit, with Liberty Cap in the background.

Jenny, Vicki and I on the summit, with Liberty Cap in the background.

Vicki, Jenny and I on the Columbia Crest, Mount Rainier's summit.

Vicki, Jenny and I on the Columbia Crest, Mount Rainier’s summit.

On the summit of Mount Rainier.

On the summit of Mount Rainier.

On the Columbia Crest, the summit of Mount Rainier:

We took some pictures and looked for the summit register, but couldn’t find it. We didn’t care too much. We returned to our packs via the south rim and good views of Mount Adams, ate a bit more, and then roped up for our descent.

Jenny and I on the descent, high up on the mountain (picture by Vicki).

Jenny and I on the descent, high up on the mountain (picture by Vicki).

I lead the first half down, right to the top of the Disappointment Cleaver. The snow had got quite soft by now, and our going was slow. Before the cleaver, we switched up our positions, and had Jenny lead, with Vicki in the middle, and myself taking up the rear. The cleaver was a bit tricky, but I must have bad memory, because it was much shorter than I had expected. Good! We cleared the cleaver and the icefall called Icebox quickly and headed back towards Ingraham Flats.

While on the cleaver, our second rope team tried to radio us from camp, but we couldn’t respond, the situation was just too difficult to stop and radio. Once we were back on the Ingraham Glacier, we tried to call back, but they must have already descended towards Paradise.

We hopped over the Cathedral Gap again and were back on the Cowlitz Glacier. At this point, I must admit, I ran a bit out of energy. When we had stopped to snack, I had always spent some time with group tasks, such as looking at maps or trying to reach our second rope team, and I hadn’t eaten enough. I was dead-tired by the time we reached Camp Muir again. It had taken us about four hours to get back to camp.

We ate — the best Snickers bar of my life! — and took a brief nap, then packed up our tents. At 3 PM, we started the hike back down. Unfortunately, glissading didn’t quite work on the upper Muir Snowfield, but we hit a few decent glissades farther down. Just a few minutes after 7 PM, we were back at our cars.

The hike out, with all our gear in our packs again, was long, but we made it.

The hike out, with all our gear in our packs again, was long, but we made it.

Jenny and I ate at the Basecamp Bar and Grill, and reached our home in Seattle at 11 PM. We had been up for 24 hours. 24 amazing hours.

Now, almost three days later, I still haven’t come down the mountain. I’m still high.

Jenny and I will most likely try to go up again later this year, on the Emmons route, if possible. And we’ve already signed up for a three-day climb this next weekend, to Dome Peak, Washington’s 21st highest mountain (according to the Bulger list).

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

We Climbed Mount Rainier

On Sunday, June 25, 2017, our friend Vicki, Jenny, and I summited Mount Rainier, the tallest mountain in Washington state. Mount Rainier is the most prominent mountain in the contiguous US (only Denali in Alaska and Mauna Kea on Hawaii are more prominent), and also the most glaciated mountain in the lower 48.

This has been Jenny’s and my main project for the last four months, or so, and took hours, days, and weeks of organizing, conditioning, and planning. We went to Glacier Basin and Camp Schurman in preparation for it, we climbed Silver Star Mountain and The Fin to get used to double-header weekends, and we summited Colchuck Peak to get used to steep glaciers. I hadn’t written about any of this, because I wasn’t sure that we would reach our goal of scaling Mount Rainier.

But now we have reached Mount Rainier’s summit, at 14,411 feet. Now I’ll start to slowly post more reports and pictures about the last few months.

Ascending Mount Rainier, at about 13,000 feet:

In the crater of Mount Rainier, at 14,000 feet:

On the Columbia Crest, the summit of Mount Rainier:

On the summit of Mount Rainier.

On the summit of Mount Rainier.

Share
Posted in Pictures | 1 Comment

Print This Print This   Email This Email This

Invoking Maven from Java

On one of my work projects right now, I’m adding unit tests to a code generator. The easiest way to make sure the generated code works is to run mvn clean install. Of course, I want to do that from within the unit test and fail the test if the Maven run fails.

Fortunately, there’s Maven Invoker. Here’s how to use it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   private void invokeMavenInstall(File file) throws MavenInvocationException, CommandLineException {
        InvocationRequest request = new DefaultInvocationRequest();
        request.setPomFile(file);
        request.setBatchMode(true);
        request.setDebug(true);

        // this prevents M2_HOME from being added in MavenCommandLineBuilder.setShellEnvironment, which then fails to
        // start mvn with "Error: Could not find or load main class org.codehaus.plexus.classworlds.launcher.Launcher"
        request.setShellEnvironmentInherited(false);
        request.setGoals(Collections.singletonList("install"));

        Invoker invoker = new DefaultInvoker();
        invoker.setMavenHome(new File(System.getenv("M3_HOME")));
        invoker.setOutputHandler(new SystemOutHandler());
        invoker.setErrorHandler(new SystemOutHandler());
        invoker.setLogger(new PrintStreamLogger(System.out, InvokerLogger.DEBUG));
        InvocationResult result = invoker.execute(request);
        if (result.getExecutionException() != null) {
            throw result.getExecutionException();
        }
        assertEquals(0, result.getExitCode());
    }
}

The interesting bit here is the request.setShellEnvironmentInherited(false); line. Without it, the MavenCommandLineBuilder.setShellEnvironment method adds an environment variable M2_HOME to it, which prevents Maven from starting up.

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Domain Housekeeping

I’m in the process of doing some housekeeping with my domains. In particular, I will drop the domains concutest.org and superscalar.org, among a few other, less frequently used domains.

superscalar.org used to point to my old personal homepage, but since I hadn’t updated it in a while, I just pointed pointed it to my professional homepage, which had been available at www.ricken.us for years already. superscalar.org will go away, www.ricken.us will stay.

The story for concutest.org is a little more complex. It is the domain for my research project Concutest, but also had a subdomain mint.concutest.org for the Mint multi-stage Java research project I was a part of. There was also blog.concutest.org, which was a synonym for this blog at www.concurrentaffair.org; community.concutest.org, which was part of a community effort to identify concurrency invariants of the Java API, which I ended some time in 2016; and drjava.concutest.org, which hosted an internal DrJava blog, also taken offline in 2016.

I have create two subdomains for Concutest and Mint:

These subdomains are already active. I hope this will not cause too much inconvenience.

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Query API with startId, endId, and count

I recently had to write a query API for items in a service. Each item has a numerical, consecutively increasing integer as identifier. Therefore, it made sense to allow callers to specify a range. It also makes sense to specify the number of results to be returned, since we don’t want the result to get too big. The numeric identifiers actually provide a natural way of pagination.

Specifying a range using startId and endId is simple. So is startId and count. But endId and count is tricky. We can’t just start at endId - count + 1, because some of the items in that range might get filtered out because of other query settings, and then we’d return fewer than count items.

Okay, so we include all items, filter first, and then return the last count items. That works, but that creates an unnecessarily long temporary list, which may cause memory problems.

I solved this by using a NavigableMap, and when endId and count are used together, I process a reverse view of the Map using NavigableMap.descendingMap(). Then I can simply limit the number of items and after filtering reverse the list again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Store {
  private final NavigableMap<Integer, CachedItem> m_cache;

  /**
   * Get a list of items.
   * @param includePredicate predicate that returns true if an item should be included in the list
   * @param reverse true to process the items in reverse order
   * @param maxCount optional maximum size of the result
   * @return list of items
   */

  public List<Item> getItems(Predicate<Item> includePredicate, boolean reverse, Optional<Integer> maxCount) {
    // NOTE: the cache currently holds all items ever created in memory... how could that possibly go wrong?

    Stream<Item> stream = (reverse ? m_cache.descendingMap() : m_cache).values().stream()
            .map(cached -> cached.copyItem())
            .filter(includePredicate);

    if (maxCount.isPresent()) {
      stream = stream.limit(maxCount.get());
    }

    return ImmutableList.copyOf(stream.collect(Collectors.toList()));
  }
}

class Controller {
  @RequestMapping(value="/items", method= RequestMethod.GET)
  public Response queryItems(@PathVariable String apiVersion,
                             @RequestParam(value = "filter", required = false) String[] filter,
                             @RequestParam(value = "start", required = false) @Min(1) Integer start,
                             @RequestParam(value = "end", required = false) @Min(1) Integer end,
                             @RequestParam(value = "count", required = false) Integer count,
                             HttpServletRequest request) {
    Response rval = new Response();

    if (start != null && end != null && count != null) {
      throw new IllegalArgumentException("Cannot use start, end, and count all together");
    }

    Predicate<Item> statusPredicate = getStatusPredicate(filter);
    Predicate<Item> idPredicate = getIdPredicate(Optional.ofNullable(start), Optional.ofNullable(end));

    // if we are using an end value and count, we have to process the list in reverse to make sure we accumulate
    // the correct number of jobs
    boolean reverse = (count != null) && (end != null);

    List<Item> items = m_asyncJobStore.getItems(statusPredicate.and(idPredicate),
                reverse, Optional.ofNullable(count));

    if (reverse) {
      items = Lists.reverse(items);
    }

    ItemsQueryType itemsQuery = new ItemsQueryType();
    itemsQuery.setItems(items);
    rval.setItemsQuery(itemsQuery);

    return rval;
  }

  @VisibleForTesting
  static Predicate<AsyncJobType> getIdPredicate(Optional<Integer> start, Optional<Integer> end) {
    Predicate<AsyncJobType> idPredicate = asyncJobType -> true;
    if (start.isPresent()) {
      idPredicate = idPredicate.and(asyncJobType -> asyncJobType.getId() >= start.get());
    }
    if (end.isPresent()) {
      idPredicate = idPredicate.and(asyncJobType -> asyncJobType.getId() <= end.get());
    }
    return idPredicate;
  }

  @VisibleForTesting
  static Predicate<AsyncJobType> getStatusPredicate(String[] filter) {
    Predicate<AsyncJobType> statusPredicate;
    if (filter == null || filter.length == 0) {
      statusPredicate = asyncJobType -> true; // include everything
    } else {
      statusPredicate = asyncJobType -> {
        boolean include = false;
        for(String status: filter) {
          if (status.equalsIgnoreCase(asyncJobType.getStatus().name())) {
            include = true;
            break;
          }
        }
        return include;
      };
    }
    return statusPredicate;
  }
}

I’m pretty happy with this solution for returning the right number of items, no matter how the range is specified.

The big problem, however, is that the class already caches everything in memory before I added the query API. Therefore, this query change could be as well-written as possible, and the cache would still blow up the heap at some point.

Unfortunately, many people don’t seem to think at that scale…

Share
Posted in Code Pranger, Programming | Leave a comment

Print This Print This   Email This Email This

Backing Out Changes and Fixing Things

Let’s say we have three people: Adam, Bob, and Charlie.

Adam submits a change, which later turns out to be bad.
Bob submits a somewhat unrelated change.
Charlie realizes that Adam’s change is bad and backs it out.
That, however, breaks Bob’s unit tests, because Bob’s mock expectations are based on Adam’s change.
Therefore, Charlie backs out Bob’s change as well.

Who has to re-do the changes and fix things?

Adam definitely needs to fix his shit, right?

But should Bob have to re-do his work? Or should Adam have to fix that as well? Or maybe Charlie, because in backing out Adam’s change, Charlie broke Bob’s tests?

Sigh… hi, I’m Bob.

Share
Posted in Code Pranger | Leave a comment

Print This Print This   Email This Email This

Hex Mountain on Snowshoes

Jenny coming up the "Ridge from Hell."

Jenny coming up the “Ridge from Hell.”

Happy to be in the snow together!

Happy to be in the snow together!

First clear view of Hex Mountain's summit.

First clear view of Hex Mountain’s summit.

Looking northwest from about 4000 feet.

Looking northwest from about 4000 feet.

Jenny on the last push before the summit.

Jenny on the last push before the summit.

Looking towards Mt. Rainier. My phone's camera couldn't capture it, but I don't think I've ever seen Little Tahoma this clear from that far away.

Looking towards Mt. Rainier. My phone’s camera couldn’t capture it, but I don’t think I’ve ever seen Little Tahoma this clear from that far away.

Looking north towards Mount Stuart, Sherpa Peak, Colchuck Peak, Dragontail Peak, and Little Annapurna.

Looking north towards Mount Stuart, Sherpa Peak, Colchuck Peak, Dragontail Peak, and Little Annapurna.

On the summit of Hex Mountain, with Rainier out!

On the summit of Hex Mountain, with Rainier out!

Approximate route to the summit and back. We went clockwise. The part just west of Point 3501 was the hardest part.

Approximate route to the summit and back. We went clockwise. The part just west of Point 3501 was the hardest part.

Share
Posted in Pictures | Leave a comment

Print This Print This   Email This Email This

Guye Peak GPS Track

Here’s the GPS track for our winter scramble of Guye Peak, courtesy of our climb leader John.

Guye Peak track

Guye Peak track

Share
Posted in Pictures | Leave a comment

Print This Print This   Email This Email This

Capturing a Runnable with JMockit and Executing It

At work, we use JMockit. I’m far more comfortable with Mockito, which made it very easy for me to understand how to capture a Runnable passed in, execute it, and then check that certain calls were made. I have always struggled with doing that with JMockit, but I think I finally figured it out:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class DemoWithRunnable {
    public interface Dependency {
        int someMethod();
        void otherMethod(int i);
    }

    private final Dependency dependency;
    private final ExecutorService executor;

    public DemoWithRunnable(Dependency d, ExecutorService e) {
        this.dependency = d;
        this.executor = e;
    }

    public void doSomething() {
        executor.submit(new Runnable() {
            public void run() {
                int i = dependency.someMethod();
                dependency.otherMethod(i);
            }
        });
    }
}

What I want to do here in a test, of course, is:

  1. Check that we call ExecutorService.submit
  2. Run the Runnable that was passed to submit
  3. Mock someMethod and return some value, and
  4. Check that otherMethod was called with the returned value.

And I think this is how you do it in JMockit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class DemoWithRunnableTest {
    @Mocked DemoWithRunnable.Dependency d;

    @Mocked ExecutorService e;

    @Test
    public void testDoSomething() {
        DemoWithRunnable impl = new DemoWithRunnable(d, e);

        impl.doSomething();

        List<Runnable> r = new ArrayList<>();

        // capture the Runnable
        new Verifications() {{
            e.submit(withCapture(r));
        }};

        // add new expectations for code in the Runnable, with return values
        new Expectations() {{
            d.someMethod(); result = 42;
        }};

        // run the Runnable
        r.get(0).run();

        // verifications for the code in the Runnable
        new Verifications() {{
            d.otherMethod(42);
        }};
    }
}
Share
Posted in Programming | Leave a comment

Print This Print This   Email This Email This

Guye Peak Winter Scramble with the Mountaineers

Jenny and I went on a winter scramble up Guye Peak with other Mountaineers on Saturday. It was the first new peak of the year.

It was a beautiful day, but very cold: 13 Fahrenheit/-10 Celsius. There was no wind and a lot of deep powder snow. We wore snowshoes basically from the trailhead. We roughly followed Commonwealth Creek up toward Cave Ridge and then hooked back around to the summit of Guye Peak.

We noticed, though, that our snowshoes, purchased cheaply at Costco, are unsuitable for climbing. As soon as there is any elevation, we can’t kick steps or plunge-step, we basically float, almost as if on skis. Everyone else had MSR snowshoes. I think we’ll have to check if we need some of those.

Guye Peak from below.

Guye Peak from below.

On the Guye Peak summit, looking down toward Chair Peak and Source Lake.

On the Guye Peak summit, looking down toward Chair Peak and Source Lake.

Mount Rainier from Guye Peak.

Mount Rainier from Guye Peak.

Jenny on the summit cornice of Guye Peak.

Jenny on the summit cornice of Guye Peak.

I made it to the summit too!

I made it to the summit too!

After coming down from the summit, about to have a bite.

After coming down from the summit, about to have a bite.

Jenny, about to descend the crux.

Jenny, about to descend the crux.

All smiles. I knew the crux wouldn't be too bad (contrary to the the snowshoe descent).

All smiles. I knew the crux wouldn’t be too bad (contrary to the the snowshoe descent).

Share
Posted in Pictures | Leave a comment

Print This Print This   Email This Email This

Toyota Corolla 2017 Loaner Car

At the end of 2016, I had a 2017 Toyota Corolla loaner car while my own 2011 Toyota Camry was being repaired (a damaged wheel speed sensor from getting stuck in deep snow, which rendered ABS and traction control inoperable).

The 2017 Corolla had a lot more gadgets than my own car. Most significantly, it was equipped with adaptive cruise control, lane departure warning, Bluetooth calling and text-to-speech messaging, and voice control.

I didn’t do much highway driving, but I could see that the adaptive cruise control would be useful. In city traffic, or even on Seattle’s congested freeways, it wasn’t that useful: Many times, a car merged in ahead of me, because the distance was relatively large, and then the cruise control slowed down. But at longer distances, in the country, I think it would work well.

Lane departure warning was also interesting. IT could beep at the driver or even steer a little to stay within the lane. In city traffic, again, though, I often found it a little over-reactive. And often it didn’t pick up the lanes in the city, because they were faded, or the curb was a little far away, or there was a raised median. But again, I think on freeways it would probably be useful.

I did like the Bluetooth integration. The car had all my contacts on my phone, the call history, and I could even dial by voice. It also asked me if I wanted text messages read out aloud. One of the possible ring tones included saying the caller name, if it was in the contacts list. Very useful.

Voice control, on the other hand, aside from making calls, seemed pretty useless, though. And the voice recognition was far worse than that of Alexa in Amazon Echo devices.

The Corolla was also much louder, the engine jerkier, and the controls less intuitive than those of my 2011 Camry. I will never understand, for example, why people choose up/down controls for temperature or fan speed, with a numeric display. Knobs you can turn are so much faster and simpler to use while driving, without looking. And that’s important in a car.

All in all, I’m very glad that I have my old car back now.

Share
Posted in Uncategorized | Leave a comment

Print This Print This   Email This Email This

Hike 40 of 2016: Another Trip Up Mount Si

Yesterday, on December 30, we were kicked out of work because we were moved from one building to another. Forced day off. I took the opportunity to complete hike number 40 of the year. Unfortunately, Jenny couldn’t get the day off from work, and I had to go by myself.

The avalanche forecast predicted considerable danger, which limited my choices a bit. I also didn’t want to stay out for too long, since we had planned to have a friend over in the evening.

I therefore defaulted to Mount Si, the old Mountaineers treadmill. But it was a gorgeous day. Starting at about 1.6 miles, there was snow. I don’t have microspikes, only full crampons, and that seemed like overkill, so I moved a bit more cautiously just in my boots. Mount Teneriffe and a very snowy Mailbox Peak showed off their beauty.

I’m quite happy that I made it to 40 hikes this year. That’s 12 more than in 2015, and 2015 was my most active year before.

20161230_100130

20161230_101505

20161230_102633

20161230_104925

20161230_110924

Share
Posted in Pictures | Leave a comment

Print This Print This   Email This Email This