How to Use SSH to Access a Linux Machine from Windows

Posted on November 21 2012 09:11 AM by John Atten in   ||   Comments (0)

NOTE: I do a LOT of handholding in this article. I wanted to be sure someone who is less-than-familiar with the Linux Command Line Interface and/or SSH would have no trouble understanding what is going on. Here are some navigation links to more readily find what you need:

Getting to Know Linux

I have recently undertaken to learn how to develop on the Linux platform. I grew up in the Windows world, and decided it was time to expand my horizons. After all, the vast bulk of the web runs on some variant of Linux, and some of today’s most in-demand web application development occurs in Ruby on Rails, which is most at home on a Linux machine.

What I have found so far is that there is a whole lot to like about this strange new OS, and the underlying philosophies and tools which form the core of the Linux experience.

One of the very first things I wanted to do was learn how to access a Linux box remotely from my Windows desktop. The first step in this process was understanding how to set up a Secure Shell (SSH) connection. At first it might seem this is a little redundant, since my Linux machine sits about 9 feet away from my Windows development box. However, this is a necessary first step in order to eventually be able to:

  • Spin up a Linux VM instance on a IAAS/cloud service such as Amazon EC2 and/or Windows Azure, and control/access/utilize it from my windows desktop.
  • Set up and manage a hosted Linux server for deployment of web applications/sites
  • Understand remote access in a Linux environment

Of course, on top of those items in the list above, this was also a good exercise to get me started doing useful things with my new Linux machine!

Minor Linux Familiarity Required

We will need to perform some Linux commands via the Bash Command Line Interface (CLI), both on our Linux machine, and through our remote connection once we get it set up. If, like myself, you are new to Linux, you may want to review some Bash basics. While the posts linked to below are parts of a series on using Git version control, each of the following focus on basic Bash commands used to navigate and manipulate files in a Linux system.

Review Basic Bash Navigation:

Setting up the Linux Machine

I am using Linux Mint for my first forays into the Linux world. Mint is built atop Ubuntu, and features a friendly GUI for those of us just getting started. However, for me, the GUI is mainly for those “oh shit, I don’t know what to do” moments. One of my main purposes in setting up this machine was to utilize the Bash terminal as much as possible, and master this aspect of this new platform. In fact, we are going to perform this entire exercise using the Bash terminal when interacting with the Linux box.

For this post, I am dealing with a local Linux machine, on my home network behind a router. In another post, I will discuss exposing the machine to the outside world via the internet.

Installing OpenSSH on your Linux Machine

Having recently installed a fresh Mint OS, the first thing we have to do is install an SSH server. Your Linux Distro may or may not come pre-configured with OpenSSH, which I understand is the SSH server of choice in Linux-land.

Linux distro’s based on Debian-flavored Linux use the apt-get install newSotwareTitle to find and download software packages, and/or confirm and update existing installations if they are already present on your machine. So, let’s use apt-get to download OpenSSH on our Linux machine (note - this post assumes you have super-user/administrative permissions on your Linux machine):

Install OpenSSH:

Open the Bash terminal on your Linux machine, and type the following command and hit enter. Since you are using sudo to perform this action, be ready to enter your password prior to execution:

Install OpenSSH Server:
$ sudo apt-get install openssh-server

 

In my case, OpenSSH was already present on the system, so my terminal output looks like this:

install_openssh_server_thumb3

If your machine did not have OpenSSH Server installed, the terminal will ask that you confirm installation/update of any number of packages. Type “Y” and hit enter. You will then see the terminal window populate with the actions taken and packages added.

Now, we could do some of the SSH server configuration right now at our Bash terminal. However, instead, I am going to move over to the Windows side, and do the rest of the configuring from there, by way of an SSH connection.

The Most Common SSH Client for Windows - PuTTY (no, that is not a typo)

Use of the SSH protocol is less common in the Windows universe then in Linux. However, the most popular SSH client for use on a windows machine is PuTTY, an open source terminal emulator which can act as a client for SSH, Telnet, and other protocols. To get started setting up your SSH client on Windows, visit the PuTTY download page, download and install putty on your machine. The easiest way to go is to download the putty-0.62-installer.exe package, which includes everything required for our purposes:

Once the download completes, run the installer.

Use PuTTYGen to Create a Public/Private key pair for your Windows client machine

SSH utilizes Key-based authorization to ensure the security of a connection. A simple description of how this works (From Wikipedia):

SSH uses public-key cryptography to authenticate the remote computer and allow it to authenticate the user, if necessary.[1] Anyone can produce a matching pair of different keys (public and private). The public key is placed on all computers that must allow access to the owner of the matching private key (the owner keeps the private key secret). While authentication is based on the private key, the key itself is never transferred through the network during authentication.

[Read More]

For our purposes, we will use the Handy PuTTYGen utility installed with our PuTTY package to create our keys. Open PuTTYGen (Start Menu --> PuTTY (Folder) --> PuTTYGen (application)) and you should see the following:

PuTTYGen, Ready to Create a Public/Private Key Pair:

PuTTY-Key-Generator-Before-Generate_

