Sunday, August 1, 2010

Conky

Introducing Conky

conky desktop image

Conky is a free tool for embedding information into your X desktop. I use it during the work day to keep an eye on my computer's resources and to help remind me how I should be utilizing my own resources.

This tutorial isn't designed to teach you how to use Conky. For that, you'll have to check out the Conky documentation. However, this tutorial will show you how to customize Conky to embed information in your Ubuntu desktop, just like the image to the right.

Configuring Conky

Conky is configurable through the .conkyrc file in your home directory. In order to get Conky to produce the output shown above, I made a few changes to the standard sample .conkyrc file, which you can find at /usr/share/doc/conky/examples/conkyrc.sample.gz.

Conky's colors are highly configurable. I've selected a nice earthy palette of yellow, orange and gray.

# Colors
default_color BDB76B # Values
color0 FF8C00 # Headers
color1 lightgrey # Line Items
color2 BDB76B # Highlighted Values

Next I use my newly defined color palette to draw attention to the processes that are currently consuming the most cpu and memory resources on my computer.

${color1} ${top name 1} ${top pid 1} ${color2}${top cpu 1}${color1} ${top mem 1}
...

${color1} ${top_mem name 1} ${top_mem pid 1} ${top_mem cpu 1} ${color2}${top_mem mem 1}

I've also asked Conky to embed the contents of a text file into my desktop. Specifically, I like to always see my TODO list.

${color0}TODO:${color1}
${execi 30 cat /home/username/Desktop/TODO.txt | fold -w40 }

Integrating all of the tweaks described above, I arrived at the .conkyrc configuration file cataloged below.

################################ Conky Settings ################################

# set to yes if you want Conky to be forked in the background
background no

# X font when Xft is disabled, you can pick one with program xfontsel
font arial

# Use Xft?
use_xft yes

# Xft font when Xft is enabled
xftfont Bitstream Vera Sans Mono:size=11

# Text alpha when using Xft
xftalpha 0.8

# mail spool
mail_spool $MAIL

# Update interval in seconds
update_interval 5.0

# This is the number of times Conky will update before quitting.
# Set to zero to run forever.
total_run_times 0

# Create own window instead of using desktop (required in nautilus)
own_window yes

# If own_window is yes, you may use type normal, desktop or override
own_window_type override

# Use pseudo transparency with own_window?
own_window_transparent yes

# If own_window_transparent is set to no, you can set the background colour here
own_window_colour hotpink

# If own_window is yes, these window manager hints may be used
own_window_hints undecorated,below,sticky

# Use double buffering (reduces flicker, may not work for everyone)
double_buffer yes

# Minimum size of text area
minimum_size 280 5

# Draw shades?
draw_shades no

# Draw outlines?
draw_outline no

# Draw borders around text
draw_borders no

# Draw borders around graphs
draw_graph_borders yes

# Stippled borders?
stippled_borders 8

# border margins
border_margin 4

# border width
border_width 1

# Default border colors
default_shade_color black
default_outline_color black

# Text alignment, other possible values are commented
alignment top_right

# Gap between borders of screen and text
# same thing as passing -x at command line
gap_x 20
gap_y 50

# Subtract file system buffers from used memory?
no_buffers yes

# set to yes if you want all text to be in uppercase
uppercase no

# number of cpu samples to average
# set to 1 to disable averaging
cpu_avg_samples 2

# number of net samples to average
# set to 1 to disable averaging
net_avg_samples 2

# Force UTF8? note that UTF8 support required XFT
override_utf8_locale no

# Add spaces to keep things from moving about? This only affects certain objects.
use_spacer no

# Colors
default_color BDB76B # Values
color0 FF8C00 # Headers
color1 lightgrey # Line Items
color2 BDB76B # Highlighted Values

################################ Screen Text ################################

TEXT
$nodename - $sysname $kernel on $machine
$hr
${color0}Summary:$color
${color1}IP:$color ${addr eth0} ${alignr}${color1}Uptime:$color $uptime_short

${color1}CPU Usage:$color $cpu% ${cpubar}
${color1}RAM Usage:$color $mem/$memmax - $memperc% ${membar}
${color1}Swap Usage:$color $swap/$swapmax - $swapperc% ${swapbar}
$color$hr

${color0}File Systems:$color
/ $color${fs_used /}/${fs_size /} ${fs_bar /}
$color$hr
${color} Name             PID     CPU%   MEM%
${color0}Top CPU:$color
${color1} ${top name 1} ${top pid 1} ${color2}${top cpu 1}${color1} ${top mem 1}

${color1} ${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}
${color1} ${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}
${color1} ${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}
${color0}Top Mem:$color

