MySQL in Docker with Java Hibernate

docker mysql logos

Recently, I started working on a new server project at work, and wanted to be able to run a local dev environment with Docker. This has become my normal flow for a couple of server projects because of how easy Docker is to work with, and especially for the fact that I don’t need to set up any of the supporting structure on my machine to run the server. I really dislike needing to install things like Tomcat, Apache, or a MySQL server locally on my machine for development. Every time one of those things needs to be installed, I know that it’s one more thing on my machine that I need to maintain, and one more thing that could break and cause me to dump hours into fixing. With Docker, I don’t need to care about that, I can fire something off with a repeatable, programatic configuration that may be ephemeral, and disappear when I’m done with it.

My work project has two parts, first is the Java part that uses JPA/Hibernate as an ORM layer which talks to a MySQL database. While the Java portion of this was fairly straightforward, the MySQL part was not.

Outline

  • Source code
  • Basic SQL structure
  • Java code
  • Java Docker scripts
  • MySQL Docker scripts
  • Tying it together

SQL Structure

Here we have a very basic relational structure in MySQL. We have Users, Tags, and UserTags to tie the two together. If you’re familiar with SQL, this should be familiar to you.

Java code

I have a bit more Java code that I could share here, but this is probably the most important bit, which is setting up the connection. If you haven’t set things up correctly, establishing the connection will be the first thing to fail. If you’re interested in seeing the actual JPA/Hibernate entities, check out the source code.

Java Docker scripts

The Java Dockerfile is dead simple, basically, we’re just copying a jar to a stock java8 Docker image, and running that jar.

MySQL Docker scripts

Next up, is the MySQL Docker file. It’s not terrible, but there are a couple of scripts, and something a little non-obvious going on.

When I started poking around with MySQL and Docker, I wanted to use the official MySQL base, as opposed to rolling my own. While I did find a good example run command in the CoreOS documentation, I didn’t find one that used a Dockerfile, and a SQL script to set up the database. So, I had to start digging.

One of the first things that I learned was that the mysqld command in the CMD line does not run the system daemon directly. Instead, it is run through the entrypoint wrapper. If you want to do custom things to MySQL during its first run, or startup, then you’ll want to modify the MySQL entrypoint script. The one that I used is shown below with my comments inline. Basically, I wanted to add a parameter for my setup SQL script.

You’ll see on line 84 where I added the initialization from the script I passed in. You will also note that I added line 103, turning on show_compatibility_56 in /etc/mysql/my.cnf. This was the solution to a problem that I had run into where whenever I tried to connect from my Java app as a non-root user, I was given an error like the following:

SELECT command denied to user 'test'@'host' for table 'session_variables'

I ran across the initial solution on StackOverflow, and was able to implement it in this entrypoint script.

Tying it together

Below is my buildrun.sh script, which I use as a one-liner for setting everything up and running it. For the purposes of this post, it makes some sense, though for my actual implementation, I split the Java app from the SQL instance, so that I can iterate on my Java code without needing to constantly setup and teardown a MySQL instance. It’s also set up such that both instances are ephemeral, since I get annoyed when Docker leaves around dozens of 300MB files on my relatively constrained SSD.

The other issue that is common with running MySQL in Docker is one of data persistence. Unless you are keeping around the instances that you run, you’re going to lose data in between runs if you don’t do something to handle that. Data persistence is obviously an important aspect of a database. There are two options for keeping your data persistent:

  1. Passing in a volume from host
  2. Creating a share Docker volume

On my Mac, I had trouble with using a host volume for MySQL, I kept getting permissions errors, and it would fail to run after that. So, I opted for creating the shared Docker volume. To me, using a host volume would still be preferable, since I’d really like to be able to commit the data directory to my repository, so that I can share it with the code, but oh well. (Yes, I know that sounds strange and bad, but in my particular case, it does make some sense.)

The shared Docker volume is created on line 22, and explained in the Docker documentation. In our run command, we call that volume on line 42.

Conclusion

This was a bit more involved than I suspected, but I think that it was worth spending the time to get this up and running. The workflow is much nicer than needing to do this stuff natively, or constantly deploying to a remote host. When I came into the office on Monday morning, after getting this up and running over the weekend, the Docker portions without issue, and it saved me a ton of time. Hopefully this information saves you a bit of time as well.

GitHub repo with source for this whole project.

Review of the Expobar Office Lever Plus

