Tuesday, September 18, 2007

Automatic Directory Creation via Spring Application Context File

Spring lets you define property editors that convert from a text representation to an object representation. We can hook into this feature to automate directory creation like this:
public class DirectoryEditor extends PropertyEditorSupport {

 public void setAsText(String textValue) {
  File f = new File(textValue);
  if (!f.exists()) {
   if (!f.mkdirs()) {
    throw new ConfigurationException("Unable To Create Directory: [" + textValue + "]");
   }
  } else if (!f.isDirectory()) {
   throw new ConfigurationException("File Is Not a Directory: [" + textValue + "]");
  }
  setValue(new Directory(textValue));
 }
 
}
Then create a bean definition so that Spring knows about it:
  <bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
      <map>
        <entry key="com.twintechs.spring.Directory">
          <bean id="directoryEditor" class="com.twintechs.spring.DirectoryEditor" />
        </entry>
      </map>
    </property>
</bean>
Then create a Directory class:
public class Directory {

 private String name;

 public Directory(String _name) {
  super();
  this.name = _name;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
 
}
The last step is to use the Directory class in place of the String class in any Java objects.

Saturday, September 08, 2007

Extracting Dependancy Lists from Maven POM Files

Recently a client of mine wanted to know if they were including the same dependency versions in their subprojects. They had 12 modules feeding into one maven-based build so I was inefficient to check the dependencies by hand. After running this program, copy the output into a program to sort it.

package com.affy.pom;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class PomAnalysisDriver {

 private static XPathFactory xpathFactory = XPathFactory.newInstance();

 XPathExpression artifactIdExpr = null;

 XPathExpression dependencyExpr = null;

 XPathExpression depGroupIdExpr = null;

 XPathExpression depArtifactIdExpr = null;

 XPathExpression depVersionExpr = null;

 public static void main(String[] args) throws Exception {
  try {
   List pomFiles = new ArrayList();

   PomAnalysisDriver pad = new PomAnalysisDriver();
   pad.init();
   pad.searchForPomFiles("[ROOT_DIR]", pomFiles);
   pad.execute(pomFiles);
  } finally {
   System.out.println("Done.");
  }
 }

 public void init() throws XPathExpressionException {
  XPath xpath = xpathFactory.newXPath();
  artifactIdExpr = xpath.compile("/project/artifactId/text()");
  dependencyExpr = xpath.compile("/project/dependencies/dependency");
  depGroupIdExpr = xpath.compile("./groupId/text()");
  depArtifactIdExpr = xpath.compile("./artifactId/text()");
  depVersionExpr = xpath.compile("./version/text()");
 }

 public void searchForPomFiles(final String dir, final List pomFiles) {
  File initialDir = new File(dir);
  if (initialDir.isDirectory() == false) {
   return;
  } else {
   String[] files = initialDir.list();
   for (String filename : files) {
    String suffix = "";
    if (dir.endsWith("\\") == false && dir.endsWith("/") == false) {
     suffix = "\\";
    }
    File childDir = new File(dir + suffix + filename);
    if (childDir.isDirectory() == true) {
     searchForPomFiles(dir + suffix + filename, pomFiles);
    } else {
     if (filename.equals("pom.xml")) {
      pomFiles.add(childDir.getAbsolutePath());
     }
    }
   }
  }
 }

 public void execute(final List pomFiles) throws SAXException, IOException,
 ParserConfigurationException, XPathExpressionException {
  for (String pomFilename : pomFiles) {
   File f = new File(pomFilename);
   Document document;

   DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
   DocumentBuilder builder = factory.newDocumentBuilder();
   document = builder.parse(f);

   // Node projectNode = document.getDocumentElement();
   // NodeList children = projectNode.getChildNodes();

   String projectName = (String)artifactIdExpr.evaluate(document, XPathConstants.STRING);
   
   List dependencies = new ArrayList();

   NodeList nodes = (NodeList)dependencyExpr.evaluate(document, XPathConstants.NODESET);
   for (int i = 0; i < nodes.getLength(); i++) {
    Node dependency = nodes.item(i);
    String depGroupId = (String) depGroupIdExpr.evaluate(dependency,
                                          XPathConstants.STRING);
    String depArtifactId = (String) depArtifactIdExpr.evaluate(dependency,
                                          XPathConstants.STRING);
    String depVersion = (String) depVersionExpr.evaluate(dependency,
                                          XPathConstants.STRING);
    String depId = "/" + depGroupId + "/" + depArtifactId + "/" + 
                                          depVersion + "/" + projectName;
    dependencies.add(depId);
   }

   for (String s : dependencies) {
    System.out.println(s);
   }
  }
 }

}

Setting Up a Ubuntu Perforce Server

Setting Up a Ubuntu Perforce Server

This is a step-by-step guide to installing and configuring Perforce on a Ubuntu server. It assumes that you have already created a Basic Ubuntu Server. There are several sections below:

  • Download Perforce
  • Configure Perforce
  • Run Perforce Server For the First Time
  • Setup Perforce As Bootup Service
  • ToDo
  • Resources

Download Perforce

  1. Use Lynx to download the P4D software.
      lynx http://www.perforce.com/perforce/downloads/linux26x86.html
    
