Articles by Thomas

Linux geek. Christian. Love challenges. Haven’t done enough to be as old as I am.

Alas, poor me. Given an IP address that may modify itself over time. A web address would have to be altered time and time again in order to point to my home server. Thankfully, there are sites that will host a domain name and accept updates when they are notified of IP address changes. This is commonly referred to as Dynamic DNS

There are dozens of Dynamic DNS services on the Internet. To pick the right one, I gathered some requirements of my own:

  • It must be supported by OpenWRT’s DNS scripts  (dyndns.org, changeip.com, zoneedit.com, no-ip.com, freedns.afraid.org)
  • It must be free to use (changeip is only commercial, so it is out)
  • It must be able to use domain that I have already registered instead of using their own domain names(down to zoneedit.com and freedns.afraid.org)

Right now, I am trying out both zoneedit.com and freedns.afraid.org.  Here are some pros and cons with each service.  Some of these detail were unexpected.  Hopefully, this will help others who are facing similar problems.

When a domain is put on freedns.afraid.org, other registered members of the service are free to create subdomains off of your domain.  This is nice for people who are doing the subdomains, but horrible if you are trying to keep any type of brand consistency for your domain.  Someone can take anything.yourdomain.com and put whatever they want there.  In order to hide the domain from other users, a fee of $5 monthly must be paid.

The part about freedns.afraid.org that came as a present surprise is the update URL.  It doesn’t contain the account’s password.  Instead, it has a unique key in it.  That way, if the key to perform an update is compromised, the worst that can happen is someone else points the domain to a different web site.  The account itself is safe.  The attacker doesn’t even know the account name.

Zoneedit.com allows for two free domains before they start to charge a nominal fee.  The domains are yours and other users can’t create subdomains off of them.

The update URL for zoneedit.com contains the user name and password for the account.  Anyone listening to the traffic on the account can compromise the entire account.

Both services update the DNS record quickly after a change IP request is sent.

Both services have somewhat dated web pages.  The slight edge goes to freedns.afraid.org just because of how simple it is.

After all of that, it looks like zoneedit.com is the winner.  I don’t like my password existing in clear text anywhere; however, the traffic can be sent via SSL to protect it from simple traffic sniffing attacks.

Allowing other users create subdomains off of one of my domains does not appeal to me at all.  That is the only issue that disqualifies freedns.afraid.org.  It is otherwise a great service.

If there is something that you would like me to try out, or if there is another service that I missed, please drop me a comment.

Tags: , ,

ocpjp

This goal has been on my mind for two years now. The first time I head through the Sierra and Bates SCJP book (ISBN: 978-0-07-159106-5) was about a year and a half ago. For those unfamiliar, it is the most acclaimed study guide in existence, for this exam, and is 800 pages long.

Last February, was the first time when I had a testing center picked out and a date in my mind.   However, that was also the time when I found out that it was time to move back to Michigan.  The exam would have to wait while the house shopping, cleaning, selling/renting of our old place, and other mayhem took place.

The last year, I have felt a fire coming back.  Coding is fun again.  It is amazing how much better it feels to be in a job situation where my work is defined by my skills and creativity.

Here’s a tale of two Roles:

  • Role Type A: The role is always inside to your skill set, the building blocks for the jobs you perform are pre-determined, and there is only one way to complete the job correctly.
  • Role Type B:  The role expands your skill set.  The building blocks for the jobs push you to go beyond your current level of knowledge and become more skilled in your craft.  The correct answer is determined collaboratively with input from many sources.

Another way that I think of this is that a Type A role is like eating the same food day in and day out.  Some people need type this stability.  Uncertainty is a scary thing.

The reason the exam became so important lately is that I am in a Type B Role.  There are opportunities to make decisions based on new knowledge I learn.   This is also why architecture seems to be the perfect fit for my persona.

Study:

There are many stories of people who study for the exam for 2-3 hours a night for 2-3 months and do just fine on the exam.  This was more than just an exam to me.  This was an exercise to become great at using a tool that I use every day.   A final score of 95% reflects that goal.

After reading through the Sierra and Bates book again this year, I discovered they had also made a book of practice exams (ISBN: 978-0-07-226088-5).  That book is what put my score over the top.  The practice exams in it are nothing short of brutal.  The average score from taking the practice exams was around 70%.  I never finished a practice exam in the alloted time of 3 hours (the real exam is 2 1/2 hours now).

The practice exams felt like work.  By the end of my studies, I had finished 7 practice exams that totaled 420 questions in about 24 hours.  After review time and time spent on other exam sites , I estimate the number of questions in my study to well over 1,000.

There are roughly 100 classes sitting around in my Eclipse project that I started while studying for the exam.  All of them touch on concepts in Java, and many of them are in the “gotcha” category.   Q: What primitive types are the same size in bytes, but cannot be assigned implicitly to any other primitive type????? A: short and char .