I recently took the plunge on a new espresso machine. I had written a review of my previous machine, and how I had been frustrated and disappointed with it. After going without a real espresso machine for nearly two years, I thought that it was finally time to get back into it.

I stumbled on the Expobar Office Lever, and realized that it was exactly what I was looking for. First, it was certainly a powerful machine, that I would need to grow into a bit. Second, it was clearly well-built, and repairable, and most likely would last me at least 10 years. Checking the reviews, it was also a popular model, with lots of great reviews. As I was reading the reviews, (and reviews for other similar machines) I noticed that several people mentioned that having the option to plumb in water was valuable, so I found that the Office Lever Plus had that option.

expobar machine front view expobar machine top view

I bought the machine from Whole Latte Love, and it arrived within a week. The first shot that I pulled on it came out exactly how I wanted, I didn’t need to do anything to dial it in. (I somehow happened to have the grind setting just right.)

expobar machine

After the first week, I decided to go ahead and plumb in water. As it turns out, that was worth doing. I had run through about two gallons of water in the first week, and it’s not the easiest to refill. The top cover lifts off, and the tank needs to be removed from there. With all of the extra flushing before and after shots, you’ll go through a lot of water with this machine. I also drilled a hole in the bottom of the drip tray and added the hose for the drip tray drain, that lets the drain water into the sink.

expobar gaugges bottomless portafilter on-off switch

I’ve always wanted a bottomless portafilter. I also love the analog pressure gauges, and the on-off switch is quite satisfying.

latte art

I have never had a machine that was competent at steaming before, and this thing is excellent at it. As such, I need to work on my technique and latte art.

Cons

  • Requires 10-30 minutes heat up time
  • Requires lots of flushing before and after a shot
  • Refilling the water reservoir is a pain
  • Requires dumping drip tray nearly daily

Pros

  • It’s a tank, quality parts were used
  • Delivers high quality extractions
  • Great steamer, possibly endless (though I have not tested)
  • Hot water line is great for heating the cup
  • Easy to pulmb in water line
  • Drip tray can be converted to drain to the sink

Conclusion

This is a machine that I will be holding onto for years. I love what it can do, and the results that I’m getting with it before even spending much time with it. I really love that I have a machine that will allow me to learn how to steam milk properly, hopefully one day, I’ll be able to rival local coffee shops with my lattes - I’m already there on the espresso. In short, not only is it a great machine that will hopefully be my last, but I don’t really see a reason to go out for coffee. I can do as well at home.

Eating Labor Day Weekend

This weekend was an amazing weekend for food at home. I didn’t grill at all, but we ate well. I’ll share a couple of the meals that I made, one from Friday night, and the other from Monday’s dinner. I did make pancakes somewhere in there, but forgot about grabbing a photo.

Endive Salad

chive oil chive oil

First up was Friday night’s dinner, a modified Warm Radicchio Salad, with endives instead of radicchio. The salad was topped with a freshly made chive oil.

endive salad

Fruit Cumble

I made a Simple Fruit Crumble for dessert on both Friday and Monday. Friday’s was berries, Monday’s was plums.

berry crumble

Squash Salad

squash salad

I made a Warm Squash Salad for dinner on Monday, again, using the chive oil.

Kebabs

Our friends brought us some homemade kebabs to go with Monday’s dinner.

kebabs

Beet Risotto

The main course on Monday was Beet Risotto, which involved juicing a raw beet. Yes, everything in the kitchen was red after that.

beet risotto

A life you don't need a vacation from

A friend recently posted the following quote on Facebook:

“My goal is to create a life I don’t need a vacation from.”

My first reaction was, “that is 100% my goal”.

After having a child, and starting a real family, I’ve started thinking a lot more about longer term. There are two things for me that play into this.

The first component for me is burnout. Marissa Mayer wrote a piece for Bloomberg on burnout that I thought was really interesting, and insightful. Kent Nguyen wrote a follow-up piece that I also identified with. These two articles established, and built on the idea that burnout is a build up of resentment caused by giving up doing the things that you love. You can avoid burnout by identifying the things that are important to you, and protecting those things. If you love playing roller soccer every Wednesday night, and not going throws off the rest of your week, then don’t let your boss keep you working through it.

If you love your morning workout, wake up early enough to do it, and don’t start work until you have worked out (yes, you can wake up at 6am, it’s possible). If you’re waking up extra early to work out, then don’t stay at work until 1am, get to sleep early enough that you’ll be able to wake up ready to go. You can’t forego both sleep and things that you love for an extended period of time and not burn out.