Leave the settings at their defaults, and click the “Generate” button. PuTTYGen will request that you move your cursor about in the large empty area in order to ad some “randomness” to the process (and in fact will pause generation until you DO):

PuTTY-Key-Generator-On-Generate_thum

When key generation is complete, you will be presented with some additional settings to complete before saving your keys:

PuTTY-Key-Generator-Comment--Passwor[2]

Complete the Following Items in the Generator Form as follows:

  • Key Comment can technically be anything you like, but convention is to use your email address
  • The Key Passphrase is not required, but is strongly recommended as an additional level of security, just in case anyone were to get hold of your private key. Use a reasonably strong (but easy to remember) pass word here.

Once you have completed these items, it is time to save your keys. First, I would create a directory (folder) in your Windows User Folder named “SSH Keys” in which to store your private keys. Then, click the “save private key” button and save the key there.

NOTE: Don’t use the “Save Public Key” feature. OpenSSH expects the public key in a slightly different format than PuTTYGen provides, so instead, we are going to copy the key as presented in the PuTTYGen window straight into our authorized_keys file once we log into our Linux machine.

ALSO NOTE: It is not necessary to save the public key which corresponds to the private key we just made, because we can use the PuTTYGen “Load” button to load our private key, which will then also load the proper public key data back into the Public Key Window for copying once again.

Leave the PuTTYGen Window open, and lets configure PuTTY for our first login.

Configure PuTTY for the Initial Login

The first time we log in to our Linux machine, we will use plain old password authentication, so that we can pass our public SSH key directly over the (relatively) secure connection, and avoid exposing it in a way which might result in someone else getting ahold of it. This is also good practice in a number of different ways.

Open the PuTTY application (Start Menu --> PuTTY (folder) --> PuTTY (application))

Enter the IP Address of your Linux Machine:

PuTTY-First-Login-Enter-Ip-Address_t

As you can see in the picture above, enter the IP Address of the Server machine (your Linux box). If you don’t know what the IP address of your Linux machine is, follow this link:

Leave the port specification as the default value of 22 (this is the standard port used for SSH logins). While there are potential security reasons to change this later, for now it will do.

Next, in the tree menu to the left, select the Connection/Data node and enter your user name you use to log in to the Linux machine (REMINDER - we are assuming your user profile includes super-user permissions):

Enter your User Name in the Connection/Data node form:

PuTTY-Configuration-add-user-name_th

Leave the rest of the settings at their default values (as shown above). Now, go back to the Session node, and enter a name for this configuration in the “Saved Sessions” space, then click “Save. In my case, I saved this session configuration using the IP address, and a brief description of the configuration:

PuTTY-Configuration-Save-Pwd-Auth_th

First Remote Login to your Linux computer with Password Authentication

Ok, with those details tended to, click on the “open” button. This first time we log in, you will likely be presented with a warning dialog telling you that there are no keys cached for the server you are attempting to connect to, and do you wish to cache them now:

PuTTY-Security-Alert-Cahce-Server-Ad

Since we know this machine is on your LAN go ahead and click “Yes.” You should be presented with a terminal window that looks like this:

PuTTY-First-Login-Terminal-Enter-Use

Next, enter the password you use to log in to your Linux machine and hit enter (note that in the terminal here, the cursor does not move, nor are standard obfuscated password placeholders used - in other words, as you type your password, it will appear as if nothing is happening). Your terminal should now resemble this:

PuTTY-First-Login-Terminal-Logged-In[2]

Congratulations - you have now logged into your Linux terminal from your Windows computer. However, we are not yet using SSH, and in fact this method is not a very secure way to remotely access another machine. Next we need to set up our key-based authentication. Once we have confirmed all is well with that, we will disable the Username/Password-based authentication we are using now in favor of the much stronger key-based security.

Add Your Public Key to the Linux Machine