The real exam was much easier than the practice exams.  I actually finished with enough time to review the test for errors.

You:

The word I appears way too much in this post.  That usually means that it isn’t much help for people who are just passing by this post on the Internet.  If you would like to ask me a specific question about the exam, please leave a comment and I will answer it in a day or so.  Of course, there are some other great places to do that as well.

Tags: ,

CS331 was a fun class. Data structures are right up my ally. They take a simple concept and expand it to apply to scenarios in neat and interesting ways.  It was eight years ago when I took (and passed) that class…Yikes!

Most of the assignment, actually, 4 out of 5 assignments, were coded and turned in on time.  This was not a small feat.  In college, it was common for me to work a 30 hour week.  Getting assignments done was usually a balancing act of putting one thing on hold while moving on to another (such is life).  Four out of five is not all that bad.  So why did that one problem give me such an issue.  Why has there been a sour note in the melody that was a great collegiate career?  Because I said I would finish it, that’s why.

Java coding has been taking up a lot of my nights lately.  It’s fun again to write code; just like in college.  When thinking up of things to code, the memory of this assignment came to the front of my mind.  It was time.

After reviewing the C++ I had written 8 years ago, it was obvious that it was close to working.  There was just a piece or two missing.  Perhaps that is another reason why this had been stuck in my mind.   The other thing that was obvious is that I name my variables much better now.  Reading my old code was nothing less than a chore.  Writing code that someone else, not myself, can maintain is something that I took for granted then.  Of course, in college, it was cheating to work with someone else on a project :) .

The rules for the application

  • The application will read input from a text file
  • The application will use Dikjstra’s algorithm to find the shortest path between all reachable nodes in the graph

Here’s a short refresher on Dijkstra’s Algorithm from Wikipedia:

Let the node at which we are starting be called the initial node. Let the distance of node Y be the distance from the initial node to Y. Dijkstra’s algorithm will assign some initial distance values and will try to improve them step by step.

  1. Assign to every node a distance value: set it to zero for our initial node and to infinity for all other nodes.
  2. Mark all nodes as unvisited. Set initial node as current.
  3. For current node, consider all its unvisited neighbors and calculate their tentative distance. For example, if current node (A) has distance of 6, and an edge connecting it with another node (B) is 2, the distance to B through A will be 6+2=8. If this distance is less than the previously recorded distance, overwrite the distance. All unvisited neighbors are added to an unvisited set.
  4. When we are done considering all neighbors of the current node, mark it as visited. A visited node will not be checked ever again; its distance recorded now is final and minimal.
  5. The next current node will be the node with the lowest distance in the unvisited set.
  6. If all nodes have been visited, finish. Otherwise, set the unvisited node with the smallest distance (from the initial node, considering all nodes in graph) as the next “current node” and continue from step 3.

Design:

  • There are two main objects in play, an Edge and a Node.  A Node is a destination and an Edge describes how to get there.
  • To accomplish #5 above, a PriorityQueue is used.  It is a sorted Queue.
  • There is another requirement that was part of the problem.  There are two types of graphs, one that has bi-directional nodes and one that has directional nodes.  Instead of creating two Edge objects with a source and a destination, I decided to make the Edge object less directional sounding.  It has a leftNode and a rightNode.  That way, in a directional graph, left is before right.  In a bi-directional graph the direction can be considered ambiguous.

Changes if this Were Production Code:

  • The nodeId is used all over the place.  In order to make this code apply to more situations, the Id should be a generic type
  • Better comments

What I Learned:

  • HashMaps are very intuitive.  Give a key, get a value.
  • If at first you don’t succeed…..but 8 years later… really?

The Code (Save all files to the same directory):
Save as Main.java