The second part comes in the form of a parable that I read somewhere that’s stuck with me.


fisherman

There was once a businessman who was sitting by the beach in a small Brazilian village. As he sat, he saw a Brazilian fisherman rowing a small boat towards the shore having caught quite few big fish.

The businessman was impressed and asked the fisherman, “How long does it take you to catch so many fish?” The fisherman replied, “Oh, just a short while.” “Then why don’t you stay longer at sea and catch even more?” The businessman was astonished. “This is enough to feed my whole family,” the fisherman said. The businessman then asked, “So, what do you do for the rest of the day?” The fisherman replied, “Well, I usually wake up early in the morning, go out to sea and catch a few fish, then go back and play with my kids. In the afternoon, I take a nap with my wife, and evening comes, I join my buddies in the village for a drink — we play guitar, sing and dance throughout the night.”

The businessman offered a suggestion to the fisherman. “I am a PhD in business management. I could help you to become a more successful person. From now on, you should spend more time at sea and try to catch as many fish as possible. When you have saved enough money, you could buy a bigger boat and catch even more fish. Soon you will be able to afford to buy more boats, set up your own company, your own production plant for canned food and distribution network. By then, you will have moved out of this village and to Sao Paulo, where you can set up HQ to manage your other branches.”

The fisherman continues, “And after that?” The businessman laughs heartily, “After that, you can live like a king in your own house, and when the time is right, you can go public and float your shares in the Stock Exchange, and you will be rich.” The fisherman asks, “And after that?” The businessman says, “After that, you can finally retire, you can move to a house by the fishing village, wake up early in the morning, catch a few fish, then return home to play with kids, have a nice afternoon nap with your wife, and when evening comes, you can join your buddies for a drink, play the guitar, sing and dance throughout the night!”

The fisherman was puzzled, “Isn’t that what I am doing now?”


This version of the story was from here (but not where I originally read it).

The idea is that if you can find some things that you love doing, and you do those every day, then what else do you need? Another component of the fisherman story is about the people. He’s got a family and good friends to spend time with.

If we combine the two ideas, find some things you love and do them, and don’t let people stop you from doing them. Having a group of people to share those experiences with is as important.

As you work on these things, you’ll get to a point where you love the life that you have, and you won’t really feel the need to get away from it. Sure, travel is nice, and seeing family is important, but the point is that you won’t need a break from the day to day, because you will love your day to day.

This is something that I have been trying to do myself, identify different things that I love, people that I want to spend time with, and protecting those activities, and carving out time to spend with people. Most importantly, prioritizing my family and investing time with them. I’m not perfect, but I think that I have been continuously improving along this path, and it has made a huge difference to me.

Comparison of Asynchronous Data Loading in Java

threads

Recently, I was working on a problem at work that had some blocking calls that I thought I may not want to block on. My first instinct was to throw those requests into a thread-pool, but I also needed to get the values out of them. There are some obvious ways of doing this, but they all involve different trade-offs.

I’m going to compare a few different methods of doing asynchronous work in Java, and try to think about the pros and cons of each. What I will not say is what you should use, since that’s going to be application-specific. This is also not intended to be an exhaustive discussion, since I simply don’t have that kind of time, and I’m sure that there are many ways of doing this. Instead, I will try to focus on the most common methods. This is also going to be about Java, not Android, Android has some fancy-pants thread handling stuff that is not present in Java.

Futures

If you need to have the value returned to the calling thread, you’ll probably need to use Future. This may involve some amount of blocking I/O. Here’s the basic flow for a Future:

  1. create a reference to your future, and give it a type
  2. pass a Callable to a thread-pool, and make the return type of the callable match your future
  3. get the value out of your future

Here’s a code example:

Future<String> future = threadpool.submit((Callable<String>)() -> getString());
// blocks until the callable returns
String myString = future.get();

Here’s the rub with that, future.get() is a blocking call, so while you’ve run getString() on another thread, you’re still blocking the calling thread.

If you don’t need the value right away, you can avoid calling get(), and periodically check future.isDone(). Here is an example of that:

Future<String> future = threadpool.submit((Callable<String>)() -> getString());
// do some work while the task is running
doSomeWork();
anotherMethodCall();
if (!future.isDone())
    System.out.println("still need a blocking call");
// blocks until the callable returns
String myString = future.get();