${color1} ${top_mem name 1} ${top_mem pid 1} ${top_mem cpu 1} ${color2}${top_mem mem 1}
${color1} ${top_mem name 2} ${top_mem pid 2} ${top_mem cpu 2} ${top_mem mem 2}
${color1} ${top_mem name 3} ${top_mem pid 3} ${top_mem cpu 3} ${top_mem mem 3}
${color1} ${top_mem name 4} ${top_mem pid 4} ${top_mem cpu 4} ${top_mem mem 4}

$hr
${color0}Network Connections:$color ${tcp_portmon 1 65535 count}
$color Inbound Ports: ${tcp_portmon 1 32767 count}  Outbound Ports: ${tcp_portmon 32768 61000 count}
${color0}Outbound Connection ${alignr} Remote Service/Port${color1}
${tcp_portmon 32768 61000 rhost 0} ${alignr} ${tcp_portmon 32768 61000 rservice 0}
${tcp_portmon 32768 61000 rhost 1} ${alignr} ${tcp_portmon 32768 61000 rservice 1}
${tcp_portmon 32768 61000 rhost 2} ${alignr} ${tcp_portmon 32768 61000 rservice 2}
${tcp_portmon 32768 61000 rhost 3} ${alignr} ${tcp_portmon 32768 61000 rservice 3}
${tcp_portmon 32768 61000 rhost 4} ${alignr} ${tcp_portmon 32768 61000 rservice 4}

${color0}Inbound Connection ${alignr} Local Service/Port${color1}
${tcp_portmon 1 32767 rhost 0} ${alignr} ${tcp_portmon 1 32767 lservice 0}
${tcp_portmon 1 32767 rhost 1} ${alignr} ${tcp_portmon 1 32767 lservice 1}
${tcp_portmon 1 32767 rhost 2} ${alignr} ${tcp_portmon 1 32767 lservice 2}
${tcp_portmon 1 32767 rhost 3} ${alignr} ${tcp_portmon 1 32767 lservice 3}
${tcp_portmon 1 32767 rhost 4} ${alignr} ${tcp_portmon 1 32767 lservice 4}

$hr
${color0}TODO:${color1}
${execi 30 cat /home/username/Desktop/TODO.txt | fold -w40 }

Running Conky at Login

In order to enjoy the Conky configuration features described in the section above, you may also want to know how to run Conky automatically when you log in. To do so, you need to configure a new startup program for your Ubuntu session, which you can do by clicking System -> Preferences -> Sessions.

Sometimes Conky acts finicky if you start it up before your X session has had time to stabilized, so I have my Conky startup program call this shell script.

#!/bin/sh
sleep 30 && conky

Declarative Container Managed Authentication

Motivation

For web applications that provide dynamic content tailored to individual users, authentication is an important concern. Authentication confirms that a user is, indeed, who they claim to be. As a result, (web) applications employ authentication to confirm the identities of users and protect privileged resources.

The Java Servlet specification supports declarative authentication, which eases the implementation of a web application's authentication strategy. By using declarative authentication, web developers simply declare that some or all of the application's resources are protected. Web developers are liberated from the burden of writing code that describes how those security constraints are enforced.

This tutorial provides a quick overview for web developers who want to take advantage of the Servlet specification's support of declarative authentication. The tutorial covers both container-agnostic elements of the web.xml file that support declarative authentication, as well as some authentication-related features of the Tomcat servlet container.

Securing Web Resources Declaratively

The web.xml file is the first stop on the way to adding declarative authentication to a web application. In that file, we'll want to declare what type of authentication should be used. By appropriately configuring a <login-config> element, we can tell the servlet container what query method should be used when authenticating users. Several options are available, though form-based authentication is the most common.

<login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>
      /WEB-INF/pages/login.jsp?info=login
    </form-login-page>
    <form-error-page>
      /WEB-INF/pages/login.jsp?info=login_failed
    </form-error-page>
  </form-login-config>
</login-config>

The <login-config> element above tells the servlet container how it should authenticate users for the related web application. Next we need to tell the servlet container which resources in the web application require that the user be authenticated.

<security-constraint>
  <web-resource-collection>
    <web-resource-name>Secured Resources</web-resource-name>
    <description>
      Only logged in users should be able to visit the 
      resources in this collection.
    </description>
    <url-pattern>/command/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>user</role-name>
  </auth-constraint>
  <user-data-constraint>
    <transport-guarantee>NONE</transport-guarantee>
  </user-data-constraint>
</security-constraint>

<security-role>
    <role-name>user</role-name>
</security-role>

The <security-constraint> element above provides a trivial example in which all URLs that map to the pattern "/command/*" are secured so that they may only be accessed by authenticated users in the "user" role.

Defining a JDBCRealm

All of the authentication-related configuration that we did above in the web.xml file is highly portable since that file will be packaged and deployed with our web application. Notice, however, that the configuration work we did in web.xml isn't sufficient by itself to support declarative authentication. Specifically, we've not yet told the Servlet container where and how to access the web application's list of valid users with related passwords.