import java.io.BufferedReader;
import java.io.Console;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class Main {

	//These would be in a Value Object if Object orientation was a concern
	Queue<Node> unsettledNodes = new PriorityQueue<Node>();
	Map<Integer,ArrayList<Edge>> edgeMapKeyedOnNodeId = new HashMap<Integer,ArrayList<Edge>>();
	Map<Integer,Node> nodeMap = new HashMap<Integer,Node>();
	String uOrD;

	Integer INFINITE_DISTANCE = Integer.MAX_VALUE;

	public static void main(String... args)
	{
		Integer choice = -1;
		while (choice != 0)
		{
			Console console = System.console();
			String input;
			if (console != null)
			{
				console.printf("Enter in the file number to open 1-3 or 0 to exit\n");
				input = console.readLine();
			}
			else
			{
				input = "3";
			}
			choice = Integer.parseInt(input);

			if(!choice.equals(0))
			{
				Main main = new Main();
				Integer numberOfNodes = main.readFileInput(choice);
				Integer startNodeId =  main.getStartNode(numberOfNodes);
				main.runDijkstra(startNodeId);
				main.printPaths();
			}

			if (console == null)
			{
				choice = 0;
			}
		}
	}

	/**
	 * Reads in the file and populates the graph variables
	 * @param fileNumber the number of the file to load from
	 * @return the number of nodes in the graph
	 */
	private Integer readFileInput(Integer fileNumber)
	{
		Integer numberOfNodes = 0;
		try
		{
			FileReader fr = new FileReader("edge_weights" + fileNumber);
			BufferedReader bfr = new BufferedReader(fr);

			numberOfNodes = Integer.parseInt(bfr.readLine());
			numberOfNodes --; // 0 base it for ease of use
			uOrD = bfr.readLine(); //Directed or Undirected
			String nodeLine;
			ArrayList<Edge> edgeList = null;

			while ((nodeLine = bfr.readLine()) != null)
			{
				Scanner scanner = new Scanner(nodeLine);
				Edge edge = new Edge();
				edge.setVertexLeft(scanner.nextInt());
				edgeList = edgeMapKeyedOnNodeId.get(edge.getVertexLeft());
				if (edgeList == null)
				{
					edgeList = new ArrayList<Edge>();
				}
				edge.setVertexRight(scanner.nextInt());
				edge.setWeight(scanner.nextInt());
				edgeList.add(edge);
				edgeMapKeyedOnNodeId.put(edge.getVertexLeft(), edgeList);
				if (uOrD.equals("U"))
				{
					//Add the same edge to the possible edges from the right vertex
					//The same edge object is put in the left and right maps when in unordered mode
					edgeList = edgeMapKeyedOnNodeId.get(edge.getVertexRight());
					if (edgeList == null)
					{
						edgeList = new ArrayList<Edge>();
					}
					edgeList.add(edge);
					edgeMapKeyedOnNodeId.put(edge.getVertexRight(), edgeList);
				}
			}
			bfr.close();
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return numberOfNodes;
	}

	/**
	 * Prompt the user for a start node
	 * @param numberOfNodes the number of nodes in the graph
	 * @return the start node the user entered
	 */
	public Integer getStartNode(Integer numberOfNodes)
	{
		Integer startNode = -1;
		Console console = System.console();

		while (startNode < 0 || startNode > numberOfNodes)
		{
			if (console != null)
			{
				console.printf("Enter a start node 0-" + numberOfNodes + "\n");
				startNode = Integer.parseInt(console.readLine());
			}
			else
			{
				startNode = 0;
			}
		}
		return startNode;
	}

	public void runDijkstra(Integer startNodeId)
	{
		Node node = new Node();
		node.setNodeId(startNodeId);
		node.setWeight(0);
		nodeMap.put(node.getNodeId(),node);	

		Integer totalComparisons = 0;
		while (node != null)
		{
			if (edgeMapKeyedOnNodeId.get(node.getNodeId()) != null)
			{
				for(Edge edge : edgeMapKeyedOnNodeId.get(node.getNodeId()))
				{
					Node nextDestination;
					Integer nextDestinationId = null;
					//If unordered the id of the next destination may be the right or the left vertex
					if (uOrD.equals("U"))
					{
						nextDestinationId = edge.getOppositeVertex(node.getNodeId());
					}
					else
					{
						//If ordered, the destination is always the right vertex
						nextDestinationId = edge.getVertexRight();
					}

					if (nodeMap.get(nextDestinationId) == null)
					{
						nextDestination = new Node();
						nextDestination.setNodeId(nextDestinationId);
						nextDestination.setWeight(INFINITE_DISTANCE);
					}
					else
					{
						nextDestination = nodeMap.get(nextDestinationId);
					}

					System.out.println("Calculating distance From " + node.getNodeId() + " To: " + nextDestination.getNodeId() + " Traversed: " + nextDestination.getTraversed());
					System.out.println("Current Node Distance: " + node.getWeight() + " Edge Weight: " + edge.getWeight() + " Against " + nextDestination.getWeight());

					if(!nextDestination.traversed)
					{
						totalComparisons ++;
						if(nextDestination.getWeight() > node.getWeight() + edge.weight)
						{
							System.out.println("Found a shorter path to " + nextDestination.getNodeId());

							nextDestination.setWeight(edge.weight + node.getWeight());
							nextDestination.setPredecessor(node);
							nodeMap.put(nextDestination.getNodeId(),nextDestination);
							unsettledNodes.offer(nextDestination);
						}
					}
				}
			}
			node.traversed = true;
			node = unsettledNodes.poll();
		}

		System.out.println("Total distance comparisons: " + totalComparisons);
	}

	/**
	 * Print out all of the paths to all of the possible destinations
	 */
	public void printPaths()
	{
		for(int i = 0; i<= nodeMap.size(); i++)
		{
			if (nodeMap.get(i) != null)
			{
				List<Node> shortestPath = getShortestPathTo(nodeMap.get(i));
				System.out.println("Shortest path to node index " + i);
				for (Node point : shortestPath)
				{
					System.out.println(point);
				}
			}
		}
	}

	/**
	 * Returns a list containing the nodes that are in the shortest path to the destination
	 * @param destinationNode
	 * @return
	 */
	public List<Node> getShortestPathTo(Node destinationNode)
	{
		List<Node> path = new ArrayList<Node>();
		for (Node lastNode = destinationNode; lastNode.getPredecessor() != null; lastNode = lastNode.getPredecessor())
		{
			path.add(lastNode);
		}

		Collections.reverse(path);
		return path;
	}

	/**
	 * This is the representation of an edge.  Note that is does not imply direction.
	 *
	 */
	class Edge
	{
		Integer vertexLeft;
		Integer vertexRight;
		Integer weight;
		public Integer getVertexLeft() {
			return vertexLeft;
		}
		public void setVertexLeft(Integer vertexLeft) {
			this.vertexLeft = vertexLeft;
		}
		public Integer getVertexRight() {
			return vertexRight;
		}
		public void setVertexRight(Integer vertexRight) {
			this.vertexRight = vertexRight;
		}
		public Integer getWeight() {
			return weight;
		}
		public void setWeight(Integer weight) {
			this.weight = weight;
		}
		@Override
		public String toString()
		{
			String stringed = "Left: " + vertexLeft + " Right: " + vertexRight + " Weight: " + weight;
			return stringed;
		}

		public Integer getOppositeVertex (Integer vertex)
		{
			Integer oppositeVertex = null;
			if (vertex.equals(vertexLeft))
			{
				oppositeVertex = vertexRight;
			}
			else
			{
				oppositeVertex = vertexLeft;
			}
			return oppositeVertex;
		}
	}

	/**
	 * This is a representation of a node on the graph
	 */
	class Node implements Comparable<Node>
	{
		Integer nodeId;
		Boolean traversed = false;
		Integer distanceFromStartNode = Integer.MAX_VALUE;
		Node predecessor;

		public Node getPredecessor() {
			return predecessor;
		}
		public void setPredecessor(Node predecessor) {
			this.predecessor = predecessor;
		}
		public Integer getNodeId() {
			return nodeId;
		}
		public void setNodeId(Integer vertexStart) {
			this.nodeId = vertexStart;
		}
		public Boolean getTraversed() {
			return traversed;
		}
		public void setTraversed(Boolean traversed) {
			this.traversed = traversed;
		}
		public Integer getWeight() {
			return distanceFromStartNode;
		}
		public void setWeight(Integer weight) {
			this.distanceFromStartNode = weight;
		}
		@Override
		public int compareTo(Node o) {
			return this.getWeight().compareTo(o.distanceFromStartNode);
		}
		@Override
		public String toString()
		{
			return ("Node Id:" + nodeId + " Weight: " + distanceFromStartNode);
		}
	}
}

Save as edge_weights1

6
U
0 1 800
0 2 2985
0 3 310
0 4 200
1 2 410
1 3 612
2 3 1421
3 4 400
1 5 100
5 4 100

Save as edge_weights2

5
D
1 0 800
2 0 2985
0 2 2985
0 3 310
4 0 200
1 2 410
2 1 410
3 1 612
1 3 612
2 3 1421
3 2 1421
3 4 400

Save as edge_weights3

10
U
1 0 3
2 1 17
2 3 2
8 9 1
0 7 2
8 0 13
7 8 10
7 6 5
6 8 4
5 6 3
4 5 15
4 9 12
3 9 9
2 9 6
4 3 3

Run (in the same directory as all of the files):

javac Main.java
java -cp . Main

Tags:

It has been a while since running became a hobby. About a year now. Along the way, I’ve been setting up goals for myself to meet or exceed. This particular one was the most ambitious this far. On May 14, 2011, I accomplished this goal.

The decision to go for this goal was due to a compromise. A 5k was seeming too short and a marathon too long. A 25k seemed like a good compromise for a newbie runner like myself. I work with who have run the race before and gave me positive reviews. I would like to echo them and say that if you’re considering the race, it is quite a rewarding experience.

The under two hours part of the goal was more of a personal decision. If I’m doing this, I’m doing it good. There is also special recognition for those who are able to beat the 2 hour mark. If I run next year, I get a bib that acknowledges the achievement. I also knew what shape I was in. A goal of 2 hours would mean that a lot of work would be required; it was going to be tough.

Like most things, the preparation was 90% of the race. On the Riverbank Run’s website there is a training schedule that I tried to follow. Because of my lofty goals, I decided to follow the expert running schedule. At the peak of training, I was running for 45-50 miles a week.

The time commitment was larger than expected. Most of my Saturday was spent recovering from a 12-15 mile run. This gets especially rough when your wife decided to throw a surprise birthday party involving 70 people and indoor rock climbing. :) The time it takes doesn’t just count time spent on the road. There is time to stretch, time to dress, time to clean, and time to recover. Looking back at it, I’m glad that I opted for a distance shorter than a marathon.