There are cases where this is acceptable, or even preferred. For example, if the threadpool represents some resource that you’d like to tightly bound (e.g. a set of 10 threads for doing network requests), and you actually need the response before you can do any more work on the calling thread, then this pattern makes sense.

pros

  • returns your value back to the calling thread
  • fairly straightforward threading model
  • you know exactly when in your flow you will have your value

cons

  • very likely to block the calling thread

Callbacks and Wrapper Classes

A second possible way to handle this problem is to create a callback or wrapper class (or wrapper interface), which provides some known method that can be called from within the thread-pool after the initial work is done.

High-level view of this method:

  1. create a method or runnable to call
  2. add a call back to the callback in the task submitted to the thread-pool

The biggest problem with this method is that you may not be able to, or want to modify the task running in the thread-pool to call your callback. It ties the two together, making the task more tailored for one specific application.

Let’s take a look at how this might work:

// first, define our callback class
static class Callback implements Runnable {
    private String result;

    public void setResult(String s) {
        result = s;
    }

    @Override
    public void run() {
        handleResponse(result);
    }
}

public static void handleResponse(String result) {
    System.out.println("got result: " + result);
}

void doAsyncWork(){
  Callback callback = new Callback();
  threadpool.submit(() -> {
      callback.setsetResult(getString());
      callback.run();
  });
}

This is quite a bit more verbose than the Future solution, however it does avoid the issue of blocking the caller thread. While this is non-blocking, it is not terribly flexible, as you need to know something about the callback in the task that’s running on the thread-pool. Ideally, you should be able to separate these things out more.

pros

  • non-blocking

cons

  • does not return to the calling thread
  • verbose
  • messy
  • brittle

Observers

Observers are a little more verbose than Callbacks, but can be much more flexible. The key to Observers that makes them more flexible is that they separate out the retrieval of a value from the logic of handling that data. You can also have multiple Observers watching an Observable.

Observers provide more separation, and may be able to be made more generic with less effort than a callback. However, you still need to create a class and a method that you’ll use for setting the data and kicking off the Observer.

High-level view of this method:

  1. create an Observable class
  2. implement Observer
  3. set the value in the Observable in the task submitted to the thread-pool

Let’s look at an implementation.

protected static class ContentObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        if (!(o instanceof ObservableContent))
            return;

        System.out.println("update result" + ((ObservableContent) o).getContent());
    }
}

protected static class ObservableContent extends Observable {
    private String content = null;

    public ObservableContent() {
    }

    public void setContent(String content) {
        this.content = content;
        setChanged();
        notifyObservers();
    }

    public String getContent() {
        return content;
    }

}

public static void doAsyncWork(ObservableContent content) {
    threadpool.submit(() -> {
        content.setContent(getString());
    });
}

pros

  • non-blocking
  • flexible

cons

  • does not return to the calling thread
  • verbose

RxJava

While RxJava is not a part of the Java language, I think that it is interesting, and seems to be quickly gaining popularity. RxJava takes the observer pattern a step further, and greatly simplifies it.

RxJava is nearly its own DSL (Domain Specific Language), so there’s a lot available with RxJava.

High-level view of this method:

  1. create a RxJava Observable
  2. tell it what to do
  3. tell it what thread to do the work on
  4. give it a Subscriber
  5. tell the Subscriber which thread to work on

It’s easier than it sounds, let’s take a look.

public static void handleResponse(String result) {
    System.out.println("got result: " + result);
}

Observable.just(0)
    .map(i -> request())
    .subscribeOn(Schedulers.from(threadpool))
    .observeOn(Schedulers.io())
    .subscribe(c -> handleResponse(c));

I’m not going to explain how to use RxJava here, but I will say that Dan Lew has a great series on RxJava on his blog.

pros

  • non-blocking
  • able to return to different threads
  • very flexible
  • compact
  • simple

cons

  • does not return to the calling thread
  • adds a dependency
  • requires learning how to use RxJava

Conclusion

I think that I’m going to give RxJava a try next time I need a non-blocking asynchronous data loading in Java. It still depends on the application, without the Java 8 lambdas, RxJava is still fairly verbose, similar to Java Observers. There are also considerations around whether or not you want to take dependencies in your application, and which specific dependencies you want to take. If I didn’t want the dependency, I would probably use Java’s built-in Observers. However, if I need that return value on the same thread, it’s still going to be Futures, and blocking the calling thread.

