BLOG

How to Integrate Fisheye + Crucible in AWS with JIRA in the Atlassian Cloud

Let’s say you’ve got JIRA running, not as a standalone server, but as a service from Atlassian. You’re doing this for whatever reason, but probably because it’s cheaper and you don’t want to be bothered with applying patches and keeping the server running. Let’s also say that you decide you really want Crucible so that your team can do code reviews before the code gets into the repository. Crucible doesn’t come as a cloud service; you have to run it on your own server. Finally, let’s say that you’ve already got some stuff running in the AWS cloud and you still don’t want to have to hassle with getting space and hardware into some data center somewhere, so you’re going to put Crucible on its own little EC2 server. How do we get these crazy kids together? First, spin up a new EC2 instance. How big? Well, your mileage may vary, but a micro instance is too small to deal with the RAM requirements. Atlassian doesn’t tell you this, but deep in the configs the Fisheye/Crucible server requires at least a gigabyte of heap. You can start out with a medium instance and then if for no reason Crucible just stops answering, you’ll probably want to get more RAM. When you spin up the instance, AWS wants you to specify a security group with firewall rules. You would think, looking at the Fisheye/Crucible documentation, that what you need is port 8060 (for Crucible) and port 22 (for ssh, so you can get in and work with the server). But, as we’ll see, you really need to have 80 (HTTP) or 443 (HTTPS) open. By default, Fisheye/Crucible wants to listen on port 8060. Atlassian has all their services listen on high ports so they don’t have to start as a root user, and that’s good. But Atlassian cloud services refuse to link to services that are running on ports other than 80 or 443, even if you put the port in the URL of the service. To be clear: your default Fisheye installation URL will be something like http://fisheye.mycoolcompany.com:8060 but JIRA in the cloud will refuse to talk to that URL. It’ll let you type it in, but then it will complain that your server couldn’t be reached. Do not go bald tearing at your hair because your server is, too, reachable. Let’s say you aren’t quite ready to deal with SSL just yet, so you just want to go regular old HTTP. So what you need is to get Fisheye to listen on port 80. But if you do that, then Fisheye has to be started by root, and Atlassian doesn’t trust their code that much, so you probably shouldn’t, either. (There are lots of good reasons for this that have nothing to do with trusting the quality of the code. So don’t freak out.) What you really need to do is set up a reverse proxy that listens on port 80 and forwards all requests to port 8060. This sounds like a job for nginx! But let’s also say that you’ve never actually set up nginx before. How do you do this? Okay, Amazon has built their own package for it, so you just have to `sudo yum install nginx` on your Crucible server. But how do you configure it? There are pages on the web that will tell you how to do it, but they’re incomplete. This one is typical; it contains all the information you need except the part where the server section they show you has to be contained inside an http section. If you try copying that config and putting it in /etc/nginx as nginx.conf, it won’t work. Here’s the missing bit: the Amazon package is already set up almost correctly for you. Open up /etc/nginx/nginx.conf and comment out the entire server section inside the http section. Leave everything else. Now create a new file in /etc/nginx/conf.d/ called something like crucible.conf. That is where you define your reverse proxy:  
server {
  listen 80;
  server_name fisheye.mycoolcompany.com;
  location / {
  	proxy_set_header X-Real-IP $remote_addr;
  	proxy_set_header X-Forwarded-For $remote_addr;
  	proxy_set_header Host $host;
  	proxy_pass http://localhost:8060;
  }
}
Now, you can start up nginx using the init.d script: `sudo /etc/init.d/nginx start` and you’re good to go. Now when you go to the administrative section of your Crucible server, you can say that the site URL is just http://fisheye.mycoolcompany.com with no port at the end. Now you can go to Application Links and create a link to your JIRA over at https://mycoolcompany.atlassian.net and JIRA will be able to create the reciprocal link.

Building the Mac App

Overview