On raceday, I was buzzing. The weather was perfect for a long run. Out of the gate, the wind was at my back. The first 8 miles were fast. Probably a little too fast. Around mile 11, we had turned back, the wind was against us, my times began to slow down. Along the side of the road, there were people, lots of them towards the last two miles. There were people from the community, cheerleader squads, and military service members. I, of course, didn’t take water from the service members, but gave them some applause as I ran by. They had already given enough.

It was due to the people who were cheering and the volunteers that I kept pushing. There was some slack in my goal time, but why not do better? I finished strong, going uphill, with people cheering all over.

*Splits*
Mile: Pace in Minutes per Mile
1: 7.11 — Watch lost my position during this time. This distance isn’t totally accurate
2: 7:20
3: 7:21
4: 7:26
5: 7:15 — Passed the 7:30 pace guy here
6: 7:21
7: 7:17
8: 7:21
9: 7:36 — Switched back and lost the wind. Also a bit more hilly
10: 7:41
11: 7:35
12: 7:46
13: 7:35
14: 7:37
15: 7:42
15.57 7:30

Being healthy helps me to enjoy my life. I would recommend that everyone who has the ability, set up some goals and get out on the road. Life is too short to sit! There are lots of running clubs out there. The ones that I ran with were awesome and encouraging. Find one in your area and get to it.