If you have a better way of doing this, especially getting the value back on the same thread without blocking, I’d be interested to hear about it. (Again, for Java, not Android, as Android does not have the same constraints.)

I wrote up a full set of examples of all the above here.

Expobar Office Lever Plus espresso shot with naked portafilter

Expobar Office Lever Plus espresso shot I pulled this morning with a naked portafilter. No channeling, perfect shot.This is my second ever shot on this machine. First one went about exactly as smoothly. This is seriously the best piece of coffee equipment I’ve ever owned. I’m going need to try hard not to over-caffeinate myself.

talk.coffee

coffee

I created a new community on Slack called talkcoffee. If you’re interested in talking coffee with myself and others on Slack, then go to the talk.coffee website and fill out the form. I’ll add you as soon as I can.

Internet Interactions and a Disclaimer

A friend recently posted about being continually frustrated with seeing offensive posts on Facebook. It seems that she tended to interact with those sorts of posts, Facebook had learned that fact, and showed her more of what she interacted with. My initial advice was to lean on the ‘hide’ and ‘unfollow’ functions that Facebook offers. Then I started thinking a little more about my own behavior.

xkcd Duty Calls

Source XKCD

I used to enjoy visiting the skeptic and atheist communities on Reddit quite a bit. I identified with the thoughts and opinions of the members of those communities, and was generally frustrated with ignorant people selling snake-oil (homeopathy, anti-vaxxers, etc.). Sometimes, this would result in my interacting with people from opposing camps, and typically in an unhealthy way. It’s easy to get into a fight online, and get really worked up over how blind the other person is. Sometimes it spills over into real-life arguments, and tense discussions. Eventually, I had enough of these kinds of interactions, and decided to pull back from those communities.

Once I got away from those communities, I was quite a bit happier. Yes, there are lots of dumb people out there, that say and believe dumb things. However, I don’t think that arguing with them is going to change their mind. In fact, I think that in general, it will do exactly the opposite (since there is evidence to suggest that whenever your views are challenged, you tend to dig in your heels more than open up to opposing views). So, in addition to wearing yourself out, getting frustrated, and dealing with ignorant people, you’re actually accomplishing the opposite of your goal. At least, that was the case for me, and why I’ve completely stopped visiting the atheist and skeptic communities on Reddit and elsewhere.

By the time I started seeing these posts on Facebook, I had learned enough to not engage, and to ‘hide’ posts, and ‘unfollow’ people who frequently posted things that I found overly simplistic and/or offensive.

That said, I don’t think that conversing online is pointless, but it makes a huge difference who you’re talking to, and the tone of the conversation. I think that in general, if you’re able to talk to level-headed people that you might disagree with, and both people are respectful, and reflective, then there’s certainly room for growth and increased understanding on both sides. I would rather have an interesting conversation with someone and change a possibly incorrect opinion than dig my heels in and cling to ideas that do not evolve with my knowledge of the world. Even though I do have trouble with this sometimes.

It’s also why I simply don’t interact with people who post very strongly held opinions that are presented in a pithy, obvious way. Generally, these are in the form of memes that at least half of its potential viewers would find offensive. I want to interact with people who are thoughtful and flexible. I have no interest in entering into a conversation with someone who is gung ho on some topic, whether or not I agree with them. I have little tolerance for continuing conversations that have turned sour, and will quickly exit them.

With that said, I’d like to post the following as a disclaimer for just about anything I write that resembles a stance or opinion.

Disclaimer

I am just a person, trying to figure things out. Whatever you read on this blog, or whatever I post anywhere, is perhaps what I’m thinking and feeling at the time. It is also possible that I’ll take a devil’s advocate position, as a way to try to keep the conversation moving or try to work something out. Opinion posts here are not representative of permanently held views of mine, and instead, should be thought of more fluidly — like this is where I am right now, and I’m using this post to work something out. If you disagree, and can respond thoughtfully and respectfully, then I welcome the discussion. If you’re going to try to fire shots, and pretend like we’re on some cable “news” show where people shout at each other, then please don’t bother.

Perfect is the Enemy of Good

Perfectionism can hurt your ability to accomplish your goals. It’s pretty simple, but the idea is that if you are a perfectionist, and you hold yourself to such a high standard that any sort of failure is unbearable then it will be very difficult to get things done, and you’ll likely be miserable.