This is where we have to delve into some container-specific details. By default, the Tomcat servlet container will attempt to authenticate by reading the user names and passwords defined in its $CATALINA_HOME/conf/tomcat-users.xml file. Obviously, this is not very convenient if we intend to use a relational database to store information about our web application's users.

Thankfully, it's easy to configure Tomcat to read from a relational database instead of the tomcat-users.xml file when authenticating users. To do so, we'll need to configure a new Realm, which is Tomcat's generic term for a collection of information about users.

<Realm 
    className="org.apache.catalina.realm.JDBCRealm"
    driverName="org.postgresql.Driver"
    connectionURL="jdbc:postgresql://localhost:5432/YourDatabaseName"
    connectionName="your_db_username" 
    connectionPassword="your_db_password"
    userTable="user_table_name" 
    userNameCol="user_name_column_name" 
    userCredCol="user_password_column_name"
    userRoleTable="uesr_role_table_name" 
    roleNameCol="role_name_column_name" />

The <Realm> element defined above tells Tomcat how it can use a relational database to authenticate users.

Configuring Web Application Context

In the section above we saw how to define a <Realm> element that instructs Tomcat to employ our relational database when authenticating users. One final consideration remains: where should we place that <Realm> definition? A single Tomcat server can host multiple web applications. Presumably each of those web applications may want to use a different database to authenticate its users. As a result, we should locate our <Realm> element within the <Context> element for our web application so that the Realm applies only to our web application.

A good way to include a <Realm> element within the web application's <Context> is to include a META-INF/context.xml file in our web application. By defining a <Realm> element within such a file, we can ensure that the application will use the appropriate relational database for authentication, regardless of which Tomcat server it is deployed to.

Granting Security Privileges

One final, useful note is that we may need to augment the security constraints for our application server if we want it to use an external relational database for user authentication. For the Tomcat Servlet container, we can add an entry to the $CATALINA_HOME/config/catalina.policy file.

grant {
    permission java.net.SocketPermission "localhost:5432", 
    "connect,resolve";
};    

The above statement grants permission for any class in the servlet container to lookup and connect to our database, which runs on the localhost at port 5432.

Beware a slow death by regular expressions!

Overview & Motivation

The Java programming language provides great support for regular expressions. In general, the performance of regular expressions in Java is very good. The trade-off for that excellent performance is a (somewhat) protracted API. To see what I mean, have a a look at a canonical regular expression example from Sun's javadoc:

Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();

Kind of verbose, considering all we've accomplished is checking a single CharacterSequence for a match against one regular expression! Such complaints about decreased simplicity are likely what motivated Sun to provide some regular expression "convenience" methods inside the API for the String class. Specifically, as of version 6, the String API provides five such convenience methods:

  • public boolean matches(String regex);
  • public String replaceFirst(String regex, String replacement);
  • public String replaceAll(String regex, String replacement);
  • public String[] split(String regex);
  • public String[] split(String regex, int limit);

Each of the above convenience methods constructs and delegates the method call to Pattern and Matcher objects. What's not immediately obvious from the documentation for those convenience methods is that they become extremely inefficient as you execute them many thousands of times.

Reg. Exp. API Timing Data

Consider the following bit of code that illuminates the inefficiency of the String.matches(regex) method:

public class RegExpExerciser
{
 public static void main(String[] args)
 {

  int numTimes = 10000000;

  String regexp = "a*b";
  String toMatch = "aaaaab";

  long begin1 = System.currentTimeMillis();
  for (int i = 0; i < numTimes; i++)
  {
   toMatch.matches(regexp);
  }
  System.out.println(
   "Milliseconds using convenience method from String: " 
   + (System.currentTimeMillis() - begin1)
  );

  long begin2 = System.currentTimeMillis();
  Pattern p = Pattern.compile(regexp);
  for (int i = 0; i < numTimes; i++)
  {
   p.matcher(toMatch).matches();
  }
  System.out.println(
   "Milliseconds when re-using compiled reg. exp. pattern: " 
   + (System.currentTimeMillis() - begin2)
  );

 }
}

Executing the above code, my box reveals these times:

Milliseconds using convenience method from String: 5074
Milliseconds when re-using compiled reg. exp. pattern: 1720

RegExpHelper.java to the Rescue!

The code sample above clearly demonstrates that the time to compile a regular expression Pattern dominates the time required to actually check whether a given string matches the pattern. Utilizing that key insight, I offer a helper class in the Big-Oh Software Common library that provides simple APIs for working with regular expressions without sacrificing performance: RegExpHelper.java (javadoc). The key feature to note about that class is that it caches compiled regular expressions, making its methods both simple and efficient.