Tags:

This is example code to do a SFTP file copy using the JSCH Java library.  There are a few libraries in this arena, and all of them seem more complex than they are.  JSCH doesn’t have the best documentation along with it, but it is maintained and in the Maven repositories.

The key file just needs to be somewhere in the classpath for this to work.    The code is actually quite short.  In the actual version of this code, there is an object that has all of the values that is needed for this method and a verification routine to make sure that they are valid.

Note that the SFTP session is always connected regardless is the file to be copied doesn’t exists.  This is by design so that if there is an issue with the SSH server, an error will be received even if there is no file to copy.

JSch.setLogger(new JSCHLogger()); // More on this below
JSch jsch = new JSch();
Session session = null;
ChannelSftp channel = null;
FileInputStream localFileStream = null;

try
{
//Use key authentication if it is set, else use password auth
if (YOUR_KEY_FILE_NAME != null &amp;&amp; YOUR_KEY_FILE_NAME != "")
{
URL keyFileURL = this.getClass().getClassLoader().getResource(YOUR_KEY_FILE_NAME);
if (keyFileURL == null)
{
throw new RuntimeException("Key file " + YOUR_KEY_FILE_NAME  + "not found in classpath");
}
URI keyFileURI = keyFileURL.toURI();

jsch.addIdentity(new File(keyFileURI).getAbsolutePath());
session = jsch.getSession(YOUR_SSH_SERVER_USER_NAME, YOUR_SSH_SERVER_NAME , YOUR_SSH_SERVER_PORT);
}
else if (YOUR_SSH_PASSWORD != null &amp;&amp; YOUR_SSH_PASSWORD != "")
{
session = jsch.getSession(YOUR_SSH_SERVER_USER_NAME, YOUR_SSH_SERVER_NAME , YOUR_SSH_SERVER_PORT);
session.setPassword(YOUR_SSH_SERVER_PASSWORD);
}

//Make it so we do not do host key checking.  Enabling this would require some extra code and maintenance, but would increase security.
session.setConfig("StrictHostKeyChecking", "no");
session.setTimeout(15000);

session.connect();

channel = (ChannelSftp)session.openChannel("sftp");
channel.connect();

if (YOUR_SSH_SERVER_FILE_DIRECTORY != null &amp;&amp; YOUR_SSH_SERVER_FILE_DIRECTORY != "")
{
channel.cd(YOUR_SSH_SERVER_FILE_DIRECTORY);
}

File localFile = new File (YOUR_LOCAL_FILE_TO_COPY + YOUR_LOCAL_FILE_TO_COPY);
if (localFile.exists())
{
localFileStream = new FileInputStream(localFile);

channel.put(localFileStream, YOUR_REMOTE_FILE_NAME));
}
else
{
log.warn("Local file not found " + localFile.getAbsolutePath());
}
}

There is one more thing that needs to be done.  JSCH has its own internal logging that isn’t automatically connected to the program’s logger.  This code ties them together.

public static class JSCHLogger implements com.jcraft.jsch.Logger
{

static java.util.Hashtable&lt;Integer,String&gt; name = new java.util.Hashtable &lt;Integer,String&gt; ();

static
{
name.put(new Integer(DEBUG), "DEBUG: ");
name.put(new Integer(INFO), "INFO: ");
name.put(new Integer(WARN), "WARN: ");
name.put(new Integer(ERROR), "ERROR: ");
name.put(new Integer(FATAL), "FATAL: ");
}

public boolean isEnabled(int level)
{
return true;
}

public void log(int level, String message)
{
if(level == DEBUG)
{
log.debug(message);
}
else if(level == INFO)
{
log.info(message);
}
else if(level == WARN)
{
log.warn(message);
}
else if(level == ERROR)
{
log.error(message);
}
else if(level == FATAL)
{
log.fatal(message);
}
}
}