When you strive to be perfect, and try to uphold a very high standard, you’re dealing in morals and virtues. Now, morals and virtues have their place, but it is not necessarily useful to hold ourselves to a standard and flagellate ourselves when we fail to live up to those unreasonably high standards. In this way, perfectionism is very similar to the problem of shoulds.

This is another idea that I have picked up from the book “Feeling Good”.

lake sunset

Procrastination

When you’re wanting to get things perfect, and organized in your head completely before you start, it’s easy to put off a task until you’re ready. The problem is that a lot of times, you’ll run into issues that you couldn’t have foreseen, and all that planning will be for naught. Not only did it wreck your planning, and your mental model, but you could have learned the same thing by starting earlier.

Over-planning

Whenever I start a new project, I have to choose the language, tools, libraries, and technologies that I’m going to use to do the project. This is sort of a fun excuse to poke around and learn something new, which is great. The problem comes in when I start thinking, “what if the project is successful, will this scale?” This turns out to be a dumb question to begin with, but also one that puts me into a mode of trying way too hard to come up with a perfectly engineered and beautifully designed system before I even start. This also tends to vastly increase the scope of my projects, which leads to me realizing how much time and effort will be involved in building them. Since the project balloons in scope and effort required, it’s easy to say, “well, I’ve only got an hour, there’s no way that I’ll be able to make progress on this in that amount of time, maybe this weekend.”

The projects that I remind myself up front that I don’t need to be perfect, I just need to start are the ones that actually get off the ground.

Rework

I’ve written before about the problem that I have noticed of junior developers coming in and wanting to re-write something from scratch, and this issue is related. I have been guilty of similar issues when coming back to look at code that I had written years prior. Sometimes I’ll have it in my head that it’s not perfect, and needs to be redone completely, so that it’s perfect. This does not usually lead to making progress on the task.

Doesn’t count

If you’re trying to lose weight, putting yourself on a strict diet, and you eat a bit of chocolate, or miss a workout, what happens? Does the day go out the window, and become a cheat day? Does it wreck your whole week? I’ve done that before.

For me, when I was trying to be really strict on a diet, and I slipped on a meal, it would usually turn that day into a cheat day. If I missed a workout, it could easily wreck the week.

If you allow yourself some small failures, but keep focused on the goal, it is much easier to stick to diet and exercise.

Conclusion

This post was overly-ambitious, preachy, sloppy, and I probably shouldn’t have written it. How do I delete this?

ROK Manual Espresso

ROK setup

Back in January, I had been without an espresso machine for nearly a year. My previous espresso machine, a FrancisFrancis! X5, had failed the previous year. I had backed the now-infamous ZPM Espresso Kickstarter campaign, and was hopeful until they folded. (I’ve met Gleb a few times, he’s a great guy, and I certainly don’t hold the failure against him or Janet. Sometimes startups fail, it happens.)

I had been getting by with pour-over, but was getting a bit anxious to be able to pull real espresso at home again. I had seen the ROK machine around the internet before and thought that it was a pretty interesting machine. It is completely manual, meaning that you heat the water separately, and you supply the pressure to push the water through with the levers. I’ve had an interest in more manual espresso extraction, where I would be able to have lots of latitude to dial things in, and get the perfect shots, just the way that I wanted them. Since the ROK was not terribly expensive, I decided to give it a try, with the understanding that it was basically a stand-in for a real machine, that I would be buying down the road.

Here’s a video showing about the best extraction that you can hope for on this machine. (Note, the video is not mine.)

For the last six months, with the exception of a month-long trip to the East Coast, I have pulled shots on this machine daily. I was able to get extractions similar to what was shown in the video pretty consistently, but this is about the limit, no ristretto shots here. There is also a hard limit on the amount of force that you can exert while pulling the shot, since the press parts in the top are made of plastic, and might break if you push too hard on them.

portafilter and tamper

I do have a couple of notes on using this machine. First, I would recommend using boiling water, it will cool as it goes through the machine. You may want to run hot water through it to heat it up before trying to pull a shot. You can get a decent extraction if you dial everything in and pull hard on the levers. (Fresh beans makes a very noticeable difference.)

standalone unit milk frother

If you’re somehow still interested in a ROK, mine is currently up for sale on eBay. I’ve had this for 6 months or so, and it’s in great condition. I am also including a stainless steel tamper that fits the portafilter properly. I’m selling this because I’m upgrading to a high end machine, and don’t have a use for this anymore. The milk frother has never been used.

It’s interesting to use a totally manual machine, and I’m glad that I had the opportunity to try it out for a while.