Your Linux system stores public SSH keys for client machines in a directory within your Linux home user folder (the .ssh directory), in the authorized_keys file. Your next step depends upon whether there is already an .ssh directory on your machine, and whether or not there is already an authorized_keys file present. We can find this out quickly enough by attempting to navigate into a directory named .ssh from within our home folder (our terminal should have opened within our home folder.

If you are not familiar with navigation and basic file manipulation in Bash (the Linux terminal), have a quick look at these two articles I wrote. The articles are part of a series on using Git, but these two focus on basic Bash shell commands useful for file and directory navigation:

First, let’s attempt to navigate into the .ssh directory on our remote Linux box. Type the following into the terminal window (note - the “$'” symbol is not typed - this is the command “prompt” and indicates that the terminal is ready for command input):

$ cd .ssh

 

If there is not already a directory named .ssh in your user folder, your terminal window should look like this:

Navigate-to-ssh-directory-not-exists[2]

If this is the case, we need to create a new .ssh directory. Type the following:

$ mkdir .ssh

 

Now your terminal should look like this:

Create-ssh-Directory_thumb4

Now let’s try navigating into the new directory:

Navigate-to-new-ssh-directory_thumb2

That’s more like it! Next, since there was no .ssh directory to begin with, we also need to create our authorized keys file. We are going to create a new file, and add our new public key all in one fell swoop. Go to the PuTTYGen window (still open on your Windows desktop), and select and copy the entire public key visible in the space labeled “Public key for pasting into OpenSSH authorized_keys file”:

PuTTY-Key-Generator-Copy-Public-Key_

Now we will use the echo command to create the new authorized_keys file, and insert the Public key for our Windows machine. The syntax of our echo command is as follows:

echo YourPublicKey >> authorized_keys

 

This command will append YourPublicKey to the file authorized_keys. If the file does not exist, it will be created (ours doesn’t exist yet. If yours DOES, don’t do this this way).

First, type the echo command into the Linux remote terminal like this:

Type-Echo-Command-In-Terminal_thumb3

Then, if you right click your mouse pointer at the terminal cursor, the contents of your clipboard (containing your newly created public key for your Windows machine) will automatically paste into the current line.

Then add the >> authorized_keys to the end, and hit the Enter key:

Type-Append-to-authorized-keys-file_

Now that we have added our public key to the Linux machine, lets end our PuTTY session, and see if we can log back in using Public/Private key authentication. If this works, we will then modify our OpenSSH server configuration on the Linux box to ONLY allow this type of authorization. Go back to the Putty window, and close it. This will end the session.

Configure PuTTY for Public/Private Key Authorization

Now, open PuTTY again, and in the tree control to the left, load your previously saved session configuration, select the Connection/SSH/Auth node. Browse to find your private key you created using PuTTYGen, and select it for use. Leave the rest of the settings at their default values for now:

PuTTY-Configuration-Load-Private-Key[2]

Next, return to the Session configuration node, and type a new name for this modified configuration. As previously, I used the IP address, in conjunction with brief configuration details. Then click “Save”:

PuTTY-Configuration-Save-SSH-Auth_th

Connect to Linux/OpenSSH Server using Public/Private Key Authorization

Ok, let’s try connecting now, using our new configuration. Click on the Open button on the PuTTY interface. You should see something like this:

PuTTY-SSH-Login-Before-Enter-Passwor

Notice that this time, PuTTY tells us it is attempting to log in using public key authentication, and prompts us for the password we associated with our key when we created it. Enter the password you used when creating the key (again, the cursor will remain still while you do this), and hit Enter:

PuTTY-SSH-Logged-In_thumb2

Congratulations! You have now logged in to your Linux machine using Public/Private key authentication. While we are connected remotely, let’s tidy up a few loose ends.

Set Permissions on Keys File to Owner/Read-Only

Now that we know our keys are working properly, let’s protect the authorized_keys file on our Linux machine so that we don’t accidentally modify or delete it. Navigate into the .ssh directory, and type the following command into the Bash terminal:

chmod 400 authorized_keys

 

This sets the permissions on our authorized_keys file so that the current user, and only the current user has read-only permissions, and no one else can even access the file (that specific user can make the file writeable for themselves again by using chmod 700).

Set-KeyFile-Permissions_thumb3

Edit the OpenSSH Configuration File to Disable Password Authentication

Now that we have a working key-based authentication scheme, we have no more need for the less-then-secure password-only security we used previously. In fact, our next step will be to edit the OpenSSH configuration file on our Linux machine to NOT allow that, and to ONLY accept key-based authentication.

First, let’s make a backup copy of the configuration file. Type cd to navigate back to your home directory (entering cd with no options or destination path returns you to the home directory by default), then create a folder in your home directory to store backups like this:

$ mkdir ssh_config_backup

 

Then, use the following command to make a copy of the configuration file in the new directory we just created (note: Since we are using sudo, we will be prompted for the user password we use on the Linux machine):

$ sudo /etc/ssh/sshd_config ~/ssh_config_backup

Next, we will open the sshd_config file using vi in terminal mode. Type the following:

$ sudo vi /etc/ssh/sshd_config

 

Again, you will be promoted for your password on the Linux machine. You should see something like this after hitting the Enter key:

vi-On-File-Opened_thumb2

A few things to note:

  • I recognize it is difficult to see the dark blue text here. It will be easier to read on your actual screen
  • Notice that we are no longer in the Bash terminal per se, but instead looking at the text of the sshd_config file within the terminal.
  • At the moment, you cannot edit anything - vi is in command mode.

We will use a few (very few) basic vi commands to get this done. The Commands we need to edit this document are:

  • Use the up/down/left/right arrow keys to navigate within the document, and to position your cursor within a line of text.
  • If vi is in Command Mode, type i (lowercase i) to enter Insert mode.
  • If vi is in Insert mode, press the Esc key to return to Command Mode.
  • When you are finished editing, type :wq (colon then lowercase w the lowercase q) to save and exit the document, returning to the Bash Terminal proper.

Now, using your down arrow key, move down the document a ways until you find this line:

#PasswordAuthentication yes

 

We want to change it to:

PasswordAuthentication no

The hash symbol at the beginning of this line means that it has been “commented out” (meaning it is ignored when the OpenSSH server refers to this file during configuration). In addition, note its value is set to yes:

vi-Change-UsePasswordAuth_thumb2

First, type lowercase i to enter Insert mode, and delete the hash symbol. Then, use your right arrow key to move to the end of the line, and change the yes to no. Now press the Esc key to return to Command mode.

You would think this would be the end of it. However, at least in my current Linux Mint system, we also have to disable the Password Authentication Modules (PAM) portion of the config file (this is usually the last line in the file). To do this, use your down arrow key to navigate through the document until you find the following line:

UsePAM yes

 

We want to change it to:

UsePAM no

 

Your screen should look like this:vi-Change-UsePAM_thumb2

Use your Right-Arrow key to move to the end of that line, and type I to enter Insert Mode. Change the yes to no, then press the Esc key to return to Command Mode:

vi-Change-UsePAM-Set-To-No_thumb2

Now, once safely back in Command mode, type the following:

:wq

 

As you type, this command will appear at the bottom of the vi screen.

vi-Type-Colon-wq_thumb3

Once you hit enter, the modified file will be saved, and you will be returned to the Bash terminal:

vi-Return-To-Teminal-Window_thumb3

Restart the SSH Server

As a final step, we need to re-start the SSH Server on the Linux machine in order that the configuration changes we just made a reflected in the service. Type the following command into the terminal and hit enter:

$ sudo service ssh restart

 

Ok, now exit the current PuTTY session by closing the PuTTY window. Let’s see if we have succeeded in denying access for those seeking to login using simple password authentication. Re-open PuTTY, and load your original session configuration, which we set up without the key-based authentication using only our user name, and attempt to log in. If we have done everything correctly, you should be greeted with THIS unpleasant artifact:

Password-Login-Fails_thumb2

Hopefully, this has helped some of you get started using OpenSSH and PuTTY to connect to your Linux machine from a Windows box remotely. Of course, this is of limited usefulness when like me, your two machines are in the same room. In a future post I will discuss using SSH to connect to your Linux machine from the internet at large, and in conjunction with VNC to create a very secure Remote Desktop Connection.

Additionally, I began exploring this because my next goal is to utilize cloud services such as Amazon EC2 and Windows Azure. In that context, I want to be able to spin up a cloud-hosted Virtual Machine (VM) and perform this type of administrative stuff.

Please feel free to leave constructive feedback, and especially, to bust me where I have made a mistake or am propagating bad information. Comments are greatly appreciated!

 

Posted on November 21 2012 09:11 AM by John Atten     

Comments (0)

Find The IP Address of Your Computer on the Home or Local Area Network (LAN) -Windows 7

Posted on November 18 2012 12:48 PM by John Atten in Linux, Networking   ||   Comments (0)

This is a short post on how and where to find the IP address and static/dynamic status for your Windows 7 machine within your Local Area Network (LAN). If you are using a Linux machine, follow this link:

Find The IP Address of Your Computer on the Home or Local Area Network (LAN) -Linux (Debian-Based Systems)

Every now and then, we need to know something trivial, such as the current IP address of the machine at which we sit. Within limits, this is simple enough to do.

With a pair of useful Terminal Commands at the Windows Command Prompt, we can find the information we need:

Private (internal to our LAN) IP Address of our current computer, and our router:
> ipconfig

 

Static/Dynamic status of IP Addresses within our LAN:
> arp -a

 

Current LAN IP Address - Windows:

Open a terminal window using Start Menu (on Windows 7) by typing cmd into the “Search” field:

Open the Start Menu:

Start-Menu-Before-Search

 
Type cmd into the Search field:

Start-Menu-Found-Cmd

 
Click on the cmd Item Listed Under Programs:

Command-Prompt-On-Open

Type ipconfig into the Terminal Window:

Command-Prompt-Type-ipconfig

After you hit the enter key, the terminal window will fill with a bunch of network-related information. Depending upon your network and machine configuration, the contents of the window may well scroll down until the first few items are no longer in view (as it did here on my machine, visible below). If so, use the scrollbar to move the top of the list following the ipconfig command back into view:

Command-Prompt-After-Enter-ipconfig

In the section called “Ethernet adapter Local Area Connection”, the item named IPv4 Address represents the current IP address assigned to your machine. In my case, my current machine is assigned the IP Address 192.168.0.100.

Also in the same section, the item named “Default Gateway” represents the private, or internal IP Address of your router. In my case, my router’s internal IP Address is 192.168.0.1.

Both of these represent addresses internal to our Local Area Network, and are not exposed to the internet-at-large. Using the IP Addresses within our LAN allows us to access the other machines on our network (subject to proper permissions, of course).

For the moment, write these two addresses down. In my case:

  • The private IP Address of my computer is 192.168.0.100
  • the private IP Address of my router is 192.168.0.101

Static or Dynamic IP Assignment?

Note that these addresses are not necessarily static (or, permanent). Routers often assign IP Addresses dynamically. While a given dynamic IP address on your home network is unlikely to change very often, it CAN.  In order to check out which type of IP Address(es) we are dealing with, we can execute the arp -a command in our terminal:

Type arp -a into the terminal:

Command-Prompt-Type-apr-a

Then hit the Enter Key:

Command-Prompt-After-Enter-arp-a

Since I know that my computer’s IP Address on my network is 192.168.0.100, I can find that address in the list displayed in my terminal and see that, indeed, I am using a dynamic IP address. Further, I can see that my router, too, is using a dynamically-assigned IP Address.

On my system, these address assignments are unlikely to change. I have a small, private home network, and it is improbably that circumstances would require my router to assign them differently.

 

 

Posted on November 18 2012 12:48 PM by John Atten     

Comments (0)

Find The IP Address of Your Computer on the Home or Local Area Network (LAN) -Linux (Debian-Based Systems)

Posted on November 18 2012 12:48 PM by John Atten in Linux, Networking   ||   Comments (3)

This is a short post on how and where to find the IP address and static/dynamic status for your Linux machine within your Local Area Network (LAN). If you are using a Windows machine, follow this link:

Find The IP Address of Your Computer on the Home or Local Area Network (LAN) -Windows 7

Every now and then, we need to know something trivial, such as the current IP address of the machine at which we sit. Within limits, this is simple enough to do. NOTE: These appear to be mostly universal, but I feel compelled to mention that I am assuming a Debian-based Linux here. I am not widely versed on all the various distributions!).

With a pair of useful Commands at the Bash Terminal, we can find the information we need:

Private (internal to our LAN) IP Address of our current computer, and our router:
$ ifconfig

 

Static/Dynamic status of IP Addresses within our LAN:
$ cat / etc/network/interfaces

 

Current LAN IP Address - Linux:

Open the Bash terminal and type ifconfig

Linux-Find-IP-Address-Before-Enter

The hit the Enter Key:

Linux-Find-IP-Address-After-Enter

Note in the above that the section of the terminal output we are interested in is eth0. Within that section, we find the item inetaddr:192.168.0.113. This is the internal IP address assigned to our current machine within the Local Area Network (LAN). This address is private to our LAN, and is not exposed to the internet-at-large. Note this address, as it is how you will address this specific machine within the confines of your Local Area Network.

Static or Dynamic IP Assignment?

Note that this and other IP addresses found on our LAN are not necessarily static (or, permanent). Routers often assign IP Addresses dynamically. While a given dynamic IP address on your home network is unlikely to change very often, it CAN. In order to check out which type of IP Address we are dealing with, we can use the cat command in our Bash terminal to examine the contents of the network interfaces configuration file used by our computer. Under Most Debian-based Linux systems, we can do this as follows:

Use cat to open the network interfaces file:

Linux-Type-Network-Config-Before-Enter

The hit the Enter key:

linux-network-config-after-enter

If what you see is similar to the above, this indicates a dynamic IP assignment (the auto lo is the key phrase here. My Linux box receives a dynamic IP assignment from my home network router, as do most.

If you’re the IP assigned to your machine is static, the output to the terminal window would have looked a little like THIS (I can’t show terminal output here because I do not have a static IP assignment for my Linux Machine):

# The primary network interface
allow-hotplug eth0
iface eth0 inet static
  address 10.1.1.20
  netmask 255.255.255.0
  network 10.1.1.0
  broadcast 10.1.1.255
  gateway 10.1.1.1

 

 

 

 

Posted on November 18 2012 12:48 PM by John Atten     

Comments (3)

Excel Basics Consolidated

Posted on November 16 2012 09:11 AM by John Atten in Excel Basics, Education   ||   Comments (0)

I have done a series of posts on Microsoft Excel, targeted largely at beginners, and/or those who use Excel casually and seek to expand their skills a little. MS Excel is a powerful program, which has a host of advanced analytical and math functions, for those who spend a moment exploring them.

What is important about using excel to maximum effect is not remembering how to use each specific function of formula - it is enough to understand the syntax excel requires, and to realize that if you can dream up a use-case, there is most likely a built-in function which can do what you need.

Here, I have combined all of my Excel-related posts to date to make it easy to find what you might need. More will follow. If you have a specific request, let me know, and I will do my best to create a post addressing the topic you are interested in.

This area was created specifically for Mary (you know who you are) to begin with, a friend who has applied herself to the pursuit of learning and personal growth. Mary, you inspire! Hopefully, others will benefit as well.

Links to Excel Articles on this blog:

More links will follow, including some to useful resources outside this site. Lastly, remember, for all things related to tech, programming, and math functions, Google is your friend. Use it!

             

            Posted on November 16 2012 09:11 AM by John Atten     

            Comments (0)

            Java: Checked Exceptions, Revisited Part II

            Posted on November 5 2012 05:16 AM by John Atten in Java, CodeProject   ||   Comments (0)

            Continued From Part I - Java: Checked Exceptions, Revisited (or, a closer examination of a flawed mechanism)

            This thing is long. Here are some navigation links to topic headers:

            Use Exceptions For Exceptional Circumstances, Right?

            Most of what I know (or at least, think I know) was learned in the .net/C# environment. However, I believe it is nearly axiomatic that exceptions are to be used for exceptional circumstances, and (mostly) should not be used as a business logic construct. In the Oracle article, and in my example above, the InvalidAccountException, StopPaymentException and the InsufficientFundsException would appear to flagrantly violate this principle. Within limits, the only valid contingency exception here is AccountNotAvailableException, which is really just an obfuscation of SQLException.

            Can we re-write these classes to do away with some of the possibly extraneous exception handling?

            A Overly Simple, Hacked Example - Another Look

            First off, three of our four contingencies look a whole lot like validation problems. Depending on our project architecture, we could do away with InvalidAccountException, StopPaymentException and InsufficientFundsException and check for these contingencies from our client code before calling the getCheckingAccount method defined on the Bank class, and prior to calling the processCheck method defined on the CheckingAccount Class. First, we can add a couple of boolean methods on CheckingAccount to tell us if there is a stop payment order for a check submitted, and whether there are sufficient funds in the account to process the check:

            The re-worked CheckingAccount Class:
            public class CheckingAccount 
            
            {
            
                private String _accountID;
            
                private double _currentBalance;
            
                private ArrayList<Integer> _stoppedCheckNumbers;
            
                
            
                public String getAccountID()
            
                {
            
                    return _accountID;
            
                }
            
                
            
                
            
                public double getCurrentBalance()
            
                {
            
                    return _currentBalance;
            
                }
            
                
            
                
            
                public void setAccountID(String accountID)
            
                {
            
                    _accountID = accountID;
            
                }
            
                
            
                
            
                public void setCurrentBalance(double currentBalance)
            
                {
            
                    _currentBalance = currentBalance;
            
                }
            
                
            
                
            
                public ArrayList<Integer> getStoppedCheckNumbers()
            
                {
            
                    if(_stoppedCheckNumbers == null)
            
                    {
            
                        _stoppedCheckNumbers = new ArrayList<Integer>();
            
                    }
            
                    
            
                    return _stoppedCheckNumbers;
            
                }
            
                
            
                
            
                public boolean checkAmountApproved(double amount)
            
                {
            
                    double testBalance = _currentBalance - amount;
            
                    if(testBalance > 0)
            
                    {
            
                        return true;
            
                    }
            
                    else
            
                    {
            
                        return false;
            
                    }
            
                }
            
                
            
                
            
                public boolean checkPaymentStopped(int checkNo)
            
                {
            
                    if(_stoppedCheckNumbers.contains(checkNo))
            
                    {
            
                        return true;
            
                    }
            
                    else
            
                    {
            
                        return false;
            
                    }
            
                }
            
                
            
                
            
                public double processCheck(Check submitted) 
            
                throws DatabaseAccessException
            
                {
            
                    double newBalance = _currentBalance - submitted.getAmount();
            
                    
            
                    try
            
                    {
            
                        // <Code to Update Database to reflect current transaction>
            
                    }
            
                    catch(Exception e)
            
                    {
            
                        // <Code to log SQLException details>
            
                        
            
                        /*
            
                         * After logging and/or otherwise handling the SQL failure, throw
            
                         * an exception more appropriate to the context of the client code, 
            
                         * which does not care about the details of the data access operation, 
            
                         * only that the information could not be retreived. 
            
                         */
            
                        throw new DatabaseAccessException("Database Error");
            
                    }
            
                    
            
                    return newBalance;
            
                }
            
            }
            

            Note in the above that we have done away with all but our single contingency, DatabaseAccessException, which in reality, is our API response to a fault. Theoretically, the only exception client code is required to handle now is the DatabaseAccessException. Now, what happens to our Bank class?

            The re-Worked Bank Class:
            public class Bank 
            
            {
            
                public static CheckingAccount getCheckingAccount(String AccountID) 
            
                throws DatabaseAccessException
            
                {
            
                    CheckingAccount account = new CheckingAccount();
            
                    
            
                    try
            
                    {
            
                        /*
            
                         * <Code to retrieve Account data from data store>
            
                         */
            
                                
            
                        // Use test data to initialize an account instance:
            
                        account.setAccountID("0001 1234 5678");
            
                        account.setCurrentBalance(500.25);
            
                        account.getStoppedCheckNumbers().add(1000);
            
                    
            
                    }
            
                    catch(Exception e)
            
                    {
            
                        // <Code to log SQLException details>
            
                        
            
                        /*
            
                         * After logging and/or otherwise handling the SQL failure, throw
            
                         * an exception more appropriate to the context of the client code, 
            
                         * which does not care about the details of the data access operation, 
            
                         * only that the information could not be retrieved. 
            
                         */
            
                        throw new DatabaseAccessException("Database Error");
            
                    }
            
                    
            
                    return account;
            
                }
            
                
            
                
            
                public static boolean checkAccountExists(String AccountID)
            
                {
            
                    boolean exists = false;
            
                    try
            
                    {
            
                        /*
            
                         * <Code to check accountID exists in data store>
            
                         */     
            
                        
            
                        if(//a valid row is returned for AccountID)
            
                        {
            
                            exists = true;
            
                        }
            
                    }
            
                    catch(SQLException e)
            
                    {
            
                        // <Code to log SQLException details>
            
                        
            
                        /*
            
                         * After logging and/or otherwise handling the SQL failure, throw
            
                         * an exception more appropriate to the context of the client code, 
            
                         * which does not care about the details of the data access operation, 
            
                         * only that the information could not be retrieved. 
            
                         */
            
                        throw new DatabaseAccessException("Database Error");
            
                    }
            
                    
            
                    return exists;
            
                }
            
            }
            

            Here, we have eliminated the InvalidAccountException, and added a boolean method to check whether a valid account exists for a given account number. As before, since the original static method getCheckingAccount is still performing data access, we need to retain our DatabaseAccessException.

            Last, what does our client code look like now? We have done away with using Exceptions in a business logic context, what was the impact?

            The re-worked Mock Client Code:
            public class MockClientCode {
            
                
            
                /*
            
                 * Assume this code is supporting UI operations.
            
                 */
            
                
            
                static String SYSTEM_ERROR_MSG_UI = ""
            
                    + "The requested account is unavailable due to a system error. "
            
                    + "Please try again later.";
            
                
            
                static String INVALID_ACCOUNT_MSG_UI = ""
            
                    + "The account number provided is invalid. Please try again.";
            
                
            
                static String INSUFFICIENT_FUNDS_MSG_UI = ""
            
                    + "There are insufficient funds in the account to process this check.";
            
                
            
                static String STOP_PAYMENT_MSG_UI = ""
            
                    + "There is a stop payment order on the check submitted.
            
                    + " The transaction cannot be processed";
            
                
            
                
            
                public static void main(String args[])
            
                {
            
                    // Sample Data:
            
                    String accountID = "0001 1234 5678";
            
                    int checkNo = 1000;
            
                    double checkAmount = 100.00;
            
                    
            
                    // Use test data to initialize a test check instance:
            
                    Check customerCheck = new Check(accountID, checkNo, checkAmount);
            
                    
            
                    CheckingAccount customerAccount = null;
            
                    double newBalance;
            
                    
            
                    if(Bank.checkAccountExists(customerCheck.getAccountID()))
            
                    {
            
                        try 
            
                        {
            
                            customerAccount = Bank.getCheckingAccount(customerCheck.getAccountID());
            
                            
            
                            if(!customerAccount.checkPaymentStopped(customerCheck.getCheckNo()))
            
                            {
            
                                if(customerAccount.checkAmountApproved(customerCheck.getAmount()))
            
                                {
            
                                    newBalance = customerAccount.processCheck(customerCheck);
            
                                    
            
                                    // Output transaction result to UI:
            
                                    System.out.printf(""
            
                                    + "The transaction has been processed. New Balance is: " 
            
                                    + DecimalFormat.getCurrencyInstance().format(newBalance));
            
                                }
            
                                else // there were insufficient funds
            
                                {
            
                                    // Output the message to the user interface:
            
                                    System.out.println(INSUFFICIENT_FUNDS_MSG_UI);
            
                                }
            
                            }
            
                            else // payment was stopped on this check no.
            
                            {
            
                                // TODO Auto-generated catch block
            
                                System.out.println(STOP_PAYMENT_MSG_UI);
            
                            }
            
                        } 
            
                        catch (DatabaseAccessException e) 
            
                        {
            
                            // Output the message to the user interface:
            
                            System.out.println(SYSTEM_ERROR_MSG_UI);
            
                        }           
            
                    }
            
                    else // No valid account
            
                    {
            
                        // Output the message to the user interface:
            
                        System.out.println(INVALID_ACCOUNT_MSG_UI);
            
                    }
            
                }

            Wow. Look at that ugly nested conditional. Well, we have absolved our client code of having to handle a bunch of exceptions related to our business logic, and it is possible that improvements to the class structure, and a little refactoring could make significant improvements. In the end, though, it seems like a bit of a trade off.

            What I Meant to Like About Java Checked Exceptions

            In concept, I liked the idea that a method could declare, as part of its signature, that it throws a specific type of exception. What I do NOT like about it is that, you HAVE to. In many, cases, this mechanism can become quite annoying. A shining example, excerpted from the article link above, as follows:

            “To programmers, it seemed like most of the common methods in Java library classes declared checked exceptions for every possible failure. For example, the java.io package relies heavily on the checked exception IOException. At least 63 Java library packages issue this exception, either directly or through one of its dozens of subclasses.”

            “An I/O failure is a serious but extremely rare event. On top of that, there is usually nothing your code can do to recover from one. Java programmers found themselves forced to provide for IOException and similar unrecoverable events that could possibly occur in a simple Java library method call. Catching these exceptions added clutter to what should be simple code because there was very little that could be done in a catch block to help the situation. Not catching them was probably worse since the compiler required that you add them to the list of exceptions your method throws. This exposes implementation details that good object-oriented design would naturally want to hide.

            Obviously, it seems that it is possible to go a little too far with this scenario.

            On the other hand, wouldn’t it be a handy optional language feature to be able to add that throws clause if, in your infinite designer wisdom, it seemed the superior design choice?

            Warning: Non-Existent, Hypothetical Feature Ahead

            I grew up, so to speak, using C#, which does not have anything like the Java check-or-specify policy. It is up to the developer to properly anticipate, test for, and handle exceptions. Or to throw them programmatically, as the design may require. But in any case, client code accessing an API is blissfully unaware that a method might throw a particular type of exception until either the designed thinks of it, or it occurs in use (er, I mean, “testing”).

            I propose that a useful (but at present, non-existent) feature for an existing language would be to put that check-or-specify policy into the hands of the developer, and allow the developer to invoke it by adding the throws clause to a method signature. My hypothetical mechanism would look like this:

            • If a method defined on a class throws one or more exceptions, a compiler warning will evidence itself (at least, in the land of IDE’s such as Eclipse or Visual Studio) as opposed to the compiler error we get in Eclipse with Java. However, if there is not a throws clause included in the method signature, the warning is all you would get. You could still consume the method without handling or propagating the exception from below.
            • If the developer or designer adds a throws clause to the method signature specifying one or more particular exception types, then client code is required to recognize and address those exceptions, similar to the existing Java Check-or-Specify policy.

            The above mechanism would place the control in the hands of the designer, and allow selective enforcement of such a policy, while still providing helpful compiler warnings when, by design, such enforcement is relaxed. After all, if one team invests the time and mental energy to think trough the exception possibilities in their code, why not spare the consumer of that code the headache of doing it all over again? This would leave responsibility upon the developer of the client code to address, (or not) such exceptions as he/she sees fit.

            What Have We Learned?

            • Exceptions in Java are often misused (as they are in C# and other languages).
            • Checked Exceptions, and the Check-or-Specify Policy, can add complexity to a design, and potentially create the need for a large numbers of additional types within a project.
            • There is a solid theoretical philosophy behind Checked Exceptions in Java that has not been well-implemented in actual use. Understanding the intent behind this language/environment feature can help in design decisions.
            • Thinking of Exceptions in terms of “Faults” and “Contingencies” is a helpful way to guide Exception Handling design in accordance with the previous point.
            • Thinking of Exceptions in terms of “Faults” and “Contingencies” encourages the use of Exceptions to enforce business rules (Not sure how I feel about this).
            • As with most things in programming, effective use of Exceptions is more often than not a case of making appropriate tradeoffs and design decisions. Careful analysis of the problem domain, exception context, and attention to client code context may demand overriding theory and dogma.
            • More study and analysis is needed on my part.

             

            Half-Formed Thoughts

            In researching this post, and through the discussion on the r/java sub-reddit, I have developed the following thoughts. In examining an API or class structure, and designing an effective Exception mechanism, ask yourself:

            • Who (which component of your code) cares? Where is the specific exception most effectively handled in your design? Can you deal with the specific exception appropriately at the point it is thrown, and propagate (or otherwise notify) a different exception up the call stack which does not contain implementation details of the source class? Can you maintain encapsulation?
            • There is no law which states you can’t log the initial exception, and then throw another which is more appropriate to the context of the calling code.
            • Is the exception a result of user input? If so, this may be a sign that it is more properly handled with a validation mechanism and/or improved implementation of business rules.
            • The previous point is not always true. It could be a tradeoff, where the exception results from user input, but deep within a series of calls. In this case it might be more practically handled with an exception, even though this breaks form, and uses an exception in place of business rules and validation.
            • The Java Checked Exception and Check or Specify policy is a real mixed bag. Philosophically, I like it. However, employing it effectively, and the way I was intended, requires careful design - more, I think, than many put into exception handling.
            • It is easy to use an exception when you don’t know what else to do (meaning, you need to re-examine your design, or go back to the documentation. I am 100% guilty of this at times).
            • Don’t use exceptions to “Pass the buck"” and make what should have been YOUR problem into the nest guy’s problem. As a consumer of your API, he will have even less context do deal with YOUR exception than you do.

            This has been a difficult post to write. We tend to think of exceptions and exception handling as something we understand well. I for one tend to think about it when I need it, and not much else. Further, I am learning much of this as I go, meaning I am self-taught, and there is always the danger that I will think I have something figured out, only to learn (often in quite humbling, “what the hell am I doing writing about this” kinds of ways) I had it all wrong, or missed something which should have been obvious.

            In trying to formulate a coherent representation of my understanding here, I have developed a greater appreciation for the difficult design choices we are faced with. Trying to wrap my head around the deeper intentions of the Java Exception mechanism, and the semi-polarized disagreement about its usefulness among Java devs, has been an eye-opening experience. The education win for me extends beyond Java, and into my toolbox.

             

            Posted on November 5 2012 05:16 AM by John Atten     

            Comments (0)

            About the author

            My name is John Atten, and my username on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, Java, SQL Server 2012, learning ASP.NET MVC, html 5/CSS/Javascript. I am always looking for new information, and value your feedback (especially where I got something wrong!).

            Web Hosting by