Tags: , ,

So I’m mixing it up a little bit.  Today’s example prints from a StringBuffer and not a File.  Printing from a File should be pretty similar.  This is useful whenever something needs to be printed out for auditing purposes.

There is some code in here to get around the limitations of a page.  The number of lines on a page and the font size are part of this.  In practice, if the line in the StringBuffer is bigger than the page, it will just disappear.    In this case, the lines were already formatted to be a length that did not skip off of the page.

The code treats the page as a canvas and writes to specific part of that canvas.  If you use this code, be sure to pay attention to all of the settings that inpact the way the text appears on the page.  Some changes will impact other settings.  For example, if the font size is increased, the spacing between lines should increase.

This isn’t particularly object oriented as it is an example.

PrintService printService = PrintHelper.getPrintService(YOUR_PRINTER_NAME);
PrintRequestAttributeSet printRequestAttributeSet = new HashPrintRequestAttributeSet();
printRequestAttributeSet.add(new JobName(YOUR_JOB_NAME,Locale.getDefault()));
PrinterJob pjob = PrinterJob.getPrinterJob();
pjob.setPrintService(printService);

PDDocument document = new PDDocument();
//To load a file do this:   pd = PDDocument.load(input);
PDPage page = new PDPage();
document.addPage( page );
PDFont font = PDType1Font.HELVETICA_BOLD;
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setFont( font, 12 );
//The first number in moveTextPositionByAmount is to provide a left margin.
//The second number in moveTextPositionByAmount is to miss the header
contentStream.moveTextPositionByAmount( 100, 550 );
StringReader strReader = new StringReader(printText.toString());
BufferedReader strBufRdr = new BufferedReader(strReader);
String line = "";
Integer linesPrinted = 0;
while((line=strBufRdr.readLine()) != null)
{
contentStream.drawString(line);
linesPrinted++;
//The second parameter will change if the font changes.
//It's something that has to be guessed and checked to get right.
contentStream.moveTextPositionByAmount( 0, SPACE BETWEEN LINES -15 SHOULD BE GOOD);
if (linesPrinted % INTEGER_FOR_LINES ON A PAGE 30 SHOULD BE GOOD == 0)
{
contentStream.endText();
contentStream.close();
page = new PDPage();
document.addPage( page );
contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setFont( font, 12 );
contentStream.moveTextPositionByAmount( 100, 550 );
}
}
contentStream.endText();
contentStream.close();
//For testing, write this to a file
//document.save("outpdf.pdf");
document.silentPrint(pjob);
document.close();
}

catch (PrinterException e)
{
//Handle Error
}
catch (IOException e)
{
//Handle Error
}
}

Tags: , ,

Printing is not an easy task.  It is easy to get tied into intricate details of environment specific configuration settings such as margins and paper size. Therefore, printing a PDF without a library is not recommended unless you have a lot of time or are really interested in that sort of thing.  There are two libraries that can perform this task already.   One will be exampled today, and another tomorrow.

Today’s library is PDFRenderer https://pdf-renderer.dev.java.net/.   This library was released by Sun a couple of years ago.   Note that this example does not map a FileChannel object as some examples do.  Using a FileChannel made the file not able to be deleted until garbage collection was run.  See the bug in the code comments for more details.

try
{
File f = null;
RandomAccessFile fis = null;
FileChannel fc = null;
ByteBuffer bb = null;
String printer = YOUR_PRINTER_NAME;
PrintService printService = PrintHelper.getPrintService(printer);

f = YOUR_PDF_FILE;
//Read only access would work too
fis = new RandomAccessFile(f, "rw");
fc = fis.getChannel();
bb = ByteBuffer.allocate((int)fc.size());
fc.read(bb);


//Do not map the file to a ByteBuffer as the examples show.
// There is a reason why in java bug #474038
// http://bugs.sun.com/view_bug.do?bug_id=4724038
//fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());
//bb = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());

PDFFile pdfFile = new PDFFile(bb); // Create PDF Print Page
PDFPrintPage pages = new PDFPrintPage(pdfFile);
// Create Print Job
PrinterJob pjob = PrinterJob.getPrinterJob();
pjob.setPrintService(printService);

PageFormat pf = PrinterJob.getPrinterJob().defaultPage();

pf.setOrientation(PageFormat.PORTRAIT);

Paper paper = new Paper();

//This is to fix an error in PDF-Renderer
//View http://juixe.com/techknow/index.php/2008/01/17/print-a-pdf-document-in-java/ for details
//Printing a PDF is also possible by sending the bytes directly to the printer, but
//  the printer would have to support it.

paper.setImageableArea(0,0,paper.getWidth() * 2,paper.getHeight());

pf.setPaper(paper);

pjob.setJobName(f.getName());

Book book = new Book();
book.append(pages, pf, pdfFile.getNumPages());
pjob.setPageable(book);
pjob.print();

}
catch (FileNotFoundException e)
{
//do your error action
}
catch (IOException e)
{
//do your error action
}
catch (PrinterException e)
{
//do your error action
}
finally
{
try
{
if (fc != null)
{
fc.close();
fc = null;
}
}
catch (IOException e)
{
log.error(e);
//handle error here
}
try
{
if (fis != null)
{
fis.close();
fis = null;
}
}
catch (IOException e)
{
//handle error here
}
if (bb != null)
{
bb.clear();
}
}

Because this library has the bug that makes it not want to print in half size when printing a portrait-oriented PDF,  I cannot recommend using this library unless you find it is the only thing that will do the job.    Such obvious bugs that have been around for over a year point to unmaintained code or some other upstream issues.  Tune in tomorrow for another library that is maintained that will get the job done as well.

Tags: , ,

Six months since the last post to this blog.  What a half-year it has been.   After getting a few emails from friends about how life is going, I thought it best to summaries the top   Here’s the top 5 things I’ve been up to that have nothing to do with technology.

Running

My best 5k time this year was in the low 21 minute range.  My first 5k time was in the upper 24 minute range.

Running has been a great way to provide relaxation and competition at the same time.  Everyone is in the same boat when you are running.   The road provides the opposition and everyone has to overcome it.

Thanks to the amount of running I’ve been doing, I’m as healthy as ever.  Running has been good to me,  I’m optimistic that this will be something that will stay part of my life for a while.

Moving

The amount of fun in moving is inversely proportional to the amount of things you have.   Thankfully, we run things lean and try to keep as few non-essentials around as possible.

The new house is twice as big as any place any other place I’ve owned or rented.   The neighbors are further than a wall away, and it’s great to see families working around on the sidewalk.  We estimated 250 trick-or-treaters this year.

Mowing My Lawn

Home ownership and buying comes with some additional cost.  This is especially true when the rooms in the house comes in shades of pink and red and have words written in the paint.  We are two recarpeted rooms away from getting rid of some unpleasant odor.

Of course, Cat 6 cable has been run throughout the house, and we are still working on some various odds and ends to make this place the way we like it.

Motorcycling

It runs in the genes.  Riding in the country is great.  Getting lost and attempting to avoid gravel roads is great weekend fun.   This was the first time I got to ride with my Dad and Brother.  This leads me to…

Spending Time With Family

Lots of time to make up in this regard.  It’s going to take a while to catch up.   :)

This blog has moved.  The URL is the same, but its location in the world has changed.  It’s now hosted at a 3rd party site instead of being at my residence.   Here’s why.

I am not a business:

Carries argue that you are a business if you require a static IP address.   In fact, static IP addresses are not available in most carriers’ standard plans.  They assume that people who subscribe to their service are content consumers and not content containers.  Businesses, on the other hand, are assumed to be content containers and are permitted to have a static IP address.

This wouldn’t be an issue if there wasn’t such a dramatic price differential.   Plans that contain static IP addresses are two times as much as their counterparts.  Why?  In my case, this makes no sense.  There is no profit motive behind what I would do with the IP.  Why am I considered a business?

A good solution, from a consumer standpoint, would be to separate users into different classes.  There are plenty of customers who do need the firewalling and don’t mind the dynamic IP that basic plans provide.  However, these features are just a nuisance to advanced users.  I would gladly pay $10 a month for a static IP.  Make it an option to add to the  plan.

Internet companies are potentially loosing money because they are not providing the services people want.  A $40 basic plan vs a $80 business plan is a no-brainier, but if there was a $60 option in there…..

I am not a hosting company:

There are things that I can do better than the hosting company and things that I do poorly.  Daily SQL backups, running in a dedicated Xen VM, chrooting the Apache server, as much processor as I can use, and the availability of any piece of _free_ software I want to install, are all benefits of having a server at home.  The technical word for it is a playground.  I can do anything I want or am able to do (which is ~anything).

The hosted world provides better uptime, better speed, and manages the system and network administration.  The best part about hosting is the cost.  It’s $7 a month for me to host this and as many other sites that I’d like to build.

I am not average:

Giving up the network administration and the system administration was a tough decision for me.  It has been fun.  Everyone running DD-WRT using VLANS, custom firewall rules, and OpenVPN understands.  Likewise, everyone running XEN on a VLANed host, with more customer firewall rules, and mod_security understands.

But why:

It was fun to host, but did it amount to anything?  The skills I picked up aren’t ones that I use on a daily basis anymore.  I haven’t risen to celebrity status, or really had that many visits (this is more of a content issue).  It was a good amount of fun while it lasted, now I’ve been there, done that, and I could do it again.  But why?

Tags: ,

I am finally a cell phone owner.  It took about 9 months before something that could justify the exceptional expense that cell phones cost.

The experience of buying and owning a cell phone involves several parties.  The parties involved are the reseller, the service provider, and the phone itself.  I’m going to break these 3 part out for this review.

The provider:

This was the easiest decision for me.  Sprint has the cheapest plans right now.  Plus, I get a 15% discount through an employer perk.   The coverage isn’t that great, but it is good enough.  My overall rating of Sprint is a 6/10.

The reseller:

Best Buy was first on my list of places to buy the phone because they handle the rebates for you, instantly.  No waiting 6-8 weeks, no mailing out 17 pieces of information, they just handle it.  Plus, I will never go to a Sprint store again due to the awful customer service I’ve had with them.

I would have no problem buying a cell phone from Best Buy again.  They seem to care more than the Sprint store does about customer satisfaction.   There were two problems I had with the phone that they helped to clear up with me.  I think that these stories sum up my experience well.

The first issue I had was the phone went on sale three weeks after I bought it.  I was still looking in the fliers after purchase to see if the phone would go on sale.  It did.  The phone went from $179.99 to $99.99.   I was still in the first month of my plan, so I could cancel the phone and buy a new one for the reduced price.  This option would’ve involved quite a bit of hassle, so I was relieved when Best Buy credited my credit card with the difference without issue.  After the horrible experience I had at the Sprint store with the Palm Pre, this was _really_ a nice change.

The second issue I had was a phone issue.  The battery life on the phone wasn’t up to my standards.  I took it to Best Buy and they replaced the battery for me.  No prying questions, no acting like I was the problem; they just took a battery from another phone and gave it to me.

The phone:

I’m going to cover Android here to.  The phone and the software that run it are, warranty wise, inseparable.

The draw to the phone was two-fold.  I wanted a Android phone.  I wanted a keyboard.  The Samsung Moment was the only phone that meets these requirements that Sprint carries.

Android has been good but not great.  The Moment runs version 1.5 of Android.  I’ve found it buggy at times.  The default setup is odd.  For some reason, the GPS is on by default.  This will cause poor battery life out of the box.

Android has a great app store.  The app store is the main reason that the phone and platform are a buy.  I can download a million or so ringtones and wallpapers for free.  There are fun games to play as well to burn all of that spare time we have.  There are apps for Facebook, sports scores, alcoholic beverage creation, and for reading the US Constitution.  The amount of apps is staggering.  The quality of the apps is always iffy.  I’m only installing the top rated and downloaded apps.  Don’t be surprised if your phone crashes when using an unpopular or unsanctioned app.

The phone comes with demo applications that cannot be uninstalled.   Google makes money off of your personal information.  This comes through in their phones as well.  There is no option to not sync contacts.  If you don’t want Google to know about your friends, don’t buy one of their phones.  One of the best examples of Google trickery is the GPS setting.  There are two ways to do GPS, wireless networks, and GPS satellites.  The description under wireless networks reads: “See location in application (such as Maps) using wireless networks”.  The description under the GPS satellites reads: “Locate to street-level (requires more battery plus view of sky)”.  The descriptions are true, but biased.  Clicking on the Use wireless networks setting reveals why.  A consent form appears stating: ” Allow Google’s location service to collect anonymous and aggregate location data.  Collection will occur regardless of whether any applications are active.”  This means if this setting is on, you become a data provider for Google.  No, they don’t pay you for the information you provide.

The phone is mechanically great.  The non-slide backing feels really good in my hands.  The overall build quality is quite good.  I would rather have tactile buttons rather than the touch sensitive buttons at the bottom of the screen.  The screen is nice and bright.  It looks like a bigger screen would’ve fit in the same form factor.   I would’ve preferred the screen to fill out all the space instead of having a border.  The keyboard is good and has nice raised keys, keys for numbers, and a directional pad.  I’ve read some criticisms about the touchpad button.  I actually like it as a concept, but the implementation is poor.  The OS is slow to recognize movement on the touchpad, which makes it difficult to use.

Now for the Achilles’s heel, battery life.  The battery life of this phone is just aweful.  This phone should be thought of as more of a laptop in terms of battery life.  It is that bad.  I’ve followed all of the tips in the forums and still only get about 13 hours of standby time.  It’s bad enough where you have to plan a day around it.  If you stay at work late, or have a long drive home, the phone might die before you get there.  Connecting a personal phone to a charger at work is an annoyance and not the message I want to give to my employer.  Even using the GPS in the car makes me worry about when the next charge will have to come.  Its bad enough where it puts a shroud over all of the good features the phone actually has.  This phone has roughly half of what I would consider decent batter life.

Like I mentioned before, I’m keeping the phone.  It does what I want it to do, and I can cost-justify it.  However; i am, getting three phone chargers for Christmas and await a higher capacity battery with great anticipation.

Tags: , , , ,

« Older entries § Newer entries »