  2. Enter Y to allow the cookie from Perforce.com.
  3. Press the Down Arrow until the Downloads link is selected. Then press Enter.
  4. Press the Down Arrow until the Linux Kernel 2.6 for x86 link is selected. Then press Enter.
  5. Press the Down Arrow until the Download link is selected. Then press Enter.
  6. Press D to start the download.
  7. Press the Down Arrow to highlight the ''Save to disk'' link. Then press Enter.
  8. Use the default filename of `p4d`.
  9. Press q, then press Enter to exit Lynx.
  10. Use Lynx to download the P4 software by retrace the steps above expect selecting to download `P4` instead of `P4D`.

Configure Perforce

This information is a reprise of the [http://www.perforce.com/perforce/doc.072/manuals/p4sag/index.html Perforce Administration Guide] along with some additional information.
  1. Download the `daemon` utility. This utility allows `p4d` to be run by the `perforce` user instead of `root`.
      sudo apt-get daemon
    
  2. Connect to the directory where the `p4` and `p4d` files where downloaded (probably the home directory).
      cd ~
    
  3. Make the `p4` and `p4d` files executable.
      chmod +x p4 p4d
    
  4. Copy the Perforce executables to `/usr/local/bin` which is already in the system PATH environment variable.
      sudo mv p4 /usr/local/bin
      sudo mv p4d /usr/local/bin
    
  5. Create a group for Perforce files.
      sudo addgroup p4admin
    
  6. Create a user for Perforce administrative work.
      sudo adduser perforce
    
  7. Use `visudo` to give the perforce user account the ability to use `sudo`. Add the following line at the end o f the file.
      perforce ALL = ALL
    
  8. Log off your default user account.
  9. Log in using the `perforce` account.
  10. Create a directory to hold the repository.
      sudo mkdir /perforce_depot
      sudo chown perforce:p4admin /perforce_depot  
    
  11. Create a directory to hold Perforce log files.
      sudo mkdir /var/log/perforce
      sudo chown perforce:p4admin /var/log/perforce
    
  12. Add the following lines to the end of /etc/profile. These settings will be used by client programs run on the Linux server - not by the Perforce server.
      # Perforce Settings
      export P4JOURNAL=/var/log/perforce/journal
      export P4LOG=/var/log/perforce/p4err
      export P4PORT=localhost:1666
      export P4ROOT=/perforce_depot
      export P4USER=perforce
    
  13. Load the Perforce settings.
      source /etc/profile
    
  14. Create a Perforce group to limit resource usage using `sudo p4 group developers` to set the following values. See http://kb.perforce.com/P4dServerReference/Performance/ViewsProtect..Maxlocktime for more information.
      Group: developers
      MaxResults:  50000
      MaxScanRows: 250000
      MaxLocktime: 30000
      Timeout:     4320
      Subgroups:
      Users:
        developer
    

Setup Perforce As Bootup Service

  1. Connect to the initialization control directory.
      cd /etc/init.d
    
  2. Create the Perforce control script using `sudo vi perforce`.
      #!/bin/sh -e
    
      export P4JOURNAL=/var/log/perforce/journal
      export P4LOG=/var/log/perforce/p4err
      export P4ROOT=/perforce_depot
      export P4PORT=1666
    
      PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
    
      . /lib/lsb/init-functions
    
      p4start="p4d -d"
      p4stop="p4 admin stop"
      p4user=perforce
    
      case "$1" in
      start)
        log_action_begin_msg "Starting Perforce Server"
        daemon -u $p4user $p4start;
        ;;
    
      stop)
        log_action_begin_msg "Stopping Perforce Server"
        daemon -u $p4user $p4stop;
        ;;
    
      restart)
        stop
        start
        ;;
    
      *)
        echo "Usage: /etc/init.d/perforce (start|stop|restart)
        exit 1
        ;;
    
    esac
    
    exit 0
    
  3. Integrate the Perforce control script into the boot process.
      sudo update-rc.d perforce defaults
    
  4. Goto your home directory then use the control script to start the Perforce server.
      cd ~
      sudo /etc/init.d/perforce start
    
  5. Use the control script to restart the Perforce server.
      sudo /etc/init.d/perforce restart
    

Run Perforce Server For the First Time

Before Perforce can be used by a team there are two housekeeping task that need to be done - creating the journal and closing a security hole.
  1. Set the Perforce environment variables.
      source /etc/profile
    
  2. Start the perforce server. The command line is short because all of the settings are provided by the variables exported by /etc/profile.
      sudo p4d –d
    
  3. Create the journal.
      sudo p4d –jc
    
  4. By default, all users connected to Perforce are administrators. However, that level of security is not acceptable for a normal development environment. Run the `protect` option to switch out of the default security mode. For now, accept the default protections by typing `:wq` and pressing Enter.
      sudo p4 protect
    
  5. Stop the Perforce server.
      sudo p4 admin stop
    

TODO

* Journalling
* checkpointing
* backups

Resources

* Perforce's public depot
* http://kb.perforce.com/AdminTasks/InstallAndUpgrade/RunningAPerf..InetdOnUnix How to start `p4d` via `inetd` instead of a startup script.