Quantcast

Ben McCann

Co-founder at Connectifier.
ex-Googler. CMU alum.

Ben McCann on LinkedIn Ben McCann on AngelList Ben McCann on Twitter

Case Study: Usable and Unusable APIs

07/25/2009

It was the best of times, it was the worst of times…

It was the age of wisdom, it was the season of light.  A great library called dom4j was written with its users in mind.  It included a quick start guide and a cookbook for people that actually wanted to get things done.  Converting a document to a String took 15 characters: document.asXML().  But there were too many competing XML parsing implementations, so a standard was created.  And sadly, dom4j has not been updated to adhere to that standard.

It was the age of foolishness, it was the epoch of incredulity.  15 characters to turn a document into a string?  That is far too few.  What will we tell our managers when they ask how many lines of code we have written?  We have a better way and it is called Xerces:

import java.io.StringWriter;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;

public final class XmlUtil {

  private static final TransformerFactory factory = TransformerFactory.newInstance();

  public static String toString(Node node) {
    if (node == null) {
      return null;
    }
    try {
      Source source = new DOMSource(node);
      StringWriter stringWriter = new StringWriter();
      Result result = new StreamResult(stringWriter);
      Transformer transformer = factory.newTransformer();
      transformer.transform(source, result);
      return stringWriter.getBuffer().toString();
    } catch (TransformerConfigurationException e) {
      e.printStackTrace();
    } catch (TransformerException e) {
      e.printStackTrace();
    }
    return null;
  }

}

Sample log4j.properties file

07/18/2009

I always find the hardest part of getting started with log4j is creating a log4j.properties file. For that reason, I’ve posted an example below. This file configures log4j to log any messages of level info or higher to the console except for classes under the com.dappit.Dapper.parser or org.w3c.tidy packages.

#------------------------------------------------------------------------------
#
#  The following properties set the logging levels and log appender.  The
#  log4j.rootCategory variable defines the default log level and one or more
#  appenders.  For the console, use 'S'.  For the daily rolling file, use 'R'.
#  For an HTML formatted log, use 'H'.
#
#  To override the default (rootCategory) log level, define a property of the
#  form (see below for available values):
#
#        log4j.logger. =
#
#    Available logger names:
#      TODO
#
#    Possible Log Levels:
#      FATAL, ERROR, WARN, INFO, DEBUG
#
#------------------------------------------------------------------------------
log4j.rootCategory=INFO, S

log4j.logger.com.dappit.Dapper.parser=ERROR
log4j.logger.org.w3c.tidy=FATAL

#------------------------------------------------------------------------------
#
#  The following properties configure the console (stdout) appender.
#  See http://logging.apache.org/log4j/docs/api/index.html for details.
#
#------------------------------------------------------------------------------
log4j.appender.S = org.apache.log4j.ConsoleAppender
log4j.appender.S.layout = org.apache.log4j.PatternLayout
log4j.appender.S.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n

#------------------------------------------------------------------------------
#
#  The following properties configure the Daily Rolling File appender.
#  See http://logging.apache.org/log4j/docs/api/index.html for details.
#
#------------------------------------------------------------------------------
log4j.appender.R = org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File = logs/bensApps.log
log4j.appender.R.Append = true
log4j.appender.R.DatePattern = '.'yyy-MM-dd
log4j.appender.R.layout = org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n

#------------------------------------------------------------------------------
#
#  The following properties configure the Rolling File appender in HTML.
#  See http://logging.apache.org/log4j/docs/api/index.html for details.
#
#------------------------------------------------------------------------------
log4j.appender.H = org.apache.log4j.RollingFileAppender
log4j.appender.H.File = logs/bensApps.html
log4j.appender.H.MaxFileSize = 100KB
log4j.appender.H.Append = false
log4j.appender.H.layout = org.apache.log4j.HTMLLayout

Printing a Stack Trace anywhere in Java

07/03/2009

You don’t need to catch an Exception in order to print a stack trace in Java.  Sometimes they can be helpful for debugging and logging purposes.  Here’s an example of how to print a stack trace at any moment:

new Exception().printStackTrace();

If you want more control over the output, you can build some code off the following:

  System.out.println("Printing stack trace:");
  StackTraceElement[] elements = Thread.currentThread().getStackTrace();
  for (int i = 1; i < elements.length; i++) {
    StackTraceElement s = elements[i];
    System.out.println("\tat " + s.getClassName() + "." + s.getMethodName()
        + "(" + s.getFileName() + ":" + s.getLineNumber() + ")");
  }

Getting Started with Tonido on OpenSUSE 11.1

06/25/2009