Litho Writer is a Java application, allowing us to develop one tool to be used on Windows, Mac OS, and Linux. Distributing a Java application, however, can be difficult, as many people don’t have Java installed, as a bare JAR file doesn’t have a custom icon or custom JVM options, etc. It is possible to bundle a Java application as a Mac OS app bundle, but for several years Apple has dropped support for their Jar Bundler application (and it doesn’t work with modern JVMs, anyway). Figuring out how to create a distributable application has been a frustrating process, with no single website giving complete instructions. Having figured it out, I’ll explain the process.

1. Use Maven to build the project

If you’re developing a Java application, you probably already use either Maven or ant to build your artifact. When starting a new project, I almost always start out with Maven just because it brings in dependency resolution and I don’t have to commit all my third party libraries to my repository. In this case, it makes building the bundle a lot easier, too. Note, though, that the actual appbundler plugin is an ant plugin and we wind up running it via the maven-antrun plugin. Still, understand that this example will be using the Maven style of directory layout.

2. Build a shaded jar

My first step toward a distributable application was to build a jar that contained all the dependencies inside it so that all a user would have to do is double-click the jar or type `java -jar lithowriter.jar` without having to worry about seven lines of classpath. Java doesn’t let you include libraries inside a jar (well, you can, but it won’t work the way you want). The way to do this is to shade the jar — which basically means to unjar all the dependencies into your build directory and then jar up everything, dependencies plus your classes, into a single jar. This works very well, unless some of those dependencies are signed jars, in which case the signatures don’t match and Java will bail. So the thing to do is to use maven-shade-plugin :

<plugins>
  <plugin>     
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-shade-plugin</artifactId> 
    <version>2.4.1</version> 
    <executions>
     <execution> 
      <phase>package</phase> 
      <goals> 
       <goal>shade</goal>
      </goals>
      <configuration>
       <transformers>
        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
         <mainClass>${project.bundle.mainclass}</mainClass>
        </transformer>
       </transformers>
       <filters>
        <filter>
         <artifact>*:*</artifact>
          <excludes>
           <exclude>META-INF/*.SF</exclude>
           <exclude>META-INF/*.DSA</exclude>
           <exclude>META-INF/*.RSA</exclude>
          </excludes>
        </filter>
       </filters>
      </configuration>
     </execution>
    </executions>
 </plugin>
</plugins>

Note the property “project.bundle.mainclass” — we define that up at the top of the pom.xml, just so that we can copy and paste this snippet from one project to another. The excludes files in the filterset specify not to copy over any signatures.

3. Use the appbundler ant task and universalJavaApplicationStub

When Apple retired Jar Bundler, Oracle created the appbundler ant plugin. That plugin is great, unless you want to use a JDK more recent than 1.6. To do that, or to use a non-Oracle JDK, you’ll want to get universalJavaApplicationStub and a particular fork of the appbundler plugin.

Application stub: https://github.com/tofi86/universalJavaApplicationStub

appbundler: https://bitbucket.org/infinitekind/appbundler/overview

You need to download the source for appbundler and build the plugin. Then you need to install it into your local Maven repository so that when it’s time to run the plugin Maven can find it:

cd appbundler
ant package
mvn install:install-file -Dfile=appbundler/bin/appbudler-1.0ea.jar -DgroupId=com.oracle.appbundler -DartifactId=appbundler -Dversion=1.0ea -Dpackaging=jar

Now you can actually add the antrun plugin entry to your pom.xml:

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.7</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <target>
                    <taskdef name="appbundler"
                        classname="com.oracle.appbundler.AppBundlerTask"
                        classpathref="maven.plugin.classpath" />
                    <appbundler outputdirectory="${project.build.directory}"
                        name="${project.bundle.appname}"
                        displayname="${project.bundle.appname}"
                        icon="${project.bundle.icon}"
                        identifier="${project.groupId}"
                        shortversion="${project.version}"
                        copyright="${project.organization.name}"
                        executableName="universalJavaApplicationStub"
                        mainclassname="${project.bundle.mainclass}" />
						<classpath file="${project.build.directory}/${project.build.finalName}.jar" />
						<runtime dir="${env.JAVA_HOME}" />
                        <option value="-Xdock:name=${project.bundle.appname}"/>
                        <option value="-Dapple.laf.useScreenMenuBar=true"/>
                        <option value="-Dcom.apple.macos.use-file-dialog-packages=true"/>
                        <option value="-Dcom.apple.macos.useScreenMenuBar=true"/>
                        <option value="-Dcom.apple.mrj.application.apple.menu.about.name=${project.bundle.appname}"/>
                        <option value="-Dcom.apple.smallTabs=true"/>
                        <option value="-Dfile.encoding=UTF-8"/>
                        <option value="-Xmx1024m" name="Xmx"/>
                        <option value="-Xms500m" name="Xms"/>
                        <bundledocument extensions="lithodoc"
                        	name="Litho Writer document" 
							role="editor"
							isPackage="true"/>
                    </appbundler>
                </target>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>com.oracle.appbundler</groupId>
            <artifactId>appbundler</artifactId>
            <version>1.0ea</version>
        </dependency>
    </dependencies>
</plugin>

Note a couple interesting things about the appbundler task: the runtime element means that we’re bundling a JRE into the app so that users don’t need to have Java installed, and the bundledocument element says that this app should be associated with the .lithodoc file extension.

4. Make an iconset for the application

One would think that, since an iconset is something that every application on Mac OS has, it would be easy to make one. One would be correct, but one would also be forgiven for wondering why there are no developer tools built in to help you make such a thing easily. I’ll save you the frustration of wondering, though. Just go to the App Store and get “Icns Builder” — it’s really a wrapper around the command line developer tool (see, there is one, it’s just a pain in the neck) that lets you drag an image onto one of the predefined sizes and the app will scale the image and assign all the other required sizes. Save the icon and you’re done.

5. Place the application stub and the iconset at the project root

This is one of the bits of information that is hard to find, although it’s easily derived by trying to build the bundle (the task will fail with tantalizing error messages). Your icon and the application stub should be in the same directory as your pom.xml. If you store them somewhere else, then either Maven won’t be able to find them, the appbundler task won’t be able to find them, or the appbundler task will put them in the wrong place inside the application bundle. I have, through experimentation, achieved each of these outcomes. Just store them next to the pom.xml.

6. Fix the bundled JRE

At this point, you can build the app: `mvn package` and watch it go. The bundled JRE is there and so is your jar and your icon and all the JVM options and arguments. You can explore the package contents in Finder to verify this. However, the bundled JRE isn’t actually able to be signed by the codesign tool, and the error message codesign spits out is ambiguous. The error message suggests that either the Info.plist or the target application can’t be a link. If you look at the JRE (inside the app bundle’s Plugins folder) you’ll see that its Info.plist is not a link, so what’s the problem? The problem is that the message should say, “neither.” The Plugins/<jre>/Contents/MacOS folder contains a link to libjli.dylib (for my Oracle JDK) and that’s what codesign is barfing on. So, delete that link and copy the lib from Plugins/<jre>/Contents/Home/jre/lib/jli.

7. Generate a signing certificate

Fire up Keychain Access and use the Certificate Assistant to Request a Certificate from a Certificate Authority. Full instructions for this bit are easily followed from Apple.

8. Sign the app bundle

Finally, we can run codesign! In a Terminal window, cd into the application bundle so that you’re in the Plugins directory and then sign the JRE:

codesign -s “Developer ID Application: WidgetCo, Inc.” <jdk directory name>

then cd back up to the target directory so you can sign the whole app bundle:

codesign -s "Developer ID Application: WidgetCo, Inc." <app bundle.app>

Now your mom can run your program.

  • Lithomobilus is a new kind of e-book platform that enables new narrative forms.

    It gives readers the ability to more fully immerse themselves in their favorite stories, authors the ability to generate more revenue from their efforts, and publishers the means to transact directly with readers.

    If you are interested in finding out more about Lithomobilus, please take a look at the Lithomobilus-powered Lithomobilus app for iPad, then drop us a note: info@lithomobilus.com.

    download from the App Store