I’d heard of Tonido awhile back, but was having trouble getting it to run on OpenSUSE since it’s packaged only for Ubuntu.  Tonight I sat down and figured out how to get it to run:

  • Install alien via the YaST package manager
  • Convert the Tonido package to an RPM by using alien (alien -r filename.deb)
  • Install newly created Tonido RPM
  • Install libnotify1-32bit via YaST package manager
  • Open port 10001 in Firewall (Security and Users > Firewall > Allowed Services > Advanced)
  • Port forward port 10001 to the machine where Tonido is installed

Yay, now you can run Tonido.  When you start it, it will open Konqueror, for which it is very buggy.  So close that window and open http://127.0.0.1:10001/ in FireFox.  Now you’re off and running.

My initial thoughts:

  • This needs SSL support to really be useful since it gives access to my whole computer.
  • Too bad the setup is a bit hard.  I’m sure more people would adopt it if it used UPnP.
  • The WebShare app could be pretty cool in the future, but at the moment it’s mostly worthless.  You can only download one file at a time and there’s no upload.  I’d like to be able to mount my shares on my Windows machine.  I really wish it exported them via WebDav or SFTP.
  • The music player needs flac support.  I can’t play any of my music collection!

Determining Port Usage

05/18/2009

Want to know how to figure out what’s running on a given port on your machine?  The following example will show you what’s running on port 80 on your Linux machine:

lsof -i -n -P | grep :80

Maven on Eclipse Tutorial

04/12/2009

Install Java and Maven

First off, make sure you have a Java JDK installed and that your JAVA_HOME environment variable points to it. You can check by typing “echo %JAVA_HOME%” in Windows or “echo $JAVA_HOME” in Linux. You’ll next want to download and install Maven by following the directions on their website.  Once Maven is installed, you’ll want to create a settings.xml file in your .m2 directory, which is located at ~/.m2 in Linux or C:\Documents and Settings\%USER%\.m2 on Windows.  You can do this by copying the settings.xml file from the directory where Maven in installed to you .m2 directory.

Install m2eclipse

You’ll want to make sure your Eclipse installation is pointing at your JDK.  You can check by going to Window > Preferences > Java > Installed JREs.  You’ll also want to set the -vm flag in your eclipse.ini file which is located at the root of your Eclipse installation.  Here’s what my file looks like:

-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256M
-framework
plugins\org.eclipse.osgi_3.4.0.v20080605-1900.jar
-vm
C:\Program Files\Java\jdk1.6.0_11\bin\javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx512m

Finally, you get to install m2eclipse by opening up Eclipse and selecting Help > Software Updates… > Available Software > Add Site…  Then enter http://m2eclipse.sonatype.org/sites/m2e, check the newly added boxes, and press Install.

Create Your First Maven Project

Now for the fun.  You can create a Maven project in Eclipse by selecting New > Project… > Maven > Maven Project.  Check “Create a simple project” on the first screen and hit Next.  Enter the Group Id, Artifact Id, and Name.  For example, “com.benmccann.robot”, “window-robot”, “Window Robot”.  Now, at long last, you can do some actual programming.

Add Dependencies

It won’t be long before you’ll want to add dependancies.  Open the Maven Indexes View by selecting Window > Show View > Other… > Maven > Maven Indexes.  You’ll want to make sure you have the Maven Central Repository, so right-click the view and choose “Add Index”.  Enter http://repo1.maven.org/maven2/ for the Repository URL and hit Retrieve to fill in the Repository Id of central.  Now you can right-click the project and choose Maven > Add Dependancy.  We’ll add jUnit since you’ll probably want it anyway.  Type junit and you should see it populated into the box below.  Use the dropdown at the bottom to set a scope of “test” and hit OK.

How to use the Linux find command

04/01/2009

I always have trouble remembering how to use the find command in Linux, so here are a few examples.

Finding all hidden files:

find . -regex '.*/\..*'

Deleting all Java files:

rm -rf `find . -name *.java`

Deleting all directories named .svn:

rm -rf `find . -type d -name .svn`

Recursively set permissions for a web server:

chmod -R a+r ~/www
find ~/www -type d -exec chmod a+x {} \;

Finding all exectuable files:

find . -executable -type f

Extracting AMR Audio from Android 3GP Files

03/18/2009

The Android MediaRecorder currently records audio in AMR format and stores that within a 3GP container which provides metadata for the recording. Thanks to Sebastian Annies at Core Media it is possible to extract the AMR audio from the 3GP file using isobox4j. If you can’t play .amr files on your Windows machine, then I suggest K-Lite Codec Pack.

package com.benmccann.audio;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;

import com.coremedia.iso.FileRandomAccessDataSource;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.IsoOutputStream;
import com.coremedia.iso.mdta.Sample;

public class ThreegpReader {

  private final static int ANDROID_AUDIO_TRACK_NUM = 3;
  
  private final File inputFile;
  
  public ThreegpReader(File file) throws FileNotFoundException {
    this.inputFile = file;
  }
 
  public void extractAmr(OutputStream outputStream) throws IOException {
    IsoFile iso = new IsoFile(new FileRandomAccessDataSource(inputFile));
    iso.parse();
    iso.parseMdats();
    
    IsoOutputStream isoOutput = new IsoOutputStream(outputStream, false);

    // write an AMR header
    isoOutput.write(new byte[] {0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a});
    
    // write the audio content
    for (Sample sample : iso.getTrack(ANDROID_AUDIO_TRACK_NUM)) {
      sample.getContent(isoOutput);
    }
    isoOutput.flush();
    isoOutput.close();
  }
  
}

Android Audio Recording Tutorial

03/11/2009

After many hours of trying to record audio on my Google Android device, I’ve finally arrived at a workable solution.  There were a few bumps along the way besides the horribly out of date MediaRecorder documentation, which was sorely lacking details.  For one, I could only get audio to record to the SD card.  Additionally, the directory being recorded to must already exist before attempting to record to it. Without further ado, here is a complete example for recording audio on the Android via the MediaRecorder API:


package com.benmccann.android.hello;

import java.io.File;
import java.io.IOException;

import android.media.MediaRecorder;
import android.os.Environment;

/**
 * @author <a href="http://www.benmccann.com">Ben McCann</a>
 */
public class AudioRecorder {

  final MediaRecorder recorder = new MediaRecorder();
  final String path;

  /**
   * Creates a new audio recording at the given path (relative to root of SD card).
   */
  public AudioRecorder(String path) {
    this.path = sanitizePath(path);
  }

  private String sanitizePath(String path) {
    if (!path.startsWith("/")) {
      path = "/" + path;
    }
    if (!path.contains(".")) {
      path += ".3gp";
    }
    return Environment.getExternalStorageDirectory().getAbsolutePath() + path;
  }

  /**
   * Starts a new recording.
   */
  public void start() throws IOException {
    String state = android.os.Environment.getExternalStorageState();
    if(!state.equals(android.os.Environment.MEDIA_MOUNTED))  {
        throw new IOException("SD Card is not mounted.  It is " + state + ".");
    }

    // make sure the directory we plan to store the recording in exists
    File directory = new File(path).getParentFile();
    if (!directory.exists() && !directory.mkdirs()) {
      throw new IOException("Path to file could not be created.");
    }

    recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    recorder.setOutputFile(path);
    recorder.prepare();
    recorder.start();
  }

  /**
   * Stops a recording that has been previously started.
   */
  public void stop() throws IOException {
    recorder.stop();
    recorder.release();
  }

}

openSUSE 11.1 Installation and Setup

02/23/2009

openSUSE 11.0 was the best Linux distribution I’ve ever used.  I was hoping openSUSE 11.1 would continue the great strides of late, but it’s a bit of a mixed bag.  One of the most frustrating things for me is that support for remote access is falling by the wayside.  VNC used to be better integrated with openSUSE, but they removed integrated support in favor of KDE’s krfb, which is badly broken.  The package management in openSUSE 11.0 and 11.1 is enough to suggest upgrading if you’re using an older version – it is absolutely great, especially for those of us who have been around long enough to see the pains it’s gone through.  If you want to get up and running with openSUSE 11.1 then there are likely a few customizations you’ll want to make.

Upgrade to KDE 4.2

It’d be nice if openSUSE and KDE could sync up their release schedules a bit better.  KDE 4.2 came out a month or so after openSUSE 11.1 and you’ll likely want the upgrade.  I experienced some annoying but not critical bugs with the version that shipped.

  1. YaST > “Software” > “Software Repositories”
  2. Click “Add”
  3. Select “Specify URL”
  4. Enter: http://download.opensuse.org/repositories/KDE:/KDE4:/Factory:/Desktop/openSUSE_Factory/
  5. Also add: http://download.opensuse.org/repositories/KDE:/Qt/openSUSE_Factory/
  6. Uncheck “Dependencies” > “Autocheck”
  7. Do a search for QT and another for KDE
  8. Check all the boxes where “Installed (Available)” is blue
  9. Recheck “Dependencies” > “Autocheck” and resolve any dependency problems
  10. Hit “Accept” to install the selected packages

Setup Multimedia

This is a perennial setup step on Linux distributions.  We’ll install the codecs needed to watch DVDs, handle MP3s, etc.

  1. YaST > “Software” > “Software Repositories”
  2. Click “Add”
  3. Select “Community Repositories”
  4. Select “Packman Repository” and “VideoLan Repository”
  5. YaST > “Software” > “Software Management”
  6. Install libffmepg0 and libdvdcss.  Also, if you want to be able to watch ASF streams you should install mplayer-plugin.
  7. Start Kaffeine and tell it to handle mms and rtsp streams when it asks

Install NVIDIA drivers

If you have an NVIDIA card, then you’ll want to install the drivers.

  1. YaST > “Software” > “Software Repositories”
  2. Click “Add”
  3. Select “Community Repositories”
  4. Select “NVIDIA Repository”
  5. YaST > “Software” > “Software Management”
  6. Install “nvidia-gfxGO2-kmp-default”

Install CD Ripper

For some reason, openSUSE 11.1 no longer ships with KAudioCreator installed by default.  My guess would be that it hasn’t been ported to KDE4 yet, but it’s nice to have, so we’ll go ahead and install it anyway.  We’ll also change KAudioCreator’s (stupid) default setting of not looking up CDDB information that hasn’t been cached on the local system.

  1. YaST > “Software” > “Software Repositories”
  2. Click “Add”
  3. Select “Community Repositories”
  4. Select “openSUSE BuildService – KDE:Community”
  5. YaST > “Software” > “Software Management”
  6. Install “kdemultimedia3-CD”
  7. Open kaudiocreator
  8. Select “Settings” > “Configure KAudioCreator …” > “CDDB”
  9. Set lookup to “Cache and remote”

Upgrade WINE

WINE is continuing to evolve and getting closer every day to reaching maturity.  You’ll likely want the latest version instead of the one that was the latest when openSUSE shipped.

  1. YaST > “Software” > “Software Repositories”
  2. Click “Add”
  3. Select “Community Repositories”
  4. Select “openSUSE BuildService – Wine CVS Builds”
  5. YaST > “Software” > “Software Management”
  6. Do a search for wine and click the check mark until version upgrade is selected

Setup a static IP address

Having a static IP address is very nice when you want to remote desktop to your server or access it in some other way without worrying about what the IP address is.  There may also need to be some configuration done on your router for this one.  Or you may prefer to investigate DHCP reservations if your router supports them.

  1. YaST > “Network Devices” > “Network Settings”
  2. Under “Overview”, select your network card and click “Edit”
  3. Enter your static IP (besure to also enter DNS and gateway information)
  4. Hit save

Setup remote desktop through NX

The two main remote desktop softwares for Linux are VLC and NX.  NX is much faster, but unfortunately I’ve had some problems with desktop sharing with 11.1 vs. 11.0.  If you get 11.1 to shadow properly then please let me know.  In addition to installing NX, we’ll also open the corresponding port in the firewall so that we can connect from another machine.

  1. Download the NX Linux packages
  2. Run “rpm -iv nxclient-3.3.0-3.i386.rpm”, “rpm -iv nxnode-3.3.0-3.i386.rpm”, and “rpm -iv nxserver-3.3.0-3.i386.rpm”
  3. Run “/usr/NX/scripts/setup/nxserver –install”
  4. Run “/usr/NX/bin/nxserver –keygen”
  5. In your NX client, open “Configure…” > “General” tab > “Key …”
  6. Copy the contents of “/usr/NX/share/keys/default.id_dsa.key” into the key window and save it
  7. Open “/usr/NX/etc/server.cfg”
  8. Change line 563 from ‘EnableSessionShadowingAuthorization = “1″‘ to ‘EnableSessionShadowingAuthorization = “0″‘ which will enable you to select “Shadow” in the client under the “General” tab’s “Desktop” framebox if you’d like to do desktop sharing
  9. YaST > “Security and Users” > “Firewall” > “Allowed Services”
  10. Allow “Secure Shell Server”

Setup Network File Share using Samba

Samba allows you to share files on your computer with others on the network.

  1. YaST > “Software” > “Software Management”
  2. Install “samba” if it is not already installed
  3. YaST > “Network Services” > “Samba Server”
  4. Tell it to unblock the firewall
  5. Change sharing settings as you’d like and hit “Finish”
  6. Add a user to Samba by running “smbpasswd -a username” where username is the user you’d like to create.
  7. Connect from your Windows machine by right clicking “My Computer” and browsing your network.  If you have trouble connecting you might also try opening a “Run…” dialog off the start menu and typing in your IP address with two leading slashes “\\192.168.10.x”
Newer Posts
Older Posts