<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://www.softwarebyjeff.com/skins/common/feed.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Software By Jeff - New pages [en]</title>
		<link>http://www.softwarebyjeff.com/index.php/Special:Newpages</link>
		<description>From Software By Jeff</description>
		<language>en</language>
		<generator>MediaWiki 1.4.7</generator>
		<lastBuildDate>Sun, 20 May 2012 20:01:29 GMT</lastBuildDate>
		<item>
			<title>Mimicking External Actions with EasyMock</title>
			<link>http://www.softwarebyjeff.com/index.php/Mimicking_External_Actions_with_EasyMock</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It will happen sometimes that a unit test will need to do some work that an external source might normally do. One easy-to-see example of this is an ID that gets generated by a database when an entity is persisted. It may be the case that the software will assume success, but it could (nay, should) also be the case that some validation or use of the identifier is done after the persistence, and this can be tricky when mocking these interactions.&lt;br /&gt;
&lt;br /&gt;
Let's take a really simple and dumb annotated Entity object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@Entity&lt;br /&gt;
@Table(name=&amp;quot;pojo&amp;quot;)&lt;br /&gt;
public class POJO {&lt;br /&gt;
&lt;br /&gt;
    @Id&lt;br /&gt;
    @GeneratedValue(strategy=GenerationType.IDENTITY)&lt;br /&gt;
    public Long id;&lt;br /&gt;
&lt;br /&gt;
    @Column&lt;br /&gt;
    public String something;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When used, we would expect the database to populate our id column when we saved the object for the first time. Simple, easy.&lt;br /&gt;
&lt;br /&gt;
Let's make a simple DAO definition to save such an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public interface POJODAO {&lt;br /&gt;
    public POJO saveOrUpdate(final POJO pojo);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We'll leave the implementation of this up to the specific application, but now we know we have a way to save (or update) our POJO. Let's make a trivial class that will do exactly this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public class POJOWork {&lt;br /&gt;
&lt;br /&gt;
    public POJODAO pojoDao = null;&lt;br /&gt;
&lt;br /&gt;
    public void makeAndSavePOJO() {&lt;br /&gt;
        final POJO pojo = new POJO();&lt;br /&gt;
        pojo.something = Long.toHexString(System.currentTimeMillis());&lt;br /&gt;
        pojoDao.saveOrUpdate(pojo);&lt;br /&gt;
        if (pojo.id == null) {&lt;br /&gt;
            throw new RuntimeException(&amp;quot;POJO Save Failed!&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we've got a pretty useless method that creates and saves one of our objects, assigning some goofy data to its member. Of course, in your code, this would be more useful. After the save is complete, the ID field is used. Here, for a trivial and probably very undesireable validation, the ID field is checked for null, which it shouldn't be after a successful save, and will throw an Exception if the field is null. Again, of course, in your code something more useful would be done.&lt;br /&gt;
&lt;br /&gt;
In good test-driven (or test-defended) development, we want to have a test that verifies our method does what we expect it to. There are a few obstacles in our method that we run into when we're writing our test, the least of which is that the object we need to mock is local to the member.&lt;br /&gt;
&lt;br /&gt;
Here's a quick method that works around this, ensures that the DB interaction is mocked, and passes our simple method without hitting that Exception.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;import static org.easymock.EasyMock.*;&lt;br /&gt;
import static org.junit.Assert.assertNotNull;&lt;br /&gt;
&lt;br /&gt;
import org.easymock.IAnswer;&lt;br /&gt;
import org.junit.Test;&lt;br /&gt;
&lt;br /&gt;
public class POJOWorkTest {&lt;br /&gt;
    @Test&lt;br /&gt;
    public void makeAndSavePOJOSuccess(){&lt;br /&gt;
        final POJOWork pojoWork = new POJOWork();&lt;br /&gt;
        pojoWork.pojoDao = createMock(POJODAO.class);&lt;br /&gt;
		&lt;br /&gt;
        expect(pojoWork.pojoDao.saveOrUpdate(isA(POJO.class))).andAnswer(new IAnswer&amp;lt;POJO&amp;gt;() {&lt;br /&gt;
            @Override&lt;br /&gt;
            public POJO answer() throws Throwable {&lt;br /&gt;
                final POJO pojo = (POJO)getCurrentArguments()[0];&lt;br /&gt;
                assertNotNull(pojo.something);&lt;br /&gt;
                pojo.id = 0l;&lt;br /&gt;
                return pojo;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
		&lt;br /&gt;
        replay(pojoWork.pojoDao);&lt;br /&gt;
        pojoWork.makeAndSavePOJO();&lt;br /&gt;
        verify(pojoWork.pojoDao);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Digesting the test bit by bit, we start out making our object. We then assign its DAO to a mock implementation.&lt;br /&gt;
&lt;br /&gt;
We know that we're going to call the method, so we set up an expectation. Since we don't have either an equals() method or other means by which to compare the object passed to our expectation, we simply accept one of its kind with the isA() EasyMock matcher. If EasyMock accepts our match (which it will), it then invokes our IAnswer, calling the answer() method.&lt;br /&gt;
&lt;br /&gt;
In our answer() method, we grab the parameter (which we know from the matcher will be the right type and exist). We can do some validation here that helps us overcome other lack of access to the object; in our case, we just confirm that our POJO.somethign has a value.&lt;br /&gt;
&lt;br /&gt;
Happy that our object is prepared for the DB, we play its part and assign the id field a value. Since our test simply makes sure it's not null, we just give it a number. Should we have needed to, we could have put more thought into this, of course. Then, because our interface says we return our POJO, we do so from our answer() method.&lt;br /&gt;
&lt;br /&gt;
With our peparation complete, the test tells EasyMock to start waiting for calls, we call the method, it runs, calling our prepared EasyMock method and running as if it were in a real execution flow, and finally we validate with EasyMock that our expectations were met. Running this test gives us total green-bar happiness.&lt;br /&gt;
&lt;br /&gt;
Sure, that's the easy one; a method with a return value. What happens if we don't return anything, or what is returned has nothing to do with our object? Let's change our interface a little to just save and not return our object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public interface POJODAO {&lt;br /&gt;
    public void saveOrUpdate(final POJO pojo);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With that little change, our POJOWork code is still valid, as it was not making use of the returned value, but we can no longer use the easy expect().andAnswer() or expect().andReturn() EasyMock methods.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;import static org.easymock.EasyMock.*;&lt;br /&gt;
import static org.junit.Assert.assertNotNull;&lt;br /&gt;
&lt;br /&gt;
import org.easymock.IArgumentMatcher;&lt;br /&gt;
import org.junit.Test;&lt;br /&gt;
&lt;br /&gt;
class POJOMatcher implements IArgumentMatcher {&lt;br /&gt;
    @Override&lt;br /&gt;
    public void appendTo(StringBuffer arg0) {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public boolean matches(Object arg0) {&lt;br /&gt;
        final POJO pojo = (POJO) arg0;&lt;br /&gt;
        assertNotNull(pojo.something);&lt;br /&gt;
        pojo.id = 0l;&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public class POJOWorkTest {&lt;br /&gt;
    private POJO isPOJO() {&lt;br /&gt;
        reportMatcher(new POJOMatcher());&lt;br /&gt;
        return new POJO();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Test&lt;br /&gt;
    public void makeAndSavePOJOSuccess() {&lt;br /&gt;
        final POJOWork pojoWork = new POJOWork();&lt;br /&gt;
        pojoWork.pojoDao = createMock(POJODAO.class);&lt;br /&gt;
&lt;br /&gt;
        pojoWork.pojoDao.saveOrUpdate(isPOJO());&lt;br /&gt;
        expectLastCall();&lt;br /&gt;
&lt;br /&gt;
        replay(pojoWork.pojoDao);&lt;br /&gt;
        pojoWork.makeAndSavePOJO();&lt;br /&gt;
        verify(pojoWork.pojoDao);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
That's a bit more work, and it's not nearly as elegant or flexible. Let's go over this bit-by-bit, too.&lt;br /&gt;
&lt;br /&gt;
The new class, POJOMatcher, makes use of the EasyMock.IArgumentMatcher interface to allow EasyMock to send an object for comparison. The matches() method will be used to determine if that's the right deal after all. Since we've really got nothing to compare to, we do our validation in the matches() method just like in the answer() method before, and when it passes we set the id. The argument passed to the matcher is the one created in our test method, so it will have an id when the method continues from the mocked save attempt.&lt;br /&gt;
&lt;br /&gt;
Our test class has a new isPOJO() method, which kind of binds the IArgumentMatcher to our POJO type. While we do have to return an instance of our class, unless we want to use it to validate the information in the POJOMatcher.matches() method, it can be empty (as it is). had we wanted to, we could  have made the isPOJO() more intelligent, perhaps taking a POJO as a parameter for comparison, and returning that after setting up the ReportMatcher.&lt;br /&gt;
&lt;br /&gt;
In our test method, we changed the preparation a little bit. Since the method doesn't return a value any more, we can't pass it to EasyMock.expect(), and then can't use the expect().andAnswer() or expect().andReturn() methods, which is how we got wrapped up in ReportMatcher and IArgumentMatcher anyway. &amp;quot;Calling&amp;quot; the method and then telling EasyMock we expect the last call using the expectLastCall() will satisfy the replay and verify and let the mock do its job.&lt;br /&gt;
&lt;br /&gt;
When the call is run in the middle, our matcher will be called, comparing, ignoring our isPOJO()-provided POJO, and verifying the POJO actually passed in our implementation in the POJOMatcher.matches() method, and successfully setting the id.&lt;br /&gt;
&lt;br /&gt;
Now, in both cases, we can validate the information contained in a local object when passing through a mocked method, and can also meet our initial goal of affecting the passed object as the real object is expected to have affected it.&lt;/div&gt;</description>
			<pubDate>Wed, 21 Jul 2010 20:03:53 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Mimicking_External_Actions_with_EasyMock</comments>		</item>
		<item>
			<title>How Do Annotations Work?</title>
			<link>http://www.softwarebyjeff.com/index.php/How_Do_Annotations_Work%3F</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Recently, I was giving an overview of Java to a bunch of C/C++ developers to help them bridge the gap. Mostly I ended up assuring them that they knew what they were doing and filled in very few gaps. I did enlighten them on some of the fun things like Collections and some confusing things like Date. We covered lots of ground in those few hours of presentation and banter, but one question I couldn't answer was about annotations.&lt;br /&gt;
&lt;br /&gt;
I had put a few code samples in my presentation that I'd pulled from a recent project. Stuff I knew worked, so I wouldn't be surprised with any hastily thrown together code with style, format, or syntax errors. One of the classes was an annotated Spring class, with some code not too much unlike this snippet:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@Autowire private SomeBean someBean;&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
We went down a path of queries related to how to create new ones, which is a simple @interface declaration like the following.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public @interface Foo{ }&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
They queried how to put them in the code, and I pointed back to the example. I also showed them some other examples related to using them on class definitions, methods, and on parameter values, as defined by the @Target annotation and ElementType enum. We looked at examples of commonly encountered annotations such as @Deprecated and @SuppressWarning among others. We even looked at the different uses of @Retention and discussed the RetentionPolicy enum; how SOURCE is used to provide hints for the compilers (and IDEs) but aren't retained in the compiled code, how CLASS is retained in the code but not necessarily available at runtime, and how RUNTIME is certain to be available at runtime.&lt;br /&gt;
&lt;br /&gt;
Then they wanted to know how to use them. Not how to implement them, but how to access the @Retention(RetentionPolicy.RUNTIME) declared annotations.&lt;br /&gt;
&lt;br /&gt;
I hated to admit that I didn't really know. I knew there weren't any frequently used utility classes for grabbing annotations, but that there were annotation-related methods on the reflection classes. I knew in practice &amp;lt;i&amp;gt;that&amp;lt;/i&amp;gt; they were used, but not directly &amp;lt;i&amp;gt;how&amp;lt;/i&amp;gt;, so I set out to learn so I could completely satisfy their questions.&lt;br /&gt;
&lt;br /&gt;
To be sure, I use annotations all day long. I probably @Deprecate a method every day, sometimes permanently, sometimes just to quickly find uses as Eclipse is faster at adding them to problems than it is at searching. Any class that extends or implements another is surely fraught with @Override annotations. I probably write twice as many @Test annotated methods than anything else. I have @Autowire in nearly every function-full class of a Spring application, and every Hibernate project is filled with the @Column and all the other JPA annotations.&lt;br /&gt;
&lt;br /&gt;
What I hadn't had to do, though, was write any code to find and use annotations. I can't even recall passing an annotation to a method to try to identify them. After much digging, I ascertained that indeed there aren't any utility classes that aid in finding or using annotations. There's got to be some heavy work behind the classpath scanning in frameworks like Spring and Hibernate to find annotated classes and methods. I'm not quite prepared to dive into that, but let's look at some simple cases of using annotations.&lt;br /&gt;
&lt;br /&gt;
First, a simple set of annotations should be built for our various uses. Let's take one for each kind of element: class, method, and property.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
public @interface TypeAnnotation { }&lt;br /&gt;
&lt;br /&gt;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
public @interface FieldAnnotation { }&lt;br /&gt;
&lt;br /&gt;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
public @interface MethodAnnotation { }&lt;br /&gt;
&lt;br /&gt;
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
public @interface ParameterAnnotation { }&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
There are actually eight different ElementTypes that can be used, but these are surely the most common, and are certainly enough to provide a thorough example. I'll expand on the simple examples here, but I wanted to first discuss the annotations on our annotations.&lt;br /&gt;
&lt;br /&gt;
The @Target annotation lets the compiler know where the annotation may be used. Compilers and IDEs will warn if the annotation is used on the wrong type of element. If the @Target is left off, the annotation may be applied anywhere without warning. The ElementType must be defined, and allows multiple, but not duplicate entries. Multiple ElementType allow the annotation to be used in any of those instances.&lt;br /&gt;
&lt;br /&gt;
The @Retention annotation lets the compiler know how long to maintain the annotation. As mentioned, there are three RetentionPolicy values that are allowed, SOURCE, CLASS, and RUNTIME. If the @Retention annotation is used, a RetentionPolicy must be declared. If the @Retention annotation is not used, the default is CLASS, which will keep the annotation in the compiled class, but it may not be available to the runtime.&lt;br /&gt;
&lt;br /&gt;
The @interface is used to define the annotation. The name of the interface is then used when putting the annotation in source later. Other than that modification to the naming, the annotation is declared much like any other interface, in that you can declare member variables and methods. It is important to note these aren't intended for use as regular interfaces, so there are some other restrictions. Member variables must be public, static, or final, but may be of any valid type. Only public and abstract modifiers are allowed on methods. And only a small set of return types are allowed for methods; strictly, primitives, Strings, enumerations, other annotations are allowed, or 1-dimensional arrays of those types.&lt;br /&gt;
&lt;br /&gt;
One more bit, if you want to make an annotation that takes a value without a name, you must declare a member variable named &amp;quot;value.&amp;quot; That is, like @SuppressWarnings(&amp;quot;unchecked&amp;quot;). It can be a single value or array, depending on your needs. If you define it as an array you can provide several values by wrapping them in squiggly-braces like @SuppressWarnings({&amp;quot;unchecked&amp;quot;,&amp;quot;unused&amp;quot;}) would do. If you do not wish to use &amp;quot;value,&amp;quot; the variable must be explicitly declared (and, truly, value is optional) such as @SuppressWarnings(value=&amp;quot;unchecked&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Other differences will become apparent as the article continues, and as you play with annotations of your own. In trying to keep the article short, and not delve too much into every possible use case, I'll show some simple uses, point out some of these other nuances of @interface definitions, and leave it to the reader to expand on that.&lt;br /&gt;
&lt;br /&gt;
We've got some trivial annotations set up, so let's apply them to a simple class.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@TypeAnnotation class Foo {&lt;br /&gt;
  @FieldAnnotation public Object object;&lt;br /&gt;
&lt;br /&gt;
  @MethodAnnotation public Object setObject(@ParameterAnnotation final Object object){&lt;br /&gt;
    this.object = object;&lt;br /&gt;
  }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
If you've used annotations in your code, you've probably used some or all of these kinds of annotations. Certainly, the annotations used before have actually done something. Before we get into doing something, let's try to find the annotations at runtime.&lt;br /&gt;
&lt;br /&gt;
I mentioned previously that Spring and Hibernate and other frameworks have functionality to scan classes in the classpath for annotations. If you've taken a look at how they do it (they're both open-source projects, so dig in...) or hit a search engine looking for the answer, you probably have seen or correctly surmised that interrogating a classpath is not as easy as it seems. As such, I'm not going to try to lay that out here (sorry to whet your appetite), but instead will use the annotations in a more explicit manner.&lt;br /&gt;
&lt;br /&gt;
First, a simple method to see if the annotation exists, just as an example of accessing them.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public boolean isAliased(final Class&amp;lt;?&amp;gt; type) {&lt;br /&gt;
    final TypeAnnotation typeAnnotation = type.getAnnotation(TypeAnnotation.class);&lt;br /&gt;
    return (typeAnnotation != null);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The trivial example interrogates the provided class using the Class.getAnnotation() method. There's a similar Class.getAnnotations() that will return an array (zero-length if empty) of the annotations if you want to check for more than one. This would be used very simply by something like the following snippets, the first returning true, and the other false.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;isAliased(Foo.class);&lt;br /&gt;
isAliased(&amp;quot;yes, a literal&amp;quot;.getClass());&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
It isn't a terribly useful example, so let's make a method that does a little more. First, let's expand one of our annotations to give us some runtime differences from source.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
public @interface FieldAnnotation { String value(); }&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Here we've changed our FieldAnnotation to require we provide some additional information in the form of a String property. It's been named &amp;quot;value&amp;quot; for convenience, so we can use it without forcing the use of the name. Note that it looks like a method declaration, but we'll be treating it as both a variable and a function.&lt;br /&gt;
&lt;br /&gt;
If we applied the same annotation to the same class as before, we'd run into an error as we're missing a required property. Let's update our class a little and give this value some functionality.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@TypeAnnotation class Foo {&lt;br /&gt;
    @FieldAnnotation(&amp;quot;here&amp;quot;) public Object object;&lt;br /&gt;
&lt;br /&gt;
    @FieldAnnotation(&amp;quot;there&amp;quot;) public Object other;&lt;br /&gt;
&lt;br /&gt;
    @MethodAnnotation public Object setObject(@ParameterAnnotation final Object object){&lt;br /&gt;
      this.object = object;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Again, the class isn't terribly useful, but we now have two annotated member variables with different names. Let's make a quick method to set those values by their annotation name, ignoring their object name.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;void setPropertyByAnnotationName(final Object object, final String name, final Object value) {&lt;br /&gt;
    final Field[] fields = object.getClass().getDeclaredFields();&lt;br /&gt;
    for (final Field field : fields) {&lt;br /&gt;
        final FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);&lt;br /&gt;
        if(fieldAnnotation!=null &amp;amp;&amp;amp; name.equals(fieldAnnotation.value()){&lt;br /&gt;
            field.set(object, value);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Lots of spots for error there, as there's no null checking, it assumes the field is accessible, and the object is of the right type...but it matches our simple Foo class, so we'll let it go for now. As with the Class, there is a Field.getAnnotations() in case you'd like to investigate more than one annotation. What we have is a method that interrogates our object (first one) to look for fields (member variables) annotated with our FieldAnnotation. When it finds one, it checks to see if the name matches the value (note it looks like a function here) in the annotation. When there's a match, it sets the value as provided by our value parameter. This would be used simply as thus:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;Foo foo = new Foo();&lt;br /&gt;
setPropertyByAnnotationName(foo, &amp;quot;here&amp;quot;, &amp;quot;this is set!&amp;quot;);&lt;br /&gt;
setPropertyByAnnotationName(foo, &amp;quot;there&amp;quot;, Calendar.getInstance());&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
With this, we should end up with a Foo.object that contains the String &amp;quot;this is set!&amp;quot; and Foo.other that contains a Calendar with the current time in it.&lt;br /&gt;
&lt;br /&gt;
One might desire to ask what good is that? We surely have access to Foo.object and Foo.other directly in this class, but it will certainly be the case that we might not have such access in other classes. Additionally, with the annotation, we don't even really need to care that it's a Foo object. We could very easily declare another class with similar annotated members and have their fields accessed with the same method we just created. This is roughly how Spring's @Autowire works. They, of course, do a bit more in terms of recognizing names and types and whatnot without explicit definition, but this is the crux of it.&lt;br /&gt;
&lt;br /&gt;
Accessing methods is done pretty much the same way. Let's use the @MethodAnnotation to use the setter we've got. Note that calling methods is a lot trickier because the annotation itself has no way to ensure that the parameter list is correct. It is up to the method using reflection to determine if the prototype of the method is something it can work with. For brevity, I'll leave this using the original annotation, which means every annotated method will be called and its value set to the same thing; the same modifications on the @FieldAnnotation can be done here for similar functionality.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;void callMethodByAnnotation(final Object object, final Object value) {&lt;br /&gt;
    final Method[] methods = object.getClass().getDeclaredMethods();&lt;br /&gt;
    for (final Method method : methods) {&lt;br /&gt;
        final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);&lt;br /&gt;
        if(fieldAnnotation!=null){&lt;br /&gt;
            method.invoke(object, value);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Again, a slew of opportunities for errors with missing null-checks, accessibility, and the mentioned parameter list validation. Also, there is a similar Method.getAnnotations() method that will return an array of annotations in case you want to check for more than one. This method simply checks the class for all methods with the right annotation, and if found, tries to invoke the method with the provided object as the parameter. Again, with this functionality you can find methods in classes without caring about their real names. This, again with more guts, is how Spring handles its @RequestMapping methods in @Controller classes. As before, this can be implemented with this simple &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;Foo foo = new Foo();&lt;br /&gt;
callMethodByAnnotation(foo, &amp;quot;&amp;quot;this is set!&amp;quot;);&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
In the end, we end up with Foo.other set to a String saying &amp;quot;this is set!&amp;quot; using the annotation instead of calling the method directly.&lt;br /&gt;
&lt;br /&gt;
Trickier is handling the ElementTypePARAMETER annotations. This is because there's no direct reflection access to the parameter list. There are a few arrays that can be gathered to help identify the parameters, but the parameters are actually only realized when you use the Method.invoke(), so to use these, you need to handle all of the parameters by annotation, name, or type. A typical use would be some form of IOC or other runtime association of parameters to other values available to the application. &lt;br /&gt;
&lt;br /&gt;
Why this is tricky is that there is no opportunity, except perhaps inside an invoked method, to access the parameter annotations when calling the methods directly. With that understanding, we can see that these are best paired with annotated classes or methods, where the functions are going to be called indirectly, and therefore there is time to interrogate the Method for annotations before calling Method.invoke. That said, let's modify our other method-calling method to take a map of values to tie to our parameters.&lt;br /&gt;
&lt;br /&gt;
Before we do, though, this brings about another sticky bit. Unlike classes, member variables, and methods, parameters don't always keep their names. When classes are compiled, the parameters are reduced essentially to a type and order. The variable name associated with it is not kept with the method unless the class is compiled with debugging symbols. As such, like we did for the fields, we'll add a required value to represent the name of the parameter. This way, the annotation will maintain the value that we'll use to pull values from our map, and it won't matter if debug is enabled or not; our annotation will continue to work.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
public @interface ParameterAnnotation { String value(); }&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
And since it's required with no default, we'll have to change our class appropriately.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@TypeAnnotation class Foo {&lt;br /&gt;
    @FieldAnnotation(&amp;quot;here&amp;quot;) public Object object;&lt;br /&gt;
&lt;br /&gt;
    @FieldAnnotation(&amp;quot;there&amp;quot;) public Object other;&lt;br /&gt;
&lt;br /&gt;
    @MethodAnnotation public Object setObject(@ParameterAnnotation(&amp;quot;inside&amp;quot;) final Object object){&lt;br /&gt;
        this.object = object;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
And we'll modify the method to take advantage of that annotation.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public void callMethodByAnnotation(final Object object, final Map&amp;lt;String, Object&amp;gt; map) {&lt;br /&gt;
    final Method[] methods = object.getClass().getDeclaredMethods();&lt;br /&gt;
    for (final Method method : methods) {&lt;br /&gt;
        final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);&lt;br /&gt;
        if (methodAnnotation != null) {&lt;br /&gt;
&lt;br /&gt;
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();&lt;br /&gt;
            final Object[] parameters = new Object[parameterAnnotations.length];&lt;br /&gt;
            for (int i = 0; i &amp;lt; parameterAnnotations.length; i++) {&lt;br /&gt;
                parameters[i] = null;&lt;br /&gt;
&lt;br /&gt;
                final Annotation[] annotations = parameterAnnotations[i];&lt;br /&gt;
                for (final Annotation annotation : annotations) {&lt;br /&gt;
                    if (annotation instanceof ParameterAnnotation) {&lt;br /&gt;
                        parameters[i] = map.get(((ParameterAnnotation) annotation).value());&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            method.invoke(object, parameters);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Note the busy work necessary to grab the annotation. The annotations come from an array of arrays. The first dimension is one for each parameter, and the second dimension is the array of annotations for the parameter at that first dimension. If there are no parameters, the first dimension array is zero-length. For each parameter, if there are no annotations, that second dimension array is zero length. There is a related Method.getParameterTypes() that could be used to be certain that the type in the map was compatible with the type in the parameter, but ours is an Object, so we can be a little sloppy.&lt;br /&gt;
&lt;br /&gt;
The new method can be used very much like the other, except we'll pass the parameters in a Map. We could, of course, use any mechanism for keeping track of the things that could be put in the parameter list. If you've used Spring to annotate a @Controller with @RequestMethod methods, you'll notice that you can just toss in a whole slew of parameters such as ModelMap or HttpServletRequest and it will figure them out by type.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;Map map = new Map();&lt;br /&gt;
map.put(&amp;quot;inside&amp;quot;, &amp;quot;this is set!&amp;quot;);&lt;br /&gt;
Foo foo = new Foo();&lt;br /&gt;
callMethodByAnnotation(foo,map);&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Same caveats as before regarding shortcuts taken for brevity... Now when we call our method, it will check our map for the value. If it doesn't find an element in the map that matches our annotation name, it puts null in the parameter list. As before, our Foo.other will have a String with the value &amp;quot;this is set!&amp;quot; when we're done.&lt;br /&gt;
&lt;br /&gt;
That seems like a lot of busy work to make sense of annotations. Really it's a lot of fluff code around it to make it understandable. Really, the magic lies in the getAnnotations() or getAnnotation() methods on the Class and the reflection classes Field and Method, and the Method.getParameterAnnotations() method. And, of course, our trivial examples don't carry much insight into any kind of usefulness, so let's expand on this and make a one-class annotated XML parser. A very simple one, mind you.&lt;br /&gt;
&lt;br /&gt;
Parsing XML is a pain that way too many of us go through. There are plenty of tools to help us do this, so this isn't intended as a replacement or any kind of competition for them. It's just familiar territory, and one I think I can squeak in a small pair of classes for a solid example of annotation use.&lt;br /&gt;
&lt;br /&gt;
Consider this fairly trivial XML, where we have  a Foo element with a single attribute, id, and a nested element also named Foo.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&amp;amp;lt;Foo id=&amp;quot;alpha&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;Foo/&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;/Foo&amp;amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The astute among us can quickly envision a likewise trivial POJO that would do the same. I'm making public members for brevity; for real, we'd use getters and setters, right?&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;class Foo {&lt;br /&gt;
    public String id;&lt;br /&gt;
    public Foo foo;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
With this class in mind, we can see we'll need a couple annotations to handle identifying the class and our members.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
@interface XMLElement { String value() default &amp;quot;&amp;quot;; }&lt;br /&gt;
&lt;br /&gt;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)&lt;br /&gt;
@interface XMLAttibute { String value() default &amp;quot;&amp;quot;; }&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
A quick word about the two noticeable differences from the previous examples. First, we'll note that the XMLElement has been annotated with two ElementType targets. This allows the same attribute to be used to identify types and members, as XML elements are often neatly identified with POJOs. Also, I've added a default to each. This is both to show what it looks like and allow us to optionally rename the item in question. A small annoyance with the defaults is that there is no &amp;lt;i&amp;gt;null&amp;lt;/i&amp;gt; value. When the value is an array, you can declare an empty list as the default, but that's not really null either.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@interface ArrayExampleAnnotation { String[] value() default {}; }&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This allows us to alter our class to add the annotations as follows:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@XMLElement class Foo {&lt;br /&gt;
    @XMLAttribute public String id;&lt;br /&gt;
    @XMLElement public Foo foo;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
We could be more explicit and annotate it thusly:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@XMLElement(&amp;quot;Foo&amp;quot;) class Foo {&lt;br /&gt;
    @XMLAttribute(&amp;quot;id&amp;quot;) public String id;&lt;br /&gt;
    @XMLElement(&amp;quot;Foo&amp;quot;) public Foo foo;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Or even make the POJO and XML a little less tightly coupled by changing the POJO names from the expected XML names (this is where the value makes more sense!):&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;@XMLElement(&amp;quot;Foo&amp;quot;) class FiddleStix {&lt;br /&gt;
    @XMLAttribute(&amp;quot;id&amp;quot;) public String identifyingAttribute;&lt;br /&gt;
    @XMLElement(&amp;quot;Foo&amp;quot;) public FiddleStix next;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Now we &amp;lt;i&amp;gt;just&amp;lt;/i&amp;gt; need a function to digest our XML string and populate our POJO. To be short and sweet, and not emphasize the XML processing too much, I'm just going to use the typical W3C DOM classes that come with the Java runtime. Easy, short-ish.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;public static &amp;lt;POJO&amp;gt; POJO turnXMLStringIntoPOJO(final String string, final Class&amp;lt;POJO&amp;gt; type) {&lt;br /&gt;
    final POJO pojo = type.newInstance();&lt;br /&gt;
    final XMLElement topXmlElement = type.getAnnotation(XMLElement.class);&lt;br /&gt;
    final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()&lt;br /&gt;
            .parse(new ByteArrayInputStream(string.getBytes()));&lt;br /&gt;
    if (document.getNodeName().equals(topXmlElement.value())) {&lt;br /&gt;
        final NamedNodeMap namedNodeMap = document.getAttributes();&lt;br /&gt;
        for (int i = 0; i &amp;lt; namedNodeMap.getLength(); i++) {&lt;br /&gt;
            final Node node = namedNodeMap.item(i);&lt;br /&gt;
            String name = node.getNodeName();&lt;br /&gt;
            final Field[] fields = type.getDeclaredFields();&lt;br /&gt;
            for (final Field field : fields) {&lt;br /&gt;
                final XMLAttribute xmlAttribute = field.getAnnotation(XMLAttribute.class);&lt;br /&gt;
                if (xmlAttribute != null) {&lt;br /&gt;
                    if ((&amp;quot;&amp;quot;.equals(xmlAttribute.value()) &amp;amp;&amp;amp; field.getName().equals(name))&lt;br /&gt;
                            || xmlAttribute.value().equals(name)) {&lt;br /&gt;
                        field.set(pojo, node.getNodeValue());&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        final NodeList nodeList = document.getChildNodes();&lt;br /&gt;
        for (int i = 0; i &amp;lt; nodeList.getLength(); i++) {&lt;br /&gt;
            final Node node = nodeList.item(i);&lt;br /&gt;
            final String name = node.getNodeName();&lt;br /&gt;
            final Field[] fields = type.getDeclaredFields();&lt;br /&gt;
            for (final Field field : fields) {&lt;br /&gt;
                final XMLElement xmlElement = field.getAnnotation(XMLElement.class);&lt;br /&gt;
                if (xmlElement != null) {&lt;br /&gt;
                    if ((&amp;quot;&amp;quot;.equals(xmlElement.value()) &amp;amp;&amp;amp; field.getName().equals(name))&lt;br /&gt;
                            || xmlElement.value().equals(name)) {&lt;br /&gt;
                        final Object object = field.getType().newInstance();&lt;br /&gt;
                        field.set(pojo, object);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return pojo;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
With a little recursion or other care, this simple method could be used to handle nesting our XML so that Foo could have Foo wtih Foo containing Foo... But for our purposes, this meets our needs. Again, much with the caveats of missing care exchanged for brevity. Notice it's a generic class, giving us flexibility to just send it any old class. With our caveats, it makes assumptions that the class is annotated (or null pointer exceptions will occur). The string is quickly (since it's small) into a DOM tree, and we make two passes through it, once for the attributes and once for the elements. We compare the names of the nodes to the names (value) of our annotations, if they're defined, or to the name of the field, if the value is empty. If a match is found for the annotation, we set the value to whatever the XML contained. If a match is contained for the element, we simply instantiate the right type and add it to our object. We could explore the DOM deeper and set its values, but that's a little larger example.&lt;br /&gt;
&lt;br /&gt;
This class could take all three of our example classes (careful, as two of the classes will conflict) and parse the same XML and get the expected results.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;String xmlString = &amp;quot;&amp;amp;lt;Foo id=\&amp;quot;alpha\&amp;quot;&amp;amp;gt;&amp;amp;lt;Foo /&amp;amp;gt;&amp;amp;lt;/Foo&amp;amp;gt;&amp;quot;;&lt;br /&gt;
Foo foo = turnXMLStringIntoPOJO(xmlString, Foo.class);&lt;br /&gt;
FiddleStix fiddleStix = turnXMLStringIntoPOJO(xmlString, FiddleStix.class);&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
What should result is that our objects would have an id String of &amp;quot;alpha&amp;quot; and their same-type member populated with an empty, but instantiated item. Both classes, from the same XML just because of the annotations. &lt;br /&gt;
&lt;br /&gt;
And that, my friends, is how you use annotations. Well, one way...&lt;/div&gt;</description>
			<pubDate>Wed, 21 Jul 2010 19:54:59 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:How_Do_Annotations_Work%3F</comments>		</item>
		<item>
			<title>Self-certify Apache</title>
			<link>http://www.softwarebyjeff.com/index.php/Self-certify_Apache</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here’s how to create the files and update the configuration to create a self-certified Apache SSL/HTTPS server.&lt;br /&gt;
&lt;br /&gt;
A major assumption is the availability of the OpenSSL tools. As such, this is easier to do on a Linux distro and then copy the resulting files over. This shows and assumes such. Also, the steps are being performed as if root. Security and file access is out of the scope of this document.&lt;br /&gt;
&lt;br /&gt;
Important bits, either user input or parts to remember for later use, are highlighted. Of course, use common sense when the example doesn't match your configuration, adjusting appropriately for your intent and environment.&lt;br /&gt;
&lt;br /&gt;
== Certificate Authority ==&lt;br /&gt;
&lt;br /&gt;
This only needs to be done once, and you can either create more keys on the same server (mind the file names or paths), or copy the CA key and cert files around for use on other servers.&lt;br /&gt;
&lt;br /&gt;
The following will create a 4096-bit strength DES3 encrypted key and put it in the file ca.key. The name is  unimportant, but must be remembered as it’s used later. The key strength is also up to you; 4096 is plenty strong; I'm not sure if it can make a stronger key, in fact. Running this command will prompt you for a password and confirmation; they can’t be blank, unfortunately. Depending on the OpenSSL used, some input (mouse, bang the keys, copy files, whatever) will help speed the randomness.&lt;br /&gt;
&lt;br /&gt;
The names ca.key &amp;amp; ca.crt can be whatever you want them to be. Remember them for later use.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openssl genrsa -des3 -out ca.key 4096&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then use the key to create a certificate. Again, the names don’t matter, but they need to be remembered and used. Note this one expires in about a year (given allowance for leap-years). You’ll be prompted for the CA key password. You’ll also have to give a little bit of text that will allow users to determine the validity of the certificate (example follows). The key is that the Common Name should match the FQDN of the cert system. Since we’re not publishing the cert, it’s kind if moot, but good practice to follow.&lt;br /&gt;
&lt;br /&gt;
You'll have to type the first line completely (names and such are up to you), but then the program will prompt you for the rest; use your data, not my examples.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openssl req -new -x509 -days 365 -key ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Country Name (2 letter code) [AU]:US&lt;br /&gt;
&lt;br /&gt;
State or Province Name (full name) [Some-State]:Minnesota&lt;br /&gt;
&lt;br /&gt;
Locality Name (eg, city) []:Minneapolis&lt;br /&gt;
&lt;br /&gt;
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company&lt;br /&gt;
&lt;br /&gt;
Organizational Unit Name (eg, section) []:Department&lt;br /&gt;
&lt;br /&gt;
Common Name (eg, YOUR name) []:server.local&lt;br /&gt;
&lt;br /&gt;
Email Address []:admin@server.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Certificate Signing Request ===&lt;br /&gt;
&lt;br /&gt;
The CSR contains info about the server that the CA will use to generate a certificate. A key is used to encode the data. If you were going to use a proper third-party CA, they may have a tool to help you generate this, or you’d generate one yourself and then send them the CSR (probably in ASCII form).&lt;br /&gt;
&lt;br /&gt;
Like the CA, this makes a DES3 encrypted key. You’ll also be prompted for a password, and it can’t be blank. Remember the name for later use.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openssl genrsa -des3 -out server.key 4096&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use the key to generate the CSR. Again, like the CA, you’ll be prompted for the key password and some input for the details about the certified system; in a real third-party situation this file would be sent to them, and the information within would be validated and authenticated by various means, depending on the quality of the certificate desired—for our needs, this is just the text displayed when viewing the certificate. &lt;br /&gt;
&lt;br /&gt;
=== Common Name ===&lt;br /&gt;
&lt;br /&gt;
There is one special value here! When generating the CSR, the '''Common Name''' needs to match the server being certified as this will be compared when the clients reach the server.&lt;br /&gt;
&lt;br /&gt;
You'll have to type the first line completely (names and such are up to you), but then the program will prompt you for the rest; use your data, not my examples.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openssl req -new -key server.key -out server.csr&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Country Name (2 letter code) [AU]:US&lt;br /&gt;
&lt;br /&gt;
State or Province Name (full name) [Some-State]:Minnesota&lt;br /&gt;
&lt;br /&gt;
Locality Name (eg, city) []:Minneapolis&lt;br /&gt;
&lt;br /&gt;
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company&lt;br /&gt;
&lt;br /&gt;
Organizational Unit Name (eg, section) []:Department&lt;br /&gt;
&lt;br /&gt;
Common Name (eg, YOUR name) []:fully.qualified.domain.name&lt;br /&gt;
&lt;br /&gt;
Email Address []:admin@domain.name&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Please enter the following 'extra' attributes&lt;br /&gt;
&lt;br /&gt;
to be sent with your certificate request&lt;br /&gt;
&lt;br /&gt;
A challenge password []:&lt;br /&gt;
&lt;br /&gt;
An optional company name []:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember, that ''fully.qualified.domain.name'' needs to match the name of the server that will finally use the certificate. If you try to make this generic or if it's wrong, the browsers will receive an error indicating the server name mismatch.&lt;br /&gt;
&lt;br /&gt;
== Certificate ==&lt;br /&gt;
&lt;br /&gt;
Finally, we can create the certificate. This file and the key file will be given to Apache for serving the SSL. Using a third-party CA, this is the file they’d send you. You’d use that certificate file and the key file you used to generate the CSR to work on the server.&lt;br /&gt;
&lt;br /&gt;
Here we’re asking for a year-ish (again, allowing for leap years) long file. You’ll be prompted for the CA key password created before.&lt;br /&gt;
&lt;br /&gt;
=== Serial Number ===&lt;br /&gt;
&lt;br /&gt;
A quick word about the serial number. Unless we’ve got our openssl set up to keep track of the serial numbers for us (beyond this scope), we need to specify one—the *XX* in the command line example. Specifically, it’s got to be a new generation (incrementing numeric) for each FQDN certified; generally, just use an increment so that you don’t have to maintain the number per certificate. If in doubt, look at the old certificate.&lt;br /&gt;
&lt;br /&gt;
Start with 1, and every time you make a cert, use the next number. Or start huge and use the epoch timestamp. As long as the newer is larger than the previous, all will be well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial *XX* -out server.crt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally, we’ll take the password out of the equation. There are tricks to share the password with Apache, but really more secure than that is to make a password-less copy of the key file and make the Apache process owner (httpd) the owner of the file,  and restrict the access to just that user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openssl rsa -in server.key -out server.key.insecure&lt;br /&gt;
cp server.key server.key.secure&lt;br /&gt;
cp server.key.insecure server.key&lt;br /&gt;
chmod 600 server.key&lt;br /&gt;
chown httpd:httpd server.key&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Apache Configuration ==&lt;br /&gt;
&lt;br /&gt;
Now copy the server.crt and password-free server.key files to the Apache conf folder (or remember to replace the sample with the complete and accurate path). As repeatedly mentioned, the names of the file are irrelevant (with regard to their content), so feel free to locate and name them in ways that make sense.&lt;br /&gt;
&lt;br /&gt;
Add a few bits to the Apache configuration to use the files. If you review the extras/httpd-ssl.conf that comes with the Apache distribution, there’s a lot in there. Here’s the meat of what is needed. Note, just use the key and cert file names and paths that match.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LoadModule ssl_module modules/mod_ssl.so&lt;br /&gt;
&lt;br /&gt;
Listen 443&lt;br /&gt;
&lt;br /&gt;
NameVirtualHost *:443&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;VirtualHost *:443&amp;amp;gt;&lt;br /&gt;
    SSLEngine On&lt;br /&gt;
    SSLCertificateFile conf/server.crt&lt;br /&gt;
    SSLCertificateKeyFile conf/server.key&lt;br /&gt;
&amp;amp;lt;/VirtualHost&amp;amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Virtual Host ===&lt;br /&gt;
&lt;br /&gt;
A word about the VirtualHost. SSL is a socket-layer wrapper, and therefore occurs before the HTTP request. The VirtualHost handling mechanism for Apache happens by evaluating the HTTP request and headers. If intending to use SSL on a true virtual hosting system, each host needs its own IP or port (443 is the HTTPS default) on the server, and the VirtualHost notation needs to be affected to match accordingly. It's also worth noting that the server certificate should be unique for each (or SSL errors will occur), but the key file can be shared among the different certificates; simply, the key file needs to match with the one used to create the certificate request.&lt;/div&gt;</description>
			<pubDate>Tue, 04 May 2010 16:21:49 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Self-certify_Apache</comments>		</item>
		<item>
			<title>Screen</title>
			<link>http://www.softwarebyjeff.com/index.php/Screen</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Screen is a valuable tool to extend shell sessions. It provides some session security, preventing loss due to disconnection. It also allows multple shell sessions at the same time through the same connection, with simple switching and copying between each.&lt;br /&gt;
&lt;br /&gt;
This should help make screen more useful from the start.&lt;br /&gt;
&lt;br /&gt;
== Starting Bits ==&lt;br /&gt;
&lt;br /&gt;
For most of the modern Linux distributions, screen is probably installed by default. If not, it'll certainly be available in the distribution's software repository. If not, here's some links to get it and get it going.&lt;br /&gt;
&lt;br /&gt;
The screen homepage: http://www.gnu.org/software/screen/&lt;br /&gt;
&lt;br /&gt;
The screen manual: http://www.gnu.org/software/screen/manual/screen.html&lt;br /&gt;
&lt;br /&gt;
== Starting Screen ==&lt;br /&gt;
&lt;br /&gt;
To simply start a new screen session when connected to a system with a  terminal program (e.g., PuTTY or other SSH): '''screen'''&lt;br /&gt;
 &lt;br /&gt;
Even better, to reconnect to existing screens (or create a new one if it doesn’t exist): '''screen –DR'''&lt;br /&gt;
&lt;br /&gt;
To see if you’re already running a screen: '''screen -list'''&lt;br /&gt;
&lt;br /&gt;
Most of the time, '''screen –DR''' is the way to do it. Shell (SSH or TELNET) to a system, type ''screen –DR'' and you should be presented with a screen-wrapped shell session. Work there instead of in the raw shell. Then if you close the terminal window (accidentally or on purpose), the network drops, or any other flake occurs, the work is still in-progress on the remote system and waiting for your return.&lt;br /&gt;
&lt;br /&gt;
== Start-up Configuration ==&lt;br /&gt;
&lt;br /&gt;
Create a file in your home directory named '''.screenrc''' and put these bits in there, and you’ll be able to tell when you’re in a screen. Other bits are described in the documentation, and hints follow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
shell -bash&lt;br /&gt;
shelltitle &amp;quot;$ |bash&amp;quot;&lt;br /&gt;
hardstatus on&lt;br /&gt;
hardstatus alwayslastline&lt;br /&gt;
hardstatus string ' [ %H ] %{wb} %c:%s | %d.%m.%Y %{wr} Load: %l %{wb} %w '&lt;br /&gt;
defscrollback 50000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The options in the file are plentiful, especially when looking for what the hardstatus bits mean. http://www.gnu.org/software/screen/manual/screen.html#Command-Summary&lt;br /&gt;
&lt;br /&gt;
== Working with screen ==&lt;br /&gt;
&lt;br /&gt;
These are the used-constantly bits. Much, much more available in the documentation. http://www.gnu.org/software/screen/manual/screen.html#Default-Key-Bindings&lt;br /&gt;
&lt;br /&gt;
In screen, the default hot-key is '''Ctrl-a'''. You can change that in the .screenrc file if you want to (see the &amp;quot;escape&amp;quot; option in the Command-Summary page above). To be clear on notation, &amp;quot;Ctrl-a a&amp;quot; means to hold control, tap a, release control, tap a again, of course.&lt;br /&gt;
&lt;br /&gt;
To send hot-key to your program (instead of letting screen eat it): '''Ctrl-a a''' &lt;br /&gt;
&lt;br /&gt;
To exit screen, there are a few options. These are very frequently used:&lt;br /&gt;
&lt;br /&gt;
* To hard-kill all open screens and end this screen session (no longer running): '''Ctrl-a Ctrl-\'''&lt;br /&gt;
* To disconnect from screen (stays running) and stay in your shell: '''Ctrl-a d'''&lt;br /&gt;
* To disconnect from screen (stays running) and close your shell (power disconnect): '''Ctrl-a DD''' &lt;br /&gt;
&lt;br /&gt;
That last is useful when you’re using PuTTY or SSH to connect to the server, start screen, and then don’t need the shell again—PuTTY or the terminal running SSH will close. The alternative is that you’ll exit screen, and still be in the shell, so you’d have to exit that shell to close PuTTY or end the SSH session. If you’re on a Linux box, SSH’d to another box, you’ll have to open the terminal, SSH to another box, be at that prompt, enter screen…and exit each of those; power disconnect closes that first terminal! It does leave the screen running, so when you return to that server and restart screen (with –DR) it’ll reconnect and leave you where you were.&lt;br /&gt;
&lt;br /&gt;
* To clear the screen when in screen: '''Ctrl-a C'''&lt;br /&gt;
* To create a new screen in screen: '''Ctrl-a c''' &lt;br /&gt;
&lt;br /&gt;
An evil little bit of confusion there as older screen is still there, but dirty, but it looks like it cleared because of the new screen that appears.&lt;br /&gt;
&lt;br /&gt;
When working with multiple screens:&lt;br /&gt;
&lt;br /&gt;
* To switch to “next” screen: '''Ctrl-a n'''&lt;br /&gt;
* To switch to “previous” screen: '''Ctrl-a p'''&lt;br /&gt;
* To exit a screen in screen: '''Ctrl-d''' (or type exit) – exiting the last screen will exit screen&lt;br /&gt;
* To help keep track, you can change the title for a screen (from the &amp;quot;bash&amp;quot; in the .screenrc example above): '''Ctrl-a A'''&lt;br /&gt;
&lt;br /&gt;
One evilness in screen is that the scrolling in your terminal window is disrupted; no more useful scrollbars. Because screen is maintaining its buffer internally, the terminal scroll window doesn’t really have much to do with the scrollback buffer.  Curiously, even though it does this, it offers no navigation mechanism! One easy trick to navigate around in screen is switch to copy-mode and use the arrow keys instead of the mouse.&lt;br /&gt;
&lt;br /&gt;
* To enter copy-mode:  '''Ctrl-a ['''&lt;br /&gt;
* To mark a block, hit enter to start, navigate, and hit enter again to end.&lt;br /&gt;
* To exit copy-mode without affecting the clipboard, hit escape.&lt;br /&gt;
&lt;br /&gt;
Another benefit screen gives in this area is that if you resize the window, the screen contents resize with it. This can get a little quirky if you resize while running an interactive program (like vim) because that program will have dealt with the resizing, but the shell underneath will not. Quick cure when that funkiness becomes apparent is to resize the window again (even if it’s just grab the frame and drop it).&lt;/div&gt;</description>
			<pubDate>Tue, 04 May 2010 15:53:30 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Screen</comments>		</item>
		<item>
			<title>Unnecessary Try Catch</title>
			<link>http://www.softwarebyjeff.com/index.php/Unnecessary_Try_Catch</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I have come upon another example of useless code. This is in my trip through some horrible JUnit tests. Here is some real code, with the important bits cut out or made to work so it will run.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;CODE&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class UnnecessaryTest extends junit.framework.TestCase {&lt;br /&gt;
    public void testUnnecessary() throws Exception {&lt;br /&gt;
        try {&lt;br /&gt;
            /* Anything you want */&lt;br /&gt;
        } catch( Exception e ) {&lt;br /&gt;
            throw e;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/CODE&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What is it about writing software that makes people do stupid things?&lt;br /&gt;
&lt;br /&gt;
At first glance, it just looks like careful code. When just a little bit about JUnit is known, it fast becomes clear that the catching and throwing of the exception is pointless. Perhaps if something were done inside the catch block (which is why they exist), but except for cutting out anything in the try, this is the code of the tests. Yes, '''tests'''. This try/catch exists in every test in a dozen test classes, and in all of them, the only thing done in the catch block is to throw the exception.&lt;br /&gt;
&lt;br /&gt;
That test should simply be written as thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;CODE&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class UnnecessaryTest extends junit.framework.TestCase {&lt;br /&gt;
    public void testBetter() throws Exception {&lt;br /&gt;
        /* Anything you want */&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/CODE&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cut it out, people.&lt;br /&gt;
&lt;br /&gt;
== Also...found in PRODUCTION code ==&lt;br /&gt;
&lt;br /&gt;
I'm sure someone was setting something up, but this is actual try/catch code I found while wandering through someone's code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;CODE&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Foo  {&lt;br /&gt;
    public void foo() throws Exception {&lt;br /&gt;
        try { } catch ( Exception e ) { /* Stuff here to log e */ }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/CODE&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I know I truncate code examples and change the names, but in this case I only changed the contents of the catch block to comment the intent to log, while the real code had logger code in it. Otherwise, the contents of the try/catch are as in the real code.&lt;br /&gt;
&lt;br /&gt;
Yes. That's right. Try NOTHING. Catch and log all Exceptions thrown by NOTHING.&lt;br /&gt;
&lt;br /&gt;
Shoot me now.&lt;/div&gt;</description>
			<pubDate>Tue, 15 Apr 2008 19:09:54 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Unnecessary_Try_Catch</comments>		</item>
		<item>
			<title>Equals and HashCode</title>
			<link>http://www.softwarebyjeff.com/index.php/Equals_and_HashCode</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It happens far too often that developers shortcut creating &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; in their objects. This discussion hopes to enlighten those short-cutters, and introduce a simple solution to their problem.&lt;br /&gt;
&lt;br /&gt;
The use of &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; is assumptive of only one concern, and that is that an object is believed to be distinct based on its content, not its instantiation. If an object has no reason to be distinguished from other instances of the same object, then there is no reason to implement these methods. If, however, the contents of the object do indeed need to be evaluated, then it is not only strongly encouraged, but also quite likely required to do so.&lt;br /&gt;
&lt;br /&gt;
=== Equals and HashCode ===&lt;br /&gt;
&lt;br /&gt;
There are way too many other resources explaining the details of the &amp;quot;equals-hashCode contract&amp;quot; to go over it in much detail here, but I'll hit the high points just to make sure that everyone is on the same page.&lt;br /&gt;
&lt;br /&gt;
To start with, a basic understanding of [http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html Object] is assumed, particularly the implications of equals().&lt;br /&gt;
&lt;br /&gt;
==== Equals ====&lt;br /&gt;
&lt;br /&gt;
The basic idea of equals() is to determine if two objects have the same value. The trouble comes into determining what the &amp;quot;value&amp;quot; of an object is. An object, for this discussion, is an aggregation of properties (member variables) and optionally methods (member &amp;quot;functions&amp;quot; and &amp;quot;procedures&amp;quot;) that act on those properties. An object without properties doesn't concern this discussion, and the presence or absence of methods is not going to affect the evaluation of its properties.&lt;br /&gt;
&lt;br /&gt;
Some concepts of &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; that must hold true work for all ideas of equality. This is discussed in detail in the Object JavaDoc, but here's the main gist of the discussion that must be maintained. They're essentially the same as equals considerations for mathematics.&lt;br /&gt;
&lt;br /&gt;
If A is equal to B, then B must be equal to A. In Java, that means that &amp;lt;code&amp;gt;a.equals(b)&amp;lt;/code&amp;gt; must have the same result as &amp;lt;code&amp;gt;b.equals(a)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If A is equal to B and B is equal to C, then A must also be equal to C. In Java that means that &amp;lt;code&amp;gt;a.equals(b)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;b.equals(c)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;a.equals(c)&amp;lt;/code&amp;gt; must all have the same result.&lt;br /&gt;
&lt;br /&gt;
Finally, the same results should happen for all instances of the object with the same compared values. That is, the results need to be consistent. Every time we make a new instance of the object, and fill its members with the same values, we should get the same result out of the &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; evaluation.&lt;br /&gt;
&lt;br /&gt;
With that understanding, consider the &amp;lt;code&amp;gt;Object.equals()&amp;lt;/code&amp;gt; that every object automatically inherits. This implementation technically meets the needs of those criteria, however, it does not truly reflect the equality of an object based on its values.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;Object.equals()&amp;lt;/code&amp;gt; compares the objects references, and returns true if, and only if, the objects are the same. That is, even if different variables are used, the variables refer to the same instance of an object. Essentially, &amp;lt;code&amp;gt;Object.equals()&amp;lt;/code&amp;gt; really only works if &amp;lt;code&amp;gt;a.equals(a)&amp;lt;/code&amp;gt; is the case being tested.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Object a = new Object();&lt;br /&gt;
Object b = a;&lt;br /&gt;
a.equals(b); // This is true&lt;br /&gt;
b = new Object();&lt;br /&gt;
a.equals(b); // This is false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
One other criteria of &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; is that if a class implements &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; it must implement &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt;. The explanation is pretty straight forward.&lt;br /&gt;
&lt;br /&gt;
==== HashCode ====&lt;br /&gt;
&lt;br /&gt;
An object's &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; relates to &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; this in one simple statement.&lt;br /&gt;
&lt;br /&gt;
'''Equal objects must have equal hashCode.'''&lt;br /&gt;
&lt;br /&gt;
That's all. It is often misunderstood that &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; needs to somehow uniquely identify an object or reflect differences in &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt;, but that is expressly not the case. It is very simply the case that if &amp;lt;code&amp;gt;a.equals(b)&amp;lt;/code&amp;gt; then it must be true that &amp;lt;code&amp;gt;a.hashCode() == b.hashCode()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
It is also required that &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; needs to be consistent. Therefore, for any set of values in an object, the same &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; needs to be presented. That's all.&lt;br /&gt;
&lt;br /&gt;
If the objects do not equal each other, their &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; is irrelevant; they may be the same or different. It is not necessary to go to any lengths to ensure that if &amp;lt;code&amp;gt;a.equals(b) == false&amp;lt;/code&amp;gt; that &amp;lt;code&amp;gt;a.hashCode() != b.hashCode()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
One may argue that this means that &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; could simply return a constant value and the contract would be met. This is indeed true, but it also is fair that as much as is reasonably possible, distinct values in an object should return different &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; values. The use of the concept &amp;quot;reasonable&amp;quot; allows some flexibility and allows us to not go crazy with our &amp;lt;code&amp;gt;hashCode()&amp;lt;code&amp;gt; implementations.&lt;br /&gt;
&lt;br /&gt;
Object gets in the way here, too, because if no &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; is provided by an object, the one Object provides is essentially a representation of the object in memory. Again, this helps meet the equals-hashCode contract because if comparing the same object, the equals will be true and the hashCode will be the same.&lt;br /&gt;
&lt;br /&gt;
=== Reasons to Use ===&lt;br /&gt;
&lt;br /&gt;
Almost as important as understanding what &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; have to do with each other and some of their pitfalls, is understanding why a developer might need to implement these methods. This can be summed up with one word: '''Collection'''&lt;br /&gt;
&lt;br /&gt;
OK, so a few words should be used, but this gives a good starting point. If it is the case that an object will ever be put into a collection of any kind (Map, Set, List, etc.) that it should implement &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This again supposes that the uniqueness of an object is determined by the values of its members. In order for an object to be found by a &amp;lt;code&amp;gt;Collection.contains()&amp;lt;/code&amp;gt; method, or to be correctly distinct in a Set or as a key in a Map, equals() needs to be implemented.&lt;br /&gt;
&lt;br /&gt;
Many other good examples exist, but this is fairly trivial and tremendously common, and gives good reason for nearly every object with properties to implement &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Sample ===&lt;br /&gt;
&lt;br /&gt;
Take for this discussion this very trivial object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Simple {&lt;br /&gt;
    public Integer objectInteger = null;&lt;br /&gt;
    public int primitiveInt = 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This bean (for purposes of this and most other discussions, an object with properties and trivial methods) simply contains a small number of, well, numbers. We have one each of the object Integer and the primitive int. They're all made public to keep the source short; in a proper bean they'd be private and have getter and setter methods to access them.&lt;br /&gt;
&lt;br /&gt;
If we take this bean as written, there is no way to determine if two instances are equal based on their values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Sample a = new Simple();&lt;br /&gt;
Sample b = new Simple();&lt;br /&gt;
a.primitiveInt = 1;&lt;br /&gt;
b.primitiveInt = 1;&lt;br /&gt;
a.equals(b); // This is false!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since it is not executed, the comment must be trusted; go ahead and make the necessary files and test it out and the result will be as noted. The code sample shows the instantiation of two Sample objects, and each is provided with the same int value of one. &lt;br /&gt;
&lt;br /&gt;
It should probably be the case that these objects would be evaluated as equal, however. Looking at it logically, the object members are both null, and the int value in each instance has been set to one. These objects likely represent the same concept for which it was written.&lt;br /&gt;
&lt;br /&gt;
The equals method for this object would be pretty simple to write. There are a couple of objects, so care needs to be taken to take null into account, but this object could be corrected in one pass.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Simple {&lt;br /&gt;
    public Integer objectInteger = null;&lt;br /&gt;
    public int primitiveInt = 0;&lt;br /&gt;
&lt;br /&gt;
    public boolean equals(Object object) {&lt;br /&gt;
        if(!(object instanceof Simple))&lt;br /&gt;
            return false;&lt;br /&gt;
        Simple simple = (Simple)object;&lt;br /&gt;
        return (this == simple)  ||&lt;br /&gt;
               ((primitiveInt == simple.primitiveInt) &amp;amp;&amp;amp;&lt;br /&gt;
                ((objectInteger == null)&lt;br /&gt;
                   ? (simple.objectInteger === null)&lt;br /&gt;
                   : objectInteger.equals(simple.objectInteger)));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Looking at the new &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt; method in detail, the first thing done is to ensure that the comparison object is of the same type, and if that is not the case, surely there is no match. One helpful thing about the use of &amp;lt;code&amp;gt;instanceof&amp;lt;/code&amp;gt; is that it also catches the case of a null value passed, and an instance of a class is definitely not equal to null. The next line simply casts the parameter as our type. This can surely be done in-line, but since the comparison uses the value a few times, this is more readable.&lt;br /&gt;
&lt;br /&gt;
The last line (the returns) is one likely to be assaulted by style hawks. The bean is simple, with two members, so rather than a series of if statements one properly grouped boolean can do the job.&lt;br /&gt;
&lt;br /&gt;
The first bit compares to see if this instance is the same as the passed instance. Since the next operator is an or, the operation will return true at this point if someone is doing some derivative of &amp;lt;code&amp;gt;a.equals(a)&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Should a different instance be checked, the next part starts comparing the members. The and operator between the member checks will cause the evaluation to stop at the first failure, so the primitives are compared first. Should they be the same, the object is compared. The object comparison is done with a ternary operator to first compare for null values, then to compare the values of the objects. This provides a nice null-safe comparison that will return true if the primitives have the same value and the objects are either both null or represent the same Integer value.&lt;br /&gt;
&lt;br /&gt;
In the case that an object has some members that don't factor into the equality of the object, simply leave them out of the boolean equation, and they'll be ignored. A good example of this would be a bean that represented a row in a database; in this case, the equals comparison should only include any members that would match the database primary key (e.g., a unique ID).&lt;br /&gt;
&lt;br /&gt;
Implementing &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; is just about as simple. The key is to try to get a consistent result out of any calculation done within. The sample below shows a &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; that will work for the related &amp;lt;code&amp;gt;equals()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Simple {&lt;br /&gt;
    public Integer objectInteger = null;&lt;br /&gt;
    public int primitiveInt = 0;&lt;br /&gt;
&lt;br /&gt;
    public boolean equals(Object object) {&lt;br /&gt;
        if(!(object instanceof Simple))&lt;br /&gt;
            return false;&lt;br /&gt;
        Simple simple = (Simple)object;&lt;br /&gt;
        return (this == simple)  ||&lt;br /&gt;
               ((primitiveInt == simple.primitiveInt) &amp;amp;&amp;amp;&lt;br /&gt;
                ((objectInteger == null)&lt;br /&gt;
                   ? (simple.objectInteger === null)&lt;br /&gt;
                   : objectInteger.equals(simple.objectInteger)));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public int hashCode() {&lt;br /&gt;
        long hashCode = 10002003l + primitiveInt;&lt;br /&gt;
        if(objectInteger != null)&lt;br /&gt;
            hashCode += objectInteger.hashCode();&lt;br /&gt;
        return Long.valueOf(hashCode).intValue();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
One key to note is that the &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; includes both of the member variables since our equals does as well. Since objects that result in equals() resolving to true must have the same &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; it is recommended that the &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; calculation include a bit for each represented variable.&lt;br /&gt;
&lt;br /&gt;
Another thing that is easy to spot is that the &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; calculation uses a long, but returns only an int. This is done simply to reduce overflow errors. It is also obvious that the calculation does not start with zero. It is recommended that each class start with a distinct value. It's unclear exactly why, but it's easy to accommodate. An easy trick for Serializable classes is to use the serialVersionUID as the seed.&lt;br /&gt;
&lt;br /&gt;
It is easy to see that (after being checked for null) the member object is asked for its &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt;, not its value. It happens to be the case that for an Integer the &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; will be its value, but if this pattern is repeated, it will become easy to add any kind of object, including a String, or even a member variable of type Simple.&lt;br /&gt;
&lt;br /&gt;
A word of caution about the case where a class contains a member reference of the same type (a self-made linked-list, for example), is that should that be a self-reference, an infinite loop would be created. a simple check for this will protect the code from that happening; this check would be as simple as adding the following example to the calcuation (assuming our Simple class had a member Simple named simple):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;if(this != simple.simple) hashCode +=  simple.hashCode();&amp;lt;/code&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Finally, the long used in the calculation is truncated to an int for return.&lt;br /&gt;
&lt;br /&gt;
Now, looking at this, it will be the case the same &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; is possible for objects with different but similar values. This is perfectly acceptable, and does not violate any part of the equals-hashCode contract. The following example shows how the above Simple class would have the same &amp;lt;code&amp;gt;hashCode()&amp;lt;/code&amp;gt; for objects that are not evaluated as equal.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Sample a = new Simple();&lt;br /&gt;
Sample b = new Simple();&lt;br /&gt;
a.primitiveInt = 1;&lt;br /&gt;
b.objectInteger = new Integer(1);&lt;br /&gt;
a.equals(b); // This is false!&lt;br /&gt;
a.hashCode() == b.hashCode(); // This is OK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Yes, it is known that this sample won't compile...it's an example... For those that wonder why...the last line is a boolean in the middle of no where...put it in an if statement or assign it to a value to make it compile.&lt;/div&gt;</description>
			<pubDate>Fri, 11 Apr 2008 19:40:24 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Equals_and_HashCode</comments>		</item>
		<item>
			<title>Unnecessary Assignments</title>
			<link>http://www.softwarebyjeff.com/index.php/Unnecessary_Assignments</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Occasionally changing code results in ugly code. Someone goes to some length to do what they believe is a correction, but in fact results in stupid remains, quite often with catastrophic results.&lt;br /&gt;
&lt;br /&gt;
This tidbit was passed on by someone else, but I felt compelled to comment on it.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var = var++;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This code could be C, C++, Java, PERL, PHP, or a variety of other languages. The double-plus operator is, of course, an incrementer. In this case, a post-increment.&lt;br /&gt;
&lt;br /&gt;
At first glance, it may look to the casual observer or the lesser skilled as an increment and redundant assignment. Knowing a little about the order of operations, we know that the assignment (the equals sign) will happen last, so the other operation will happen first. The right half of the equals sign will change the value of var by incrementing by one. Then the equals sign will take that value and assign it to var by itself. Seems simply like a redundant assignment.&lt;br /&gt;
&lt;br /&gt;
The key to remember, though, is the operation is a ''post-increment''. What that means is that the value of var is incremented by one (so 9 becomes 10), but the result of the operation is to return the previous value of var (in the previous example, returns 9). What happens next is that previous value of var is again assigned to var. This very quickly negates the incrementation.&lt;br /&gt;
&lt;br /&gt;
What probably happened is that the developer passing through this saw code similar to this.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var = var + 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
That developer probably thought the double-plus operator would be better suited for the job, which any one who's been working in these languages for a while would agree. The quick-fix simply replaced the &amp;quot;plus one&amp;quot; with &amp;quot;plus plus&amp;quot;  and removed some seemingly extraneous spaces, resulting in the code on which I'm commenting.&lt;br /&gt;
&lt;br /&gt;
What this code-cleaner did wrong was fail to remove the assignment. Since the operator chosen was a post-increment, as mentioned above, the assignment negates the increment, turning the code instead to the following.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var = var;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Or, as allowable pretty much only in C or C++, the code became simply.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
A lesser evil, but still with an unnecessary assignment, would have been to use the pre-increment operator instead.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var = ++var;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This would (likely correctly) increment the value of var, and return the new value. Then the unnecessary assignment would happen and the same value would again be assigned to the variable.&lt;br /&gt;
&lt;br /&gt;
What the code-cleaner undoubtedly intended instead was simply this.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var++;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
In this case, it doesn't matter whether the post- or pre-increment operator is used as the result of the operation is simply ignored. This means the following is equally valid.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
++var;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
So, is the moral to always pre-increment? Absolutely not. There are plenty of cases where the operation order matters greatly. Many more cases where the order does not matter. The key is to be aware of the operator precedence and results of each operator in a chain of events.&lt;/div&gt;</description>
			<pubDate>Mon, 04 Feb 2008 16:43:22 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Unnecessary_Assignments</comments>		</item>
		<item>
			<title>Thoughts on Design Patterns</title>
			<link>http://www.softwarebyjeff.com/index.php/Thoughts_on_Design_Patterns</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In a recent discussion there was a conversational chasm driven by two camps of software developers. The first were whole-hearted, dyed-in-the-wool, patterns-save-the-day evangelists. The other were patterns-have-a-place, get-the-job-done, algorithm-over-fretting-over-pattern-names, self-proclaimed software realists.&lt;br /&gt;
&lt;br /&gt;
For the record, I consider myself part of the second group, 'though perhaps not so strongly as some of the others in my camp during the discussion. I firmly maintain that I can intelligently apply a pattern without knowing its name, and that given a pattern, I can ensure that it is used when creating software.&lt;br /&gt;
&lt;br /&gt;
Design Patterns, to be sure we speak of the same thing, found favor in the mid-1990s after the acceptance of a book of a similar name. The book lays out some good principals to be used when designing object-oriented software. The idea behind the book, since adapted by many other authors, is that using patterns helps convey the ideas behind the different methods of getting things done. It explains the reasons for decisions made while authoring software, and provides mechanisms for recalling and reusing similar ideas for other software projects.&lt;br /&gt;
&lt;br /&gt;
The use of patterns happens whether you know the names of them or not. If you've ever created an Interface in Java, it can be argued that you've applied a Facade Pattern. If you've ever used Java Beans it can also be argued that you've used a Visitor Pattern. If you've ever written a switch statement, you could say that you've used a Controller Pattern.&lt;br /&gt;
&lt;br /&gt;
Now, the idea that you must use patterns in designing the software smacks of restrictions should one not be completely familiar with all of the options. To say up front that one must use a Facade seems counter-productive, as it may not make sense to create an interface should the design be simple and something not expanded. The corollary to that is that if the design is extending existing functionality, it could behoove the developers to refactor the existing work to use a Facade and introduce an interface to allow the new items to reuse existing code.&lt;br /&gt;
&lt;br /&gt;
Similarly, a Visitor Pattern could be argued to be in application wherever a data structure is passed with the understanding that its data elements will be returned with potential modification. Is it necessary to declare that you're using such a pattern when in fact you're simply passing a variable by reference? I would suggest that it is not.&lt;br /&gt;
&lt;br /&gt;
Some of the more complex patterns may make sense to declare in design. Preparing a Factory Pattern, for example, provides all manner of control of the instantiation of objects. Perhaps there's a pool or synchronization behind the Factory that allows something robust or secure. Factories are good things, and are frequently used by libraries and developers starting from &amp;quot;scratch.&amp;quot; Surely, though, if one were to make a method that returned an instance of an object, they'd be using a Factory even if it hadn't been declared such.&lt;br /&gt;
&lt;br /&gt;
Do we do these things all of the time? Absolutely. &lt;br /&gt;
&lt;br /&gt;
Are we using patterns and not knowing it? Quite possibly.&lt;br /&gt;
&lt;br /&gt;
Does that make us lesser developers 'cause we're not thinking of some defined name before we start to do some logical work? That's just foolish.&lt;br /&gt;
&lt;br /&gt;
Does it make someone else better 'cause they'll start out by proclaiming a pattern to use before they finish their design? Arguably, and I argue &amp;quot;not at all.&amp;quot;&lt;/div&gt;</description>
			<pubDate>Mon, 21 Jan 2008 20:42:52 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Thoughts_on_Design_Patterns</comments>		</item>
		<item>
			<title>Consulting/QuickLetter</title>
			<link>http://www.softwarebyjeff.com/index.php/Consulting/QuickLetter</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;YOUR NAME [if possible, print this on letterhead]&lt;br /&gt;
&amp;lt;br/&amp;gt;YOUR ADDRESS&lt;br /&gt;
&amp;lt;br/&amp;gt;CITY, STATE, ZIP&lt;br /&gt;
&lt;br /&gt;
DATE&lt;br /&gt;
&lt;br /&gt;
CONTACT NAME&lt;br /&gt;
&amp;lt;br/&amp;gt;TITLE&lt;br /&gt;
&amp;lt;br/&amp;gt;COMPANY&lt;br /&gt;
&amp;lt;br/&amp;gt;ADDRESS&lt;br /&gt;
&amp;lt;br/&amp;gt;CITY, STATE, ZIP&lt;br /&gt;
&lt;br /&gt;
Dear CONTACT NAME: &lt;br /&gt;
&lt;br /&gt;
Thank you for the opportunity to provide my contracting/consulting [choose one] services to COMPANY. This letter summarizes our conversation from DATE regarding this project and will serve as the agreement between us. &lt;br /&gt;
&lt;br /&gt;
[Use one of the following]&lt;br /&gt;
&lt;br /&gt;
I agree to perform the following services for COMPANY: [Summarize the project here as specifically as possible, stating the nature of the project and any conditions or results that you and the client have discussed.] &lt;br /&gt;
&lt;br /&gt;
OR &lt;br /&gt;
&lt;br /&gt;
I will perform services for COMPANY as described in the exhibit attached to this letter.&lt;br /&gt;
&lt;br /&gt;
These services will be completed on or before DATE.&lt;br /&gt;
&lt;br /&gt;
In consideration for my performance of these services, you agree to pay me [choose one] at the rate of $XX.XX per hour OR a fixed fee of $XX.XX. &lt;br /&gt;
&lt;br /&gt;
[If being paid in two or more parts on a fixed-fee basis, modify the following:]&lt;br /&gt;
&lt;br /&gt;
I will be paid in two installments. The first installment will be $XX.XX and is due to me on DATE. After I complete the project, I will submit an invoice for the remaining amount of $XX.XX. This amount will be due within HOW MANY business days after the date on the invoice. &lt;br /&gt;
&lt;br /&gt;
[If you are charging by the hour, use this and modify, especially if the client agreed to pay you some money up front:]&lt;br /&gt;
&lt;br /&gt;
I will be paid the amount of $XX.XX on DATE [this should be before you begin the project], after which date I will begin work on the project. Upon completion of the project, I will submit an invoice that totals my hours on this project, multiplies them by my hourly rate of $XX.XX, and subtracts the amount advanced on DATE from the total. The total remaining amount will be due within HOW MANY business days after the date on the invoice.&lt;br /&gt;
&lt;br /&gt;
[Use the following if you anticipate submitting invoices throughout the project, although in that case you should consider a longer contract:]&lt;br /&gt;
&lt;br /&gt;
I will submit an invoice for my services every HOW MANY business days. The specified amount will be due within HOW MANY business days after the date on each invoice.&lt;br /&gt;
&lt;br /&gt;
If this agreement accurately summarizes the terms of our earlier conversation, please sign below on this and the attached copy. Retain one copy for your records and return one to me.&lt;br /&gt;
&lt;br /&gt;
Best regards,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
YOUR NAME&lt;br /&gt;
&lt;br /&gt;
Agreed to by: &lt;br /&gt;
&amp;lt;br/&amp;gt;COMPANY NAME&lt;br /&gt;
&amp;lt;br/&amp;gt;BY:	[Signature]&lt;br /&gt;
&amp;lt;br/&amp;gt;CONTACT NAME:&lt;br /&gt;
&amp;lt;br/&amp;gt;CLIENT TITLE: &lt;br /&gt;
&amp;lt;br/&amp;gt;DATE:&lt;/div&gt;</description>
			<pubDate>Wed, 19 Apr 2006 16:25:22 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Consulting/QuickLetter</comments>		</item>
		<item>
			<title>Sendmail milter written in PERL</title>
			<link>http://www.softwarebyjeff.com/index.php/Sendmail_milter_written_in_PERL</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://sendmail.org Sendmail] has a tremendously helpful capability to add mail filter programs to it. Mail filters can be used for a variety of things including investigation to reduce SPAM or UBE, anti-virus checking, and the one we needed, custom mail handling. There are some other full-featured programs that can run as mail filters, such as [http://procmail.org procmail], but they've got their own specific uses. &lt;br /&gt;
&lt;br /&gt;
We had a specific need to take mail as it was received and parse it for insertion into databases. We sought first a way to do this through Java, but the one sendmail fitler program we found, [http://jilter.sf.net jilter], was not so well-documented as to allow immediate use. We'll probably follow up on this route, because if we can utilize a service we might reduce invocation overhead of our current PERL solution. More on that in a moment.&lt;br /&gt;
&lt;br /&gt;
No other site had a quick and dirty discussion of how to make a simple milter. There's [http://www.milter.org milter.org], which seems to be related directly to sendmail, that provides a C API to write milters with, or [http://sendmail-milter.sourceforge.net/ Sendmail::Milter] which is a PERL wrapper for the milter.org API. Looking at these made it seem more complex than it should need to be, at least for our limited needs.&lt;br /&gt;
&lt;br /&gt;
No where could we find a simple discussion of what the milter environment needs to be, how to configure the sendmail server to use the milter, or what the milter program needs to do to handle the mail including what format the mail arrives in.&lt;br /&gt;
&lt;br /&gt;
After some thought, we realized we had a PERL milter in place. Our mail server already handles a number of mail lists using the [http://gnu.org/software/mailman mailman] software. This software comes with a mail filter that gets installed to handle mail sent to the lists' domains. We took a peek at the milter and its configuration and in the interest of open source, seek to divulge its inner operations here. Not the operations of the mailman milter, exactly, but of a PERL milter derived from the mailman milter.&lt;br /&gt;
&lt;br /&gt;
== Writing the Milter ==&lt;br /&gt;
&lt;br /&gt;
After reviewing the mailman milter, the needs of the script seemed almost laugably simple. Here's a stripped-down example of the milter.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/perl&lt;br /&gt;
# Of course, use your correct PERL installation&lt;br /&gt;
&lt;br /&gt;
# Space to remember the parameters&lt;br /&gt;
$sender = undef;&lt;br /&gt;
$server = undef;&lt;br /&gt;
@to = ();&lt;br /&gt;
# Space for the lines of the e-mail headers and body&lt;br /&gt;
@body = ();&lt;br /&gt;
&lt;br /&gt;
# Read the arguments from the server&lt;br /&gt;
#   [-r sender] server to [to...]&lt;br /&gt;
while ($#ARGV &amp;gt;= 0) {&lt;br /&gt;
	if ($ARGV[0] eq &amp;quot;-r&amp;quot;) {&lt;br /&gt;
		$sender = $ARGV[1];&lt;br /&gt;
		shift @ARGV;&lt;br /&gt;
	} elsif (!defined($server)) {&lt;br /&gt;
		$server = $ARGV[0];&lt;br /&gt;
	} else {&lt;br /&gt;
		push(@to, $ARGV[0]);&lt;br /&gt;
	}&lt;br /&gt;
	shift @ARGV;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Read the message body&lt;br /&gt;
while ($line = &amp;lt;STDIN&amp;gt;) {&lt;br /&gt;
	chomp($line);&lt;br /&gt;
	push(@body, $line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Do whatever for each recepient&lt;br /&gt;
for $addressee (@to) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the script, mark it executable, and provide it access permissions in accordance with your security needs. Poof, that's it.&lt;br /&gt;
&lt;br /&gt;
Fundamentally, the script takes a small list of parameters, the least of which is the hostname of the system and a list of recepients. Optionally, a &amp;quot;-r&amp;quot; parameter may be followed by the sender's e-mail address. When sendmail calls the milter, with the settings as used by our borrowing the mailman configuration, the &amp;quot;-r sender&amp;quot; is always sent as the first two parameters, followed by the server name, followed by the list of recepients. &lt;br /&gt;
&lt;br /&gt;
In our experimentation, however, it seems that each recepient receives a separate call to the script, even if multiple recepients to the same domain are included in the same e-mail message. This may be a configuration issue with our sendmail server, but the script, as modified, should handle multiple recepient addresses. &lt;br /&gt;
&lt;br /&gt;
Note the recepients are the part of the address before the &amp;quot;@&amp;quot; symbol, since the part after the &amp;quot;@&amp;quot; symbol is passed as the server parameter.&lt;br /&gt;
&lt;br /&gt;
After the mail message is streamed in from standard input, we iterate through the list of recepients, doing whatever we need to in that last ''for(){}'' loop. &lt;br /&gt;
&lt;br /&gt;
As written, the script will take all of the messages and discard them, as nothing is done with them. This is a very expensive way to dump messages, so we hope something more essential is done in the final loop.&lt;br /&gt;
&lt;br /&gt;
=== Mail Format ===&lt;br /&gt;
&lt;br /&gt;
The body of the message is read from standard input. The format of the message, we found through experimentation, is exactly that of the mail files written by sendmail when a message is passed by all filters and is added to a user (or alias) mailbox. That format is as follows:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
From sender@address DATE&lt;br /&gt;
header: text&lt;br /&gt;
&lt;br /&gt;
BODY&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Where the first line is a specific format that starts with the literal word &amp;quot;From&amp;quot; folowed by the fully-qualified e-mail address of the sender, and finally the date, formatted such as ''Mon Jan  1 00:00:00 1900'' (note the date is two spaces, space pre-padded, so that our &amp;quot;Jan 1&amp;quot; actually has two spaces between the &amp;quot;n&amp;quot; and &amp;quot;1,&amp;quot; which probably doesn't translate well on the web page).&lt;br /&gt;
&lt;br /&gt;
The ''header:'' lines include things such as ''Date:'', ''From:'', ''To:'', and ''Subject:'' with their appropriately formatted information following the colon. This formatting is textual, and is really up to the sender, although there are some specifics outlined in the [http://www.ietf.org/rfc/rfc0822.txt RFC 822], that we'll not cover here.&lt;br /&gt;
&lt;br /&gt;
The headers may be multi-line, each following line belonging to the previous ''header:'' until either the next ''header:'' line or the blank line signalling the end of the header block is encountered.&lt;br /&gt;
&lt;br /&gt;
There is, as mentioned, a blank line between the headers and the body of the message.&lt;br /&gt;
&lt;br /&gt;
The ''BODY'' is a plain-text representation of the message. The message may be anything sent in plain-text, including HTML pages, or encoded binaries.&lt;br /&gt;
&lt;br /&gt;
=== Separate Header and Body Processing ===&lt;br /&gt;
&lt;br /&gt;
The processing as we have it in our simple example doesn't consider the headers separately from the body. That's simple enough to do with something like the following:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
$bodyReached = undef;&lt;br /&gt;
$date = &amp;quot;&amp;quot;; &lt;br /&gt;
$to = &amp;quot;&amp;quot;;&lt;br /&gt;
$cc =&amp;quot;&amp;quot;;&lt;br /&gt;
$subject =&amp;quot;&amp;quot;;&lt;br /&gt;
while ($line = &amp;lt;STDIN&amp;gt;) {&lt;br /&gt;
	chomp($line);&lt;br /&gt;
	if( !defined($bodyReached) ){&lt;br /&gt;
		if( length($line) &amp;gt; 0 ){&lt;br /&gt;
			# only the headers we care about&lt;br /&gt;
			if( $line =~ m/^(Date:)/i ){&lt;br /&gt;
				$line =~ s/Date:*\s//i;&lt;br /&gt;
				$date = $line;&lt;br /&gt;
			} elsif( $line =~ m/^(To:)/i ){&lt;br /&gt;
				$line =~ s/To:*\s//i;&lt;br /&gt;
				$to = $line;&lt;br /&gt;
			} elsif( $line =~ m/^(Cc:)/i ){&lt;br /&gt;
				$line =~ s/Cc:*\s//i;&lt;br /&gt;
				$cc = $line;&lt;br /&gt;
			} elsif( $line =~ m/^(Subject:)/i ){&lt;br /&gt;
				$line =~ s/Subject:*\s//i;&lt;br /&gt;
				$subject = $line;&lt;br /&gt;
			} &lt;br /&gt;
		} else {&lt;br /&gt;
			$bodyReached = $line;&lt;br /&gt;
		}&lt;br /&gt;
	} else {&lt;br /&gt;
		push(@body, $line);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Replace the simple ''while(){}'' we have in the simple example, and now the loop will pull out the headers for Date, To, CC, Subject, and Message-ID, ignoring the other headers. I'll leave it to the reader to refine the regexp used, or the headers grabbed to fit your application.&lt;br /&gt;
&lt;br /&gt;
== Configuring Sendmail ==&lt;br /&gt;
&lt;br /&gt;
The sendmail configuration took a little deeper digestion. Sadly, there is no easy to find discussion on configuring sendmail to use a milter. We re-configured our sendmail step-by-step from the Mailman installation documentation to find the bits necessary.&lt;br /&gt;
&lt;br /&gt;
This discussion will outline the steps necessary to make the filter available for use in sendmail. The simple discussion outlines how to send all mail for a host or domain to the filter. The filter will handle the mail, and sendmail will forget about it entirely when the filter is done. If your filter doesn't do something with the mail, it'll be lost. Well, with our discussion, anyway. This met our needs, so our investigation left when this was complete. We haven't taken the time to investigate any signalling to sendmail to let it continue processing the mail.&lt;br /&gt;
&lt;br /&gt;
Simply, configure DNS to have an MX record pointing to the server hosting this sendmail installation. Configure the sendmail to have the mail filter script we created above enabled, and add a couple lines to configure the DNS managed host name to use the filter.&lt;br /&gt;
&lt;br /&gt;
=== M4 Configuration ===&lt;br /&gt;
&lt;br /&gt;
The [[sendmail]] installed needs to have the milter handling enabled. Our instructions include this. The configuration file needs to have a couple definitions in it to enable each filter.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
FEATURE(`mailertable',`dbm /etc/mail/mailertable')dnl&lt;br /&gt;
Mmilter, P=/etc/mail/script, F=rDFMhlqSu, U=user:group, S=EnvFromL, R=EnvToL/HdrToL, A=script $h $u&lt;br /&gt;
dnl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The ''mailertable'' line enables the sendmail mailer table, defining the database file it will use to store the hash of items. More on that in a moment.&lt;br /&gt;
&lt;br /&gt;
The ''M'' line contains the definition of the mail filter that we'll use when associating e-mail to our milter. It deserves a little bit of discussion, as it, too was hard to digest. Taking the line apart, we're going to pay attention to the parts in bold below:&lt;br /&gt;
&lt;br /&gt;
M'''milter''', P='''/etc/mail/script''', F=rDFMhlqSu, U='''user:group''', S=EnvFromL, R=EnvToL/HdrToL, A='''script''' $h $u&lt;br /&gt;
&lt;br /&gt;
The '''milter''' butted up against the defining ''M'' is the name you wish to associate with the mail filter. The name ''milter'' is actually a bad one, used here only for our abstraction. A better name would be descriptive of the purpose of the milter, perhaps even, as we do, named after the domain using the filter.&lt;br /&gt;
&lt;br /&gt;
The ''P'' parameter defines the program to be run, so '''/etc/mail/script''' is the actual path and script name of the script modeled after our example PERL script. Again, ''script'' is a bad name, so choose more appropriately. Note that this must be &amp;quot;approved&amp;quot; by your installation of sendmail, which is usually simply done by adding a symlink to the script in the /var/adm/sm.bin folder as in '''ln -s /etc/mail/script /var/adm/sm.bin/script''' as root.&lt;br /&gt;
&lt;br /&gt;
The ''U'' parameter specifies the user and group '''user:group''' as which the script will run. Whatever permissions that user has is what the script will be bound to. By default, it will use the user/group that sendmail runs under, so if you want to dump the U parameter, remember that. The user and group needs to exist, or the call will fail.&lt;br /&gt;
&lt;br /&gt;
The ''A'' parameter defines the argument vector passed to the script. We found that '''script''' should match the script name used in the ''P''&lt;br /&gt;
&lt;br /&gt;
The ''F'', ''S'', and ''R'' parameters we left as Mailman gave them to us. The flags identified in the F parameters, as well as the other parameters above, are discussed in the sendmail [http://www.sendmail.org/~ca/email/doc8.10/op-sh-5.html#sh-5.4 THE WHOLE SCOOP ON THE CONFIGURATION FILE] document, but we found it lacking in some respects. For example, while the ''S'' and ''R'' parameters are discussed, we could find no discussion of the parameters we were given with the Mailman script, although they seemed to work. Of course, we didn't experiment by changing the flags or parameters other than those we've discussed here.&lt;br /&gt;
&lt;br /&gt;
Rebuild the sendmail.cf file. Starting or restarting sendmail will enable the milter for use.&lt;br /&gt;
&lt;br /&gt;
=== Mailertable Configuration ===&lt;br /&gt;
&lt;br /&gt;
Create or edit the /etc/mail/mailertable database (as configured in the FEATURE discussed above) to include the directive to use your milter.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
host	milter:server&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Put the host name to which mail is delivered, for which you wish your filter to be used. This should be the same hostname that is expected in the e-mail address, after the ''@'' symbol. This should also be configured in the DNS as an MX entry.&lt;br /&gt;
&lt;br /&gt;
The ''milter'' should be the same name as used above, abutted to the ''M'' parameter in the M4 configuration file.&lt;br /&gt;
&lt;br /&gt;
The ''server'' parameter is what ends up delivered to the script in the first position of the parameter list. If our configuration were as shown above, the word &amp;quot;server&amp;quot; actually would be sent as the third parameter to our script. That is &amp;quot;-r from@address ''server'' recipient&amp;quot; would be sent. Use this as a key for your script, if you'd like, or whatever makes sense. In our configuration, we used the actual DNS host name (e.g., mail.domain.tld) for the ''host'' part of the line, and the domain name (e.g., domain.tld) as the ''server'' part of the line.&lt;br /&gt;
&lt;br /&gt;
=== Relay Domains Configuration ===&lt;br /&gt;
&lt;br /&gt;
Create or edit the /etc/mail/relaydomains file to include the domains for which your filter will be used. Very simply add a line with the domain (host) for which your filter will be used.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
host&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This tells sendmail that it will be allowed to relay mail for the domain. Ordinarily this may be dangerous, as you could errantly enable open relaying through your system. Since we're dumping all of the mail to the hosts listed into our milter, there's not any danger, unless your milter just turns around and sends the mail out somewhere.&lt;/div&gt;</description>
			<pubDate>Mon, 31 Oct 2005 19:52:06 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Sendmail_milter_written_in_PERL</comments>		</item>
		<item>
			<title>Sendmail Mail Server</title>
			<link>http://www.softwarebyjeff.com/index.php/Sendmail_Mail_Server</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Building and configuring [[sendmail]] from scratch&lt;br /&gt;
&lt;br /&gt;
Creating and using a simple [[sendmail milter written in PERL]]&lt;br /&gt;
&lt;br /&gt;
[[Securing sendmail]] using SPF and Domain Keys&lt;/div&gt;</description>
			<pubDate>Mon, 31 Oct 2005 18:02:39 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Sendmail_Mail_Server</comments>		</item>
		<item>
			<title>Hosting Web-site from Home</title>
			<link>http://www.softwarebyjeff.com/index.php/Hosting_Web-site_from_Home</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Firewall ==&lt;br /&gt;
&lt;br /&gt;
First, it's critical that you can reach your intended PC from the Internet on TCP port 80. If you've got no firewall, then go buy one and configure it... If you've got a firewall, you need to be able to set up port forwarding to your PC. Usually you can do this by hitting your firewall from the LAN side with a web browser, log in, find the port forwarding (sometimes called virtual servers), and set port 80 to forward to your PCs IP, which I hope you can get. The PC should either have a reserved DHCP address, or a static one. Static is easier, especially if your firewall is giving out the DHCP since they don't usually allow reservations.&lt;br /&gt;
&lt;br /&gt;
== Web Server ==&lt;br /&gt;
&lt;br /&gt;
Then, you need a web server on your machine. I recommend Apache on LINUX or Solaris almost always. If you're going to do nothing but Java stuff (e.g., servlet, JSP, Struts) and static pages and files, then go ahead and use just Tomcat. If you're stuck on Windows on this PC, you can use the Windows ports of those products, or Microsoft's IIS. IIS works, even the personal version included with XP. If you need specific help getting your web server set up, let me know.&lt;br /&gt;
&lt;br /&gt;
Make sure the server is ready to receive requests and has at least a sample web page to serve. Use something simple if you don't have one ready.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;&amp;lt;TITLE&amp;gt;COMING SOON&amp;lt;/TITLE&amp;gt;&amp;lt;/HEAD&amp;gt;&amp;lt;BODY&amp;gt;&lt;br /&gt;
&amp;lt;TABLE WIDTH=&amp;quot;100%&amp;quot; HEIGHT=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;TR VALIGN=&amp;quot;CENTER&amp;quot; ALIGN=&amp;quot;CENTER&amp;quot;&amp;gt;&amp;lt;TD&amp;gt;COMING SOON&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Domain Registration ==&lt;br /&gt;
&lt;br /&gt;
Visit [http://itsyourdomain.com ItsYourDomian.com] or some other registrar and register your domain name. If you can't get a domain name, then you need to either cooperate with someone else who has a domain (no, you cannot use this one) or just forget about this.&lt;br /&gt;
&lt;br /&gt;
The registrar will ask for some fundamental information, like your name, address, phone, and billing information. More often than not they'll require a credit card to start, and many offer other than credit card payments for renewal.&lt;br /&gt;
&lt;br /&gt;
When you've done this, you'll probably receive an &amp;quot;under construction&amp;quot; or &amp;quot;domain reserved&amp;quot; kind of page automatically, one that offers more web users to get their own domain name. Don't worry about anything they say about making web sites easy; you're going to have your own server, and whatever you set up with them will only get in the way.&lt;br /&gt;
&lt;br /&gt;
== DNS ==&lt;br /&gt;
&lt;br /&gt;
You need to be able to find your machine by way of the Internet DNS. This is where the trickery comes in.&lt;br /&gt;
&lt;br /&gt;
Visit [http://zoneedit.com Zone Edit]. Sign up and create an account. It's free for the first five domains and/or until requests for your any domains break some pretty high threshold; like 200,000 requests a month. Then it's $11 per month for a &amp;quot;credit&amp;quot; to cover more bandwidth per domain. You don't need to pay in any given month unless you go over the limit. Don't use an e-mail address from the domain when you create your account, of course, or you'll run a risk of never getting your e-mail. Use [http://mail.yahoo.com Yahoo!] or [http://gmail.com GMail] or something; if you need a GMail invite, let me know.&lt;br /&gt;
&lt;br /&gt;
You should have registered your domain name, so add that to your ZoneEdit account. They'll give you a pair of name servers that you'll have to put in your domain registration; update your registration. As soon as you do, the next time you visit a ZoneEdit page it'll let you know if you've done it right--do it right.&lt;br /&gt;
&lt;br /&gt;
== ZoneEdit Edit ==&lt;br /&gt;
&lt;br /&gt;
Now make some quick edits to your ZoneEdit account. Add an address to your domain; I recommend putting your domain name, without a servername, with the IP 0.0.0.0. Make a CNAME/Alias for www to the same server name. It'll balk, wanting instead to make two addresses with the same IP address, but if you do do it that way you'll have to maintain two addresses if your IP changes.&lt;br /&gt;
&lt;br /&gt;
Your initial domain should look something like:&lt;br /&gt;
&lt;br /&gt;
    IP Addresses: domain.tld  = 0.0.0.0&lt;br /&gt;
    Aliases: www.domain.tld =&amp;gt; domain.tld&lt;br /&gt;
    Mail Forwarding: all mail =&amp;gt; your@other.addy&lt;br /&gt;
&lt;br /&gt;
By doing the forward as a CNAME and addressing the raw omain name you easily end up with '''&amp;lt;nowiki&amp;gt;http://domain&amp;lt;/nowiki&amp;gt;''' and '''&amp;lt;nowiki&amp;gt;http://www.domain&amp;lt;/nowiki&amp;gt;''' always pointing to the same place. The way ZoneEdit does it, where they both have the same IP requires you to later do the dynamic update twice; once with and once without the &amp;quot;www.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== E-mail Notes ==&lt;br /&gt;
&lt;br /&gt;
Make sure the e-mail forwarding points to the correct place to get your e-mail for the domain; it'll default to whatever you used to create the account. You can simply create a GMail account for each domain and set up that as the domain's mail forward. If anything happens to your domain, you can still get to the e-mail, which you may need to straighten out your problem. Then you get web-mail for your domain instantly, too.&lt;br /&gt;
&lt;br /&gt;
You can reply from your domain using trickery with your e-mail client, by setting up a from address using the service or your client. With GMail, it's pretty easy using their on-line forms, and then you get and send mail from someplace where someone else worries about SPAM. Plus they have filtering so you can sort your mail by sender or recipient address or other things...I digress.&lt;br /&gt;
&lt;br /&gt;
=== Mail Server ===&lt;br /&gt;
&lt;br /&gt;
Of course, instead of using ZoneEdit's mail forwarding, you can run a mail server, but I don't recommend that for anyone without a strong sense of commitment; my server deals in tens of thousands of SPAM messages a month for only a few thousand delivered e-mails.&lt;br /&gt;
&lt;br /&gt;
If you do have a mail server, make sure that port 25 is open on your firewall, that your server is configured to handle mail for your domain, and that you have at least anti-relay and anti-spam in place, if not anti-virus, too. If you're going to POP into your server, open port 110, too.&lt;br /&gt;
&lt;br /&gt;
== DNS Updates ==&lt;br /&gt;
&lt;br /&gt;
TOnce ZoneEdit is configured, use this URL in a web browser from that PC:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
http://USERNAME:PASSWORD@dynamic.zoneedit.com/auth/dynamic.html?host=DOMAINNAME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
You would replace the upper-case bits with whatever you worked out with ZoneEdit. In the web browser you won't see anything, but if you look at the page source you'll see a one-line status. Usually it says &amp;quot;no changes required,&amp;quot; but the first time it'll say something like &amp;quot;updated,&amp;quot; and if your DHCP changes your IP, that'll update, too.&lt;br /&gt;
&lt;br /&gt;
Check the ZoneEdit page and you should see the 0.0.0.0 for your domain name has changed to your real IP.&lt;br /&gt;
&lt;br /&gt;
Note if you want to do this to more than one PC, you need to replace '''DOMAINNAME''' with '''HOST.DOMAINNAME''', or subsequent requests will replace previous requests. You also don't want to use '''www.DOMAINNAME''' as you've got a CNAME in that place.&lt;br /&gt;
&lt;br /&gt;
== Confirm ==&lt;br /&gt;
&lt;br /&gt;
You should now be able to get to your machine by the domain name. Open your favorite browser and put your domain name in the address bar. You should reach your web server (if it's running) or receive an error code.&lt;br /&gt;
&lt;br /&gt;
If you've done that fairly recently (say within a day), then your machine may have the domain name cached and you'll see the old one, not the new one. Wait a while, purge your cache, or try from a different machine.&lt;br /&gt;
&lt;br /&gt;
You should be able to get to your machine by your firewall IP, if not by name. If you can't do that either, then the web server's not up, the firewall's configured wrong, or your ISP is blocking the traffic. If your ISP blocks the traffic, then you can't do anything about it. You might try to find a port they don't block, but no one surfs by '''&amp;lt;nowiki&amp;gt;http://server:port/&amp;lt;/nowiki&amp;gt;''' by default, so your URL gets goofy from the get-go.&lt;br /&gt;
&lt;br /&gt;
== Regular DNS Updates ==&lt;br /&gt;
&lt;br /&gt;
I run Solaris, so I run a cron job every two hours to make sure that DHCP changes to my firewall IP address don't loose traffic. Usually the same address from DHCP is maintained as long as the machine re-claims it before the lease expires; and they try half-way. For example, on my server I use this script:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
/usr/local/bin/wget --http-user=USERNAME --http-passwd=PASSWORD \&lt;br /&gt;
-O /var/log/zoneedit.log \&lt;br /&gt;
http://dynamic.zoneedit.com/auth/dynamic.html?host=DOMAINNAME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the script, the file /var/log/zoneedit.log has the same one-line status as the browser displays. Update too often and ZoneEdit complains, but bi-hourly seems sufficient.&lt;br /&gt;
&lt;br /&gt;
If you're on Windows you can do an AT command to schedule it, but unlike cron you have to make a new job for every hour of the day. Plus you have to download the Windows version of [http://www.gnu.org/software/wget/wget.html wget] or figure out a way to script Internet Explorer (or write a little Perl or Java app to do the query...).&lt;/div&gt;</description>
			<pubDate>Mon, 03 Oct 2005 22:04:31 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Hosting_Web-site_from_Home</comments>		</item>
		<item>
			<title>Making Struts Projects in IDEs</title>
			<link>http://www.softwarebyjeff.com/index.php/Making_Struts_Projects_in_IDEs</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]]&lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
Here we'll discuss doing the Struts creation from scratch in our two favorite open-source Java integrated development environments.&lt;br /&gt;
&lt;br /&gt;
''More information coming''&lt;br /&gt;
&lt;br /&gt;
== NetBeans ==&lt;br /&gt;
&lt;br /&gt;
NetBeans sweetly comes with a Tomcat installation built-in, and it loves to create web applications for use on Tomcat. The deployment of these applications is built correctly in a container that is transportable, and can be done largely independently of the Tomcat server (e.g., so you can move it to other application servers). Other application servers can also be used, but for our simple discussion, we'll just use the built-in Tomcat.&lt;br /&gt;
&lt;br /&gt;
== Eclipse ==&lt;br /&gt;
&lt;br /&gt;
Eclipse has a Web Tools Project that integrates much of the development and deployment with servlet and application servers. It has built-in deployment configurations for versions of Tomcat, JBoss, WebSphere, and others. Eclipse does not include an installation of any of these application servers, instead relying on a local installation to be performed independently.&lt;br /&gt;
&lt;br /&gt;
=== Eclipse WTP ===&lt;br /&gt;
&lt;br /&gt;
The good people at Eclipse have released a set of tools called the Web Tools Platform that includes all kinds of goodies for integrating web development into the IDE without the need for finding third-party solutions. Don't get me wrong; third-party solutions are great, but integrated packages are even better.&lt;br /&gt;
&lt;br /&gt;
For Windows and LINUX there are downloadable packages that include all of Eclipse and the WTP tools in one easy installation file. For the Mac, or for an already installed Eclipse, download the updates using Eclipse's Software Update tool (in the Help menu).&lt;br /&gt;
&lt;br /&gt;
=== Install Tomcat ===&lt;br /&gt;
&lt;br /&gt;
Eclipse requires an external installation of the application server. We're going to discuss using Tomcat, but if your environment uses a different engine, don't worry, the instructions are fundamentally the same. Simply download and unpack the Tomcat core from Apache. No additional configuration is necessary as all of the Tomcat settings that will be affected by your project will remain within the workspace. This is essential as you may have many projects, or set up different configurations of Tomcat within Eclipse, and it won't affect the other configurations. You could even use a previously installed Tomcat that is actually running on the system with its own content; Eclipse is just going to use the binaries and common libraries.&lt;br /&gt;
&lt;br /&gt;
=== Configure Eclipse ===&lt;br /&gt;
&lt;br /&gt;
Once both the WTP and Tomcat are installed, it's time to configure Eclipse to use the Tomcat server. Open Eclipse, and then the Eclipse preferences. Find the Server, and install a new runtime. Select the version of the application server you're using, and provide a path to the server. Finally, tell the server which JRE or JDK to use. It's as easy as that. Sure a step-by-step or picture would be nice, but maybe later.&lt;br /&gt;
&lt;br /&gt;
=== Eclipse Web Project ===&lt;br /&gt;
&lt;br /&gt;
Once configured, setting up a Struts project in Eclipse is pretty straight forward. Start Eclipse. Create a new project by selecting File&amp;gt;&amp;gt;New&amp;gt;&amp;gt;Project. Find the Web group and in that select Dynamic Web Project. Just like other Eclipse projects, select a name and location for the files. Also select (or create if you haven't) the application server installation to use. For now we'll accept the defaults for everything else.&lt;br /&gt;
&lt;br /&gt;
What you should end up with is a project that has Tomcat and the JRE as libraries, and a JavaSource and WebContent directory. Inside the JavaSource directory should be empty, and inside the WebContent directory should be only a WEB-INF directory.&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
Use the previous information to build your struts projects. Copy the Struts library JARs to the WEB-INF/lib directory in your IDE's web source directory. Edit or create the web.xml as described previously. Create the Action and Form classes in the JavaSource directory.&lt;br /&gt;
&lt;br /&gt;
Now start the server as is appropriate for your IDE, and the site should fire up and do whatever it is you wanted it to do!&lt;/div&gt;</description>
			<pubDate>Tue, 27 Sep 2005 19:50:44 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Making_Struts_Projects_in_IDEs</comments>		</item>
		<item>
			<title>Struts Server Deployment</title>
			<link>http://www.softwarebyjeff.com/index.php/Struts_Server_Deployment</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]]&lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
Here we'll discuss how to deploy our stuff to Tomcat.&lt;br /&gt;
&lt;br /&gt;
We've made an assumption when configuring the web server that Tomcat is the servlet container in use. We'll continue that assumption here. If you're using a different servlet container or application server, the onus is on you to alter this configuration to suit your environment.&lt;br /&gt;
&lt;br /&gt;
== Files ==&lt;br /&gt;
&lt;br /&gt;
Assuming access is available directly to the directory of the Tomcat server, installation is easy. In the target directory, copy the JSP files and contents of the WEB-INF, and any other files as necessary to the correct path.&lt;br /&gt;
&lt;br /&gt;
== WAR ==&lt;br /&gt;
&lt;br /&gt;
If the project is tidy, it can be archived in a Web Archive (or WAR) file. Depending on the configuration of the Tomcat server, the presence of a new WAR file may start an automatic deployment, or the deployment may need to be done via the Tomcat management plugins.&lt;br /&gt;
&lt;br /&gt;
''More Details Coming''&lt;/div&gt;</description>
			<pubDate>Tue, 27 Sep 2005 19:49:52 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Struts_Server_Deployment</comments>		</item>
		<item>
			<title>Our Struts Web Pages</title>
			<link>http://www.softwarebyjeff.com/index.php/Our_Struts_Web_Pages</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]]&lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
Even before we completed the Struts classes, we'd know we'd need these JSPs. Whether we develop these before, during, or after we develop the classes depends on our style, the design of the site, and potentially the complexity of the bean. Since our bean is so simple, we've done that first.&lt;br /&gt;
&lt;br /&gt;
== default.jsp ==&lt;br /&gt;
&lt;br /&gt;
Our '''default.jsp''' will give us an example of the page instantiating the bean for us. In our example so far we've got a two-page site, this page will tell us to check a box and hit a button. Little more is necessary to exercise our Action and ActionForm.&lt;br /&gt;
&lt;br /&gt;
Additionally, we'll do this with Struts tags, to show the link between the bean and the JSP form. An alternative to using Struts tags is to write all kinds of JavaScript, but if we were going to do that, we wouldn't be using Struts anyway.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;%@ taglib uri=&amp;quot;http://struts.apache.org/tags-html&amp;quot; prefix=&amp;quot;html&amp;quot; %&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Struts Sample Default&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;html:form action=&amp;quot;/example.do&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html:checkbox property=&amp;quot;checked&amp;quot; /&amp;gt;Check this box&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;html:submit&amp;gt; Click this button&lt;br /&gt;
&amp;lt;/html:form&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
That should do it. What does it do?&lt;br /&gt;
&lt;br /&gt;
The first line defines a [http://struts.apache.org/userGuide/struts-html.html tag library] to use in the preparation of the JSP. The Struts tag libraries give us the ''html'' library that we're using here. The bulk of the page is straight HTML. We'll focus only on the '''html''' tags.&lt;br /&gt;
&lt;br /&gt;
=== html:form ===&lt;br /&gt;
&lt;br /&gt;
The '''html:form''' tag defines the bounds of an HTML form. The '''action''' attribute must correlate to an action defined in our struts-config.xml file. Although we don't need the bean to render the page, the bean used will be the one associated with our '''action''' element in the struts-config.xml file, so we don't really need to do anything else to define the bean we'll be using. Had we associated with an action that had no bean defined, we would have to use one of the other bean methods provided by Struts; since our action defines the ''ExampleForm'' bean, that is the one that will be used. In the page, if we needed to reference the bean directly, we'd use the '''name''' attribute from the struts-config.xml for the action (in this case ''exampleForm'').&lt;br /&gt;
&lt;br /&gt;
=== html:checkbox ===&lt;br /&gt;
&lt;br /&gt;
The '''html:checkbox''' provides the HTML form checkbox that we'll use to alter our bean's property. The '''property''' attribute is the name of the property in the bean with which this control is associated. Usually the properties are String objects, or in some cases arrays of String objects. Collections can be used, but they would typically contain String objects. String objects are just easy to move back and forth. Other types can be used, but be prepared for a little more work in that case.&lt;br /&gt;
&lt;br /&gt;
=== html:submit ===&lt;br /&gt;
&lt;br /&gt;
The '''html:submit''' provides the HTML form submit button that will engage the Struts action. It sends the form POST request (the default, which will be used since we didn't state otherwise), with the information contained in the form. In this case, our intialized bean will be sent with the single value of ''checkbox'' set according to the state of the form's checkbox.&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
&lt;br /&gt;
When the page is initially rendered, the value of the named property, in our case '''checked''' will be used to determine if the box is on. Since we didn't set a value to be used, the default of &amp;quot;on&amp;quot; will be used. If our bean's property contains exactly &amp;quot;on&amp;quot; then the checkbox would be rendered initially checked.&lt;br /&gt;
&lt;br /&gt;
== example.jsp ==&lt;br /&gt;
&lt;br /&gt;
The second JSP page in our example is simply named ''example.jsp''. We'll make it very simple, like the default.jsp, showing only the one check box.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;%@ taglib uri=&amp;quot;http://struts.apache.org/tags-html&amp;quot; prefix=&amp;quot;html&amp;quot; %&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Struts Sample Example&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;html:form action=&amp;quot;/example.do&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html:checkbox property=&amp;quot;checked&amp;quot; /&amp;gt;This better be checked on entry&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;html:submit&amp;gt; Click this button&lt;br /&gt;
&amp;lt;/html:form&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Very similar; really just a little static text has changed.&lt;br /&gt;
&lt;br /&gt;
You'll note the '''action''' of the '''html:form''' is the same as the other. For this example it is because this is the only page in our application that has a bean and action associated with it. What this allows is for the user to leave the box checked or remove the check, and upon hitting ''submit'' they'll return to either this page or default.jsp.&lt;br /&gt;
&lt;br /&gt;
Since the '''action''' defined by '''/example''' specifies an ActionForm, the page's Action is entered with the bean as populated by the calling page. The bean is possibly affected by the Action, although not in our case, and the result that ends up here is used to generate the page. Since our Action sends us to this page only if the bean value is set to &amp;quot;on,&amp;quot; the property '''checked''' will be set to &amp;quot;on,&amp;quot; and the checkbox should be initially rendered as checked; of course you can un-check the box as you use the page.&lt;/div&gt;</description>
			<pubDate>Tue, 27 Sep 2005 19:48:43 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Our_Struts_Web_Pages</comments>		</item>
		<item>
			<title>Our Struts Classes</title>
			<link>http://www.softwarebyjeff.com/index.php/Our_Struts_Classes</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]]&lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
Now that the WEB-INF/web.xml file and the referenced WEB-INF/struts-config.xml file are complete, we need to get to the work of making the classes referenced by the struts-config.xml.&lt;br /&gt;
&lt;br /&gt;
In our simple example, one Action class and one ActionForm class are needed. Since we're working our way from the middle, we'll prepare our ActionForm to have only one property, and our Action will simply verify that the property is set. Later we'll discuss some of the more useful patterns for handling a variety of properties.&lt;br /&gt;
&lt;br /&gt;
== ExampleForm ==&lt;br /&gt;
&lt;br /&gt;
Our simple '''ExampleForm''' class, which must extend from ''org.apache.struts.action.ActionForm'' will have one String property named ''checkbox''. We'll have the appropriate setter and getter. The ActionForm bean is intended to be light-weight, as it is the only data passing from the JSP to the Action class, and we generally want to keep our bandwidth low.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package package;&lt;br /&gt;
class ExampleForm extends org.apache.struts.action.ActionForm {&lt;br /&gt;
  private String checked;&lt;br /&gt;
  public void setChecked(String checked){&lt;br /&gt;
    this.checked = checked;&lt;br /&gt;
  }&lt;br /&gt;
  public String getChecked(){&lt;br /&gt;
    return checked;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Very simple.&lt;br /&gt;
&lt;br /&gt;
Since our declaration said the type of the class was ''package.ExampleForm'', we put the class in that package. Of course, ''package'' is a poor package name, and you should choose something appropriate. The ''ExampleForm.java'' file would go in our Java source directory, in the appropriate structure for whatever you replaced ''package'' with; that is, if you'd said ''com.mydomain.struts.forms'', you'd make a directory ''com'' containing a directory ''mydomain'' containing a directory ''struts'' containing a directory ''forms'' that contains our ''ExampleForm.java'' file, and you'd change the package definition in our struts-config.xml file to use ''com.mydomain.struts.forms.ExampleForm'' and not ''package.ExampleForm''.&lt;br /&gt;
&lt;br /&gt;
== ExampleAction ==&lt;br /&gt;
&lt;br /&gt;
The Action is a little more complex, but not really so complex as to be confusing.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package package;&lt;br /&gt;
class ExampleAction extends org.apache.struts.action.Action {&lt;br /&gt;
&lt;br /&gt;
  public ActionForward execute( ActionMapping mapping,&lt;br /&gt;
                                ActionForm form,&lt;br /&gt;
                                HttpServletRequest request, &lt;br /&gt;
                                HttpServletResponse response) &lt;br /&gt;
                       throws Exception {&lt;br /&gt;
&lt;br /&gt;
    ExampleForm exampleForm = (ExampleForm)form;&lt;br /&gt;
    if( (form == null) || !&amp;quot;on&amp;quot;.equalsIgnoreCase(form.getChecked()) ){&lt;br /&gt;
        return mapping.findForward(&amp;quot;failure&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return mapping.findForward(&amp;quot;success&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Again, the package name ''package'' is incorrect, and you should change that to make sense.&lt;br /&gt;
&lt;br /&gt;
So what does this class do for us?&lt;br /&gt;
&lt;br /&gt;
Our default ''execute()'' method is called when Struts executes the ''action'' we've defined with the name ''/example'', so for URLs like ''http://server/path/example.do'' we'll come into this class.&lt;br /&gt;
&lt;br /&gt;
Since our action defines a bean (using the ''name'' and ''type'' attributes of the ''action''), we know that the form parameter we're passed should be our ExampleForm. We simply cast the passed value to our expected type and move on.&lt;br /&gt;
&lt;br /&gt;
Very simply, the method verifies that the bean's ''checked'' value equals something like &amp;quot;on,&amp;quot; while disregarding case. If the form or value is null or something other than &amp;quot;on,&amp;quot; we find the ''forward'' defined with the name ''failure'' and pass our bean and work to the defined target. If the form is not null, the value not null, and the value equals &amp;quot;on&amp;quot; in some form, we find the ''forward'' defined with the name ''success'' and pass that on as our target.&lt;br /&gt;
&lt;br /&gt;
Note that we're in the center of our bean's life-cycle here. The bean should have been defined by the JSP initiating the action. We can use the bean in any legitimate way; we can review, create, alter, and remove values as we see fit. Our modified bean (from the ''form'' parameter reference) then will be passed on to our target. Since we've got an ultra-simple bean, we're not doing that now. We'll think of a better example later.&lt;br /&gt;
&lt;br /&gt;
That's it. We've now completed the server-side configuration for our Struts example.&lt;/div&gt;</description>
			<pubDate>Tue, 27 Sep 2005 19:45:51 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Our_Struts_Classes</comments>		</item>
		<item>
			<title>Struts Webserver Configuration</title>
			<link>http://www.softwarebyjeff.com/index.php/Struts_Webserver_Configuration</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]]&lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
Not meant to be a comprehensive how-to for integrating Java or Struts into a web server, but instead a brief discussion of what the Java Servlet or Application server expects, this section will introduce a handful of configuration files necessary to get your web server to serve Struts.&lt;br /&gt;
&lt;br /&gt;
Struts is entirely Java. The classes used by Struts and written by you need to be Java, well, initially. The server you use needs to serve Java. For ease, we'll assume you've got a Tomcat server set up. You may have this as part of a JBoss installation, or even as a back-end for an Apache web server installed. You might torture yourself and integrate this with Microsoft's IIS server, or you might disregard the suggestion and use something like Sun's application server or even a commercial server like IBM's WebSphere. Fundamentally, the configuration is the same to get our application served.&lt;br /&gt;
&lt;br /&gt;
At the root of our application we need to have a directory structure in place. This is defined by the Servlet API, and is what you'll find wherever you get a WAR or have to do this for other people.&lt;br /&gt;
&lt;br /&gt;
== WEB-INF ==&lt;br /&gt;
&lt;br /&gt;
The root of your application contains a directory named WEB-INF. OK, that's pretty much it. The WEB-INF directory contains the things your application needs, like library archive, resource, and configuration files. When your server is asked for a file, it starts looking up the directory tree until it finds a directory with the WEB-INF directory in it. It then uses the settings and objects within that directory to control the application. Curiously, this means you can have nested applications. This is frowned upon, so be careful to avoid this situation.&lt;br /&gt;
&lt;br /&gt;
Other directories can be added to the WEB-INF directory as you find necessary. It might be useful, for example, to spread out various configuration files in a structure mimicking your application's directory structure. You may find it useful to store other data in the WEB-INF directory, but be sure to consider what belongs in the application directory structure instead. For example, images would be better stored in an images directory outside the WEB-INF directory than in it. Generally this is because you have to refer to the WEB-INF explicitly in your web pages and classes to access the items. As a good rule of thumb, if it isn't referenced in a configuration file, it belongs out of the WEB-INF directory.&lt;br /&gt;
&lt;br /&gt;
For now we'll discuss the directories we expect to be in the WEB-INF directory of our application.&lt;br /&gt;
&lt;br /&gt;
== classes ==&lt;br /&gt;
&lt;br /&gt;
The WEB-INF may contain a directory named '''classes''', in which you'd store compiled, but not archived classes. Often, if this is the application you're developing, this is where your compiled code would go. Another use of this directory is to extract archives that won't play nice with the automatic archive loading features of our server. We discuss that below.&lt;br /&gt;
&lt;br /&gt;
Initially we won't have any .class files in the classes directory. As we add action and bean classes, though, unless we archive them we'll put them in this directory. In our IDEs we will make sure this is the directory configured for our compiler target.&lt;br /&gt;
&lt;br /&gt;
== lib ==&lt;br /&gt;
&lt;br /&gt;
The WEB-INF may contain a directory named '''lib''', in which you store the Java Archive files that will be automatically added to your classpath. Note that typically only .jar files are automatically added; if you provide an archive with .zip as its extension, it will not be automatically added. These .zip files can be added in the command-line arguments to start the server, or in the server's configuration file. Generally, you should find it possible to rename .zip files to .jar files without incurring any ill effects; the Java archiver will use the correct method for extracting the information within the archive. If you can neither successfully add the .zip files to the server's start-up classpath or rename them to .jar files, you can either unzip and jar them yourself, or unzip them entirely in the WEB-INF's classes directory.&lt;br /&gt;
&lt;br /&gt;
Since we're making a Struts application we know we'll have a lib directory. Simply put the struts.jar and the Apache commons*.jar files that come in the Struts archive downloaded from the Apache website in the lib directory. These are the foundations for Struts and are required to make it work.&lt;br /&gt;
&lt;br /&gt;
== web.xml ==&lt;br /&gt;
&lt;br /&gt;
Fundamentally, the WEB-INF is required to have only one XML file within it to be considered an application; the web.xml. If there are no supporting files or classes, the lib and classes directories are unnecessary. It is possible to write an application entirely in JavaScript, for example, in which case you'd be able to pass even the the web.xml, but we anticipate a little more complex site if you're interested in using Struts.&lt;br /&gt;
&lt;br /&gt;
The web.xml file contains information about the files and servlets in your application. For our Struts discussion, we'll pare down the file into the bare minimum to make Struts work for us. In the following example web.xml we describe only one servlet and related mapping, allowing us to get our server to serve pages using Struts.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE web-app PUBLIC &lt;br /&gt;
  &amp;quot;-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN&amp;quot;&lt;br /&gt;
  &amp;quot;http://java.sun.com/dtd/web-app_2_3.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;web-app id=&amp;quot;WebApp&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;display-name&amp;gt;App Name&amp;lt;/display-name&amp;gt;&lt;br /&gt;
  &amp;lt;servlet&amp;gt;&lt;br /&gt;
    &amp;lt;servlet-name&amp;gt;action&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;
    &amp;lt;servlet-class&amp;gt;org.apache.struts.action.ActionServlet&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;
    &amp;lt;init-param&amp;gt;&lt;br /&gt;
      &amp;lt;param-name&amp;gt;config&amp;lt;/param-name&amp;gt;&lt;br /&gt;
      &amp;lt;param-value&amp;gt;/WEB-INF/struts-config.xml&amp;lt;/param-value&amp;gt;&lt;br /&gt;
    &amp;lt;/init-param&amp;gt;&lt;br /&gt;
    &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;
  &amp;lt;/servlet&amp;gt;&lt;br /&gt;
  &amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;
    &amp;lt;servlet-name&amp;gt;action&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;
    &amp;lt;url-pattern&amp;gt;*.do&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;
  &amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;
  &amp;lt;welcome-file-list&amp;gt;&lt;br /&gt;
    &amp;lt;welcome-file&amp;gt;default.jsp&amp;lt;/welcome-file&amp;gt;&lt;br /&gt;
  &amp;lt;/welcome-file-list&amp;gt;&lt;br /&gt;
&amp;lt;/web-app&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
What does this mean?&lt;br /&gt;
&lt;br /&gt;
Let's start from the outside and work our way in, top to bottom. The heading of the XML simply describes the XML and document model we'll be following. The [http://java.sun.com/dtd/web-app_2_3.dtd Document Type Descriptor] defines what XML elements are expected and even required. The descriptor is downloadable at the URL in the header, and is subject to change with different versions of the web-app API. More documentation exists within the descriptor, so for a deep understanding of that, we defer to the document itself. The web.xml file must contain the DOCTYPE line with the description and URL, as above, in order to comply with the spec.&lt;br /&gt;
&lt;br /&gt;
The web-app is the root of our XML. This needs to wrap the guts of our application's configuration. Within this wrapper come the other elements require for our operation.&lt;br /&gt;
&lt;br /&gt;
=== servlet ===&lt;br /&gt;
&lt;br /&gt;
Here we define the servlet that will be handling our Struts application. &lt;br /&gt;
&lt;br /&gt;
The '''servlet-name''' provides a mechanism in which we can refer to this servlet in this configuration file and in URLs, if we were to so choose. It provides a quick translation layer from the nearly always longer class name and a simple-to-remember key name. Since we're defining the actions that we'll be writing, we choose the title of '''action'''.&lt;br /&gt;
&lt;br /&gt;
The '''servlet-class''' defines the class that will be used when the servlet-name is encountered. We choose the Struts class &amp;lt;code&amp;gt;org.apache.struts.action.ActionServlet&amp;lt;/code&amp;gt;, which is the &amp;quot;controller&amp;quot; of the MVC view that Struts enables. For us, that means that the ActionServlet classes will be the ones that fire and do the logic behind our scenes. This is simply telling the server that we're using Struts.&lt;br /&gt;
&lt;br /&gt;
The servlet-class allows us to configure bits for the servlet to use using the '''init-param''' portion of the XML. This is entirely servlet dependent, and the XML won't care if the parameters are there, or if the ones that are there are useful to the servlet. &lt;br /&gt;
&lt;br /&gt;
Since we're using Struts, we provide the Struts servlet a configuration file name through the '''config''' parameter name, or '''param-name'''. The parameter value, or '''param-value''' is the path and file name to the Struts configuration file. The path is relative to the location of the application, or the parent of this web.xml's WEB-INF directory, thus the example has WEB-INF. The path can be absolute, but you need to be sure the web server can read the path and file; it's a good practice to keep these configuration files in your WEB-INF directory.&lt;br /&gt;
&lt;br /&gt;
A quick aside while we're discussing the configuration file.&lt;br /&gt;
&lt;br /&gt;
The config outlines a ''module'' in Struts. Everything contained within the struts-config.xml file is configured as one module.  The bean names, the validators, the actions, and all the rest that we haven't yet discussed. Generally everything defined in a directory structure will be matched with the corresponding module. If a directory is not found, its parent is searched for, until the default module is found and used at the root of the directory structure for the application; the one defined simply as '''config''', like we have. Any subdirectory we add to our application will use this configuration, unless we define a different module.&lt;br /&gt;
&lt;br /&gt;
To facilitate this, Struts allows you to add to the '''config''' parameter name in such a way as to define a module. To do this, simply provide another '''init-param''' to the same '''servlet''' block.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;init-param&amp;gt;&lt;br /&gt;
  &amp;lt;param-name&amp;gt;config/moduleName&amp;lt;/param-name&amp;gt;&lt;br /&gt;
  &amp;lt;param-value&amp;gt;/WEB-INF/moduleName/struts-config.xml&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/init-param&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This means that anything under the ''moduleName'' subdirectory of our application will use this configuration, and not that of our default. Any subdirectories of the ''moduleName'' directory would use this same configuration, unless we again explicitly defined another module.&lt;br /&gt;
&lt;br /&gt;
Note that we followed our earlier suggestion of mimicking a directory structure; the bits of JSP rekated to this module would be in a ''moduleName'' subdirectory of your application, so we reproduce this in the file name for the configuration. This is not required in the '''param-value'''; we could have easily said '''WEB-INF/moduleName-config.xml''' and kept the module's config file in the same directory as this web.xml and the other struts-config.xml. Note that it is a good practice to keep the Struts modules in a directory structure like your site would be.&lt;br /&gt;
&lt;br /&gt;
Additionally, we're not required to have the &amp;quot;default&amp;quot; configuration, as we describe in our web.xml file. We could have only module definitions. This is dependent entirely on your site organization, but it is a good practice to have a default, even if you don't facilitate any Struts use in the root of your application.&lt;br /&gt;
&lt;br /&gt;
Finally, in the '''servlet''' element we have a '''load-on-startup''' element with an order number. The elements will be gathered and started by the Servlet server first by the order number, then the order in the grouping; that is, if you care which Servlets are started first, take care with this parameter, otherwise the server will decide and start all of the zeroes before all of the ones before all of the twos... If the Servlet is not required to be started by the server at startup, put a negative number in the parameter and the servlet won't be started until it is first called upon. Since we're only hosting one Servlet here, the number is not important, but we want our Servlet instantiated to speed start-up.&lt;br /&gt;
&lt;br /&gt;
What this does for us is instantiate our Servlet's class, and calls its init() method. Whatever that method does is what happens next. For us, the Struts ActionServlet will read the configuration files and be prepared to handle action requests.&lt;br /&gt;
&lt;br /&gt;
=== servlet-mapping ===&lt;br /&gt;
&lt;br /&gt;
While not funamentally required, we use the '''servlet-mappping''' to describe URL elements that will be forwarded to our servlet. This allows us to have URLs other than simply calling our servlet. Since we've defined '''*.do''' as our mapping, any URL that ends in .do (before any optional query string) will be handled by the named servlet, in our case the '''action''' servlet, which we defined above.&lt;br /&gt;
&lt;br /&gt;
You can choose a different '''servlet-name''' than '''action''' if you named the servlet different in the '''servlet''' element. Choose your own name. Largely this will only be known by you as it will only be used in the web.xml file. For Struts, anyway, we won't be calling the servlet directly; we'll be defining URLs using the ''.do'' extension. &lt;br /&gt;
&lt;br /&gt;
The extension name can also be changed, if you don't want ''.do'' to be used. Change it in the '''url-pattern''' parameter. Whatever the URL pattern matches will be disregarded and the rest will be passed to the Struts ActionServlet to find a corresponding action. That is, if you choose the pattern of ''/*.do'' and the request comes for ''/page.do'' the ''.do'' will be disregarded, and ''page'' will be used to find an action; similarly, if you named ''/do/*'' as the mapping, then ''/do/page'' would find the ''page'' action. This also means that with ''/*.do'' that ''path/page.do'' would be mapped to the ''path/page'' action. We'll discuss the action mapping in a bit.&lt;br /&gt;
&lt;br /&gt;
Note also that Struts only supports one servlet-mapping per Servlet instance. As a tricky work-around, if you want more than one, you can define more than servlet and servlet-mapping &amp;quot;pair&amp;quot;, even using the same configuration files or other resources. Note that these will be two instances of the Servlet running on the server, and that the in order for the instances to directly share information takes some advanced programming. This trick can be used to isolate multiple Struts &amp;quot;applications&amp;quot; on the same server. More acceptable, however, is to define multiple modules.&lt;br /&gt;
&lt;br /&gt;
=== welcome-file-list ===&lt;br /&gt;
&lt;br /&gt;
While not required for Struts to work, the '''welcome-file-list''' element adds a default home page to our application so that we don't have to hand out explicit pages in our URLs. This allows us to put a file, in this case '''default.jsp''' in a directory and have the URL end at the path containing that file; like ''http://server.name/path''.&lt;br /&gt;
&lt;br /&gt;
We can make this a Struts-aware page, allowing us to jump right into our Struts application, but that's a little more advanced than we want to discuss right now.&lt;br /&gt;
&lt;br /&gt;
Simply provide a list of file names in '''welcome-file''' elements and they will be treated, in order, as the default pages. That is, if our directory contained both an ''index.jsp'' and a ''default.jsp'' and our list contained both as welcome files, the order entered in the list would be the order in which they'd be accepted. If no file is found from the list, then the server will respond as directed; either with an error or a list of files in the directory.&lt;br /&gt;
&lt;br /&gt;
== struts-config.xml ==&lt;br /&gt;
&lt;br /&gt;
For our purposes we'll start with a very rudimentary '''struts-config.xml''' that describes the action, bean, and forward for one simple page. The goal is to lay out what needs to be done, and build from that to understand what can be done.&lt;br /&gt;
&lt;br /&gt;
The rudimentary struts-config.xml looks like this.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE struts-config PUBLIC &lt;br /&gt;
  &amp;quot;-//Apache Software Foundation//DTD Struts Configuration 1.1//EN&amp;quot;&lt;br /&gt;
  &amp;quot;http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;struts-config&amp;gt;&lt;br /&gt;
  &amp;lt;!-- Data Sources --&amp;gt;&lt;br /&gt;
  &amp;lt;data-sources /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Form Beans --&amp;gt;&lt;br /&gt;
  &amp;lt;form-beans /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Global Exceptions --&amp;gt;&lt;br /&gt;
  &amp;lt;global-exceptions /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Global Forwards --&amp;gt;&lt;br /&gt;
  &amp;lt;global-forwards /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Action Mappings --&amp;gt;&lt;br /&gt;
  &amp;lt;action-mappings /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Controller --&amp;gt;&lt;br /&gt;
  &amp;lt;controller /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Message Resources --&amp;gt;&lt;br /&gt;
  &amp;lt;message-resources /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Plug-ins --&amp;gt;&lt;br /&gt;
  &amp;lt;plug-in /&amp;gt;&lt;br /&gt;
&amp;lt;/struts-config&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What does this all mean?&lt;br /&gt;
&lt;br /&gt;
The XML Document Type Descriptor is available from [http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd Apache], and it defines the bits we want or need to have. What we have provided is a selection of all of the available elements, although none have any details provided. This is a good place to start, as it gives the skeleton from which we can build. Quite technically, we don't need anything between the '''struts-config''' element tags. &lt;br /&gt;
&lt;br /&gt;
For our purposes, we'll go with the following, and describe the details below.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE struts-config PUBLIC &lt;br /&gt;
  &amp;quot;-//Apache Software Foundation//DTD Struts Configuration 1.1//EN&amp;quot;&lt;br /&gt;
  &amp;quot;http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;struts-config&amp;gt;&lt;br /&gt;
  &amp;lt;!-- Form Beans --&amp;gt;&lt;br /&gt;
  &amp;lt;form-beans&amp;gt;&lt;br /&gt;
    &amp;lt;form-bean name=&amp;quot;exampleForm&amp;quot; type=&amp;quot;package.ExampleForm&amp;quot; /&amp;gt;&lt;br /&gt;
  &amp;lt;/form-beans&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Action Mappings --&amp;gt;&lt;br /&gt;
  &amp;lt;action-mappings&amp;gt;&lt;br /&gt;
    &amp;lt;action path=&amp;quot;/default&amp;quot; forward=&amp;quot;default.jsp&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;action path=&amp;quot;/example&amp;quot; type=&amp;quot;package.ExampleAction&amp;quot; &lt;br /&gt;
      name=&amp;quot;exampleForm&amp;quot; scope=&amp;quot;request&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;forward name=&amp;quot;success&amp;quot; path=&amp;quot;/example.jsp&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;forward name=&amp;quot;failure&amp;quot; path=&amp;quot;/default.do&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/action&amp;gt;&lt;br /&gt;
  &amp;lt;/action-mappings&amp;gt;&lt;br /&gt;
&amp;lt;/struts-config&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we've trimmed out the un-used elements for brevity. It's a good practice to leave them in, even if nothing is defined, as it provides a reminder for what can be done.&lt;br /&gt;
&lt;br /&gt;
=== form-beans ===&lt;br /&gt;
&lt;br /&gt;
In the '''form-beans''' section we have defined a single '''form-bean'''. A form bean is a predefined JavaBean that extends the ''org.apache.struts.action.ActionForm'' class. The form beans are generally no-logic data place-holder classes, with private data members and public accessor methods (getters and setters), in normal bean fashion. &lt;br /&gt;
&lt;br /&gt;
Our configuration defines one '''form-bean''', the ''exampleForm''. Of course, this name is completely up to the web developer. The '''name''' of the bean is used both in the '''struts-config.xml''' to help relate beans to actions, and also within JSP pages to use the data in the bean. The '''type''' of the bean is the fully-qualified package and class name of the Java object that will be used when this bean is called for.&lt;br /&gt;
&lt;br /&gt;
=== action-mappings ===&lt;br /&gt;
&lt;br /&gt;
The '''action-mappings''' element is the guts of the layer between the URL requested and the Struts Action that will do the requested work. Any number of '''action''' elements can be inside the '''action-mappings''', each corresponding to a URL-to-class mapping.&lt;br /&gt;
&lt;br /&gt;
==== action ====&lt;br /&gt;
&lt;br /&gt;
The ''action'' element defines the individual action handlers. The example we have shows two ''action elements'' to give a simple example, and a more common, slightly more complex example.&lt;br /&gt;
&lt;br /&gt;
The first example is for the ''/default'' action, as indicated by the '''path''' attribute. As we look at our WEB-INF/web.xml file we see that we're using ''/*.do'' as our servlet-mapping url-pattern. This action will be triggered when the URL ''/default.do'' is sent from the web server. Note that this does not mean that when the web server receives a ''/default.jsp'' URL that it will call this action. It means that when we get a ''/default'' action we'll also use the ''default.jsp''. The name of the action must be unique within the module. Other modules can have actions with the same name. Of course, the action's name is up to the developer.&lt;br /&gt;
&lt;br /&gt;
The '''forward''' attribute tells Struts what will process this request. In our example we're telling Struts to use the '''default.jsp''' page, which must exist in the root of this module. More correctly, for our value, the page must exist in the root of our module; the value is relative to the module's root, and since we put only ''default.jsp'' we're saying that JSP will be in the root. Since we provide no other information to the action, there are no other assumptions we can make when working on that JSP page; most specifically, we have not provided it with a bean to use. The forward does not need to be unique; many actions may use the same forward as their target.&lt;br /&gt;
&lt;br /&gt;
The second example '''action''' is named ''/example'', and like the ''/default'' example above, this will answer to ''/example.do'' URL requests. Unlike the previous example, this one uses a class to do the work, and then based on the results of the work, it can choose which forward to use. &lt;br /&gt;
&lt;br /&gt;
The '''type''' attribute describes the fully-qualified package and class of the Struts Action that will peform the work for this request. This class must extend ''org.apache.struts.action.Action''. By default, the Action class' '''doAction()''' method will be called. Later we'll discuss a way to use the ''parameter'' of the '''action''' to define different methods to call.&lt;br /&gt;
&lt;br /&gt;
Our '''action''' defines a bean to be used as part of the Struts activity. The action '''name''' attribute must match one of the '''form-bean''' definitions above. This bean will then be passed to the ''doAction()'' method in the ''ActionForm'' parameter.&lt;br /&gt;
&lt;br /&gt;
Our '''action''' restricts the '''scope''' of our bean to the '''request'''. This means that when the action is initiated, it has a new bean full of information from the JSP that is intiating our request. The alternative is to define the bean as '''session''', which would mean that the same bean would be used throughout the user's visit to the site, even if they navigate away from actions that use the bean. By using '''request''' we can be certain that the information in the bean is from the JSP. We can populate additional information in the bean as we pass it to the JSP, which it can then use to prepare its presentation, but whatever we've put in the bean will be missing, unless the JSP puts it back...we'll try to make that more clear as we go through the Action in a minute.&lt;br /&gt;
&lt;br /&gt;
==== forward ====&lt;br /&gt;
&lt;br /&gt;
Our second example '''action''' also defines a couple of '''forward''' mappings. These mappings allow us to decide within our action what to do under certain cases.&lt;br /&gt;
&lt;br /&gt;
The '''forward''' element has a '''name''' attribute that can be used inside our Action class to find the intended target for the action under this case. The name is chosen by the user, and has no special meaning within Struts. We've chosen '''success''' and '''failure''' as easy to understand concepts. The '''path''' attribute of the forward tells Struts which page will handle the results of our Action. In the case we choose to use the ''success'' mapping, we'll end up at the ''/example.jsp'' in the root of our module (again, as above the page is relative to the module root). If we choose the ''failure'' mapping we'll be sent to our ''/default.do'' at the root of our module, which will actually return through the previous '''action''' we mapped.&lt;br /&gt;
&lt;br /&gt;
== Digesting the struts-config.xml ==&lt;br /&gt;
&lt;br /&gt;
In our simple example, we can infer that we may go from the ''default.jsp'' by way of an ''/example.do'' URL. Within the Action processing behind the ''/example'' action, we may find ourselves at either an ''example.jsp'' or back to the ''default.jsp'', depending on the outcome of the ''ExampleAction''.&lt;/div&gt;</description>
			<pubDate>Tue, 27 Sep 2005 19:45:08 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Struts_Webserver_Configuration</comments>		</item>
		<item>
			<title>Setting Up Struts</title>
			<link>http://www.softwarebyjeff.com/index.php/Setting_Up_Struts</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]]&lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
When downloading the Struts package, there's an example WAR that one is encouraged to extract and mutate into your own project, removing the example bits that won't get used, and renaming or duplicating the bits that will. That's a poor way to start, especially if you want to understand what's required to make things work. Even more if you want to minimize a WAR.&lt;br /&gt;
&lt;br /&gt;
The documentation at the Apache website leaves a lot to be desired in this space, too. Even the two Struts books I picked up for the purpose of learning these bits is a little terse in these areas. Both of them provide examples, and then text that should help encourage you to feel excited because the examples work. I'm sure that's a hard bit to miss when writing these texts, but I'm gonna try. We'll make a very simple &amp;quot;hello world&amp;quot; Struts application, hopefully describing enough along the way that you can skip the &amp;quot;copy and alter&amp;quot; method, and just create what you need without wasting your time cleaning up our unncessary bits.&lt;br /&gt;
&lt;br /&gt;
I'm not going to define Struts or proclaim its virtues over other technologies, frameworks, patters, or however you categorize it. I'm not going to pitch a preferred methodology. I'm just going to describe the pieces and how they fit together, and a little bit of how to make them work in our favorite IDEs [http://netbeans.org NetBeans] and [http://eclipse.org Eclipse], and how to deploy them to our favorite servers like [http://jakarta.apache.org/tomcat Tomcat]. &lt;br /&gt;
&lt;br /&gt;
Assuming your Java is correct, and you use the correct things in the correct places, your application should work right out of the box; all without mutating from someone else's example application.&lt;br /&gt;
&lt;br /&gt;
== Chicken or Egg? ==&lt;br /&gt;
&lt;br /&gt;
One of the toughest parts of developing Struts applications is what to do first. There's a three-way coordination between the user-presentation JSPs, the server configuration files, and the Java classes used to do the server-side work. When deep in the development cycle, it becomes a matter of habit to create the classes first and configurations next before making the JSPs, or to create the classes and JSPs together before adding the configuration, or even to try to develop all three together, adding configuration items as classes and JSPs are introduced. For our purposes, we'll discuss first a system that has been theoretically configured before we got here, that we will use to discover the relationships between the simple components. &lt;br /&gt;
&lt;br /&gt;
We'll start with the web server, which must be running before requests can be made, and then look at the classes, which will be required when the web server starts working, and finally the JSPs that will be requested by the users.&lt;br /&gt;
&lt;br /&gt;
After we have our discussion about the component parts, we'll discuss a couple of sample set-ups from the perspective of a brand new IDE project in both NetBeans and Eclipse.&lt;/div&gt;</description>
			<pubDate>Tue, 27 Sep 2005 19:43:44 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Setting_Up_Struts</comments>		</item>
		<item>
			<title>Starting with Struts</title>
			<link>http://www.softwarebyjeff.com/index.php/Starting_with_Struts</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Starting with Struts]] &lt;br /&gt;
- [[Setting Up Struts]]&lt;br /&gt;
- [[Struts Webserver Configuration]]&lt;br /&gt;
- [[Our Struts Classes]]&lt;br /&gt;
- [[Our Struts Web Pages]]&lt;br /&gt;
- [[Struts Server Deployment]]&lt;br /&gt;
- [[Making Struts Projects in IDEs]]&lt;br /&gt;
----&lt;br /&gt;
I've been working with [http://struts.apache.org Struts] for a while, but in reality I've never started from scratch. Generally I'm brought on to work on projects already in progress, so I've never had the reason or opportunity to start from scratch.&lt;br /&gt;
&lt;br /&gt;
Struts is an application framework intent on trying to make development of web applications easier. It's an extension of the [http://java.sun.com/products/jsp/jstl/index.jsp JavaServer Pages Standard Tag Library], and a precursor to [http://java.sun.com/j2ee/javaserverfaces/index.jsp JavaServer Faces]. It's a relatively easy-to-use, non-invasive way to bring Java to a web presented application.&lt;/div&gt;</description>
			<pubDate>Mon, 26 Sep 2005 19:36:51 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Starting_with_Struts</comments>		</item>
		<item>
			<title>Sendmail</title>
			<link>http://www.softwarebyjeff.com/index.php/Sendmail</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Setting up a new server and even upgrading an old one can be a little tough. The documentation is a little short on how to make a thorough installation that allows easy post-installation modification.&lt;br /&gt;
&lt;br /&gt;
== Downloading ==&lt;br /&gt;
&lt;br /&gt;
Downloading is simple enough. Visit the [http://sendmail.org sendmail] home page, find the current stable release and download it.&lt;br /&gt;
&lt;br /&gt;
Sendmail is also available on many LINUX distributions as a package that can easily be installed by point-and-click. We prefer getting software straight from the horse's mouth, if you please. As it happens, at this time, sendmail.com has v8.13.5 available, but the latest in SuSe's YaST is v8.13.3. Not a big difference, but they're two versions out; it'd be fair to expect them to upgrade to the next version, which would only make them one version out, but it may be the case that more would be released by sendmail.com.&lt;br /&gt;
&lt;br /&gt;
Additionally, for the adventerous, there is the next version of sendmail available in beta from from sendmail.com. Use at your own risk.&lt;br /&gt;
&lt;br /&gt;
== Building ==&lt;br /&gt;
&lt;br /&gt;
The archive from sendmail.com breaks out all of the tools that accompany sendmail into directories within their distribution. Unpack the archive you've downloaded using the appropriate tool. We chose the gzipped tar, so we simply do '''tar -xzf sendmail.8.13.5.tar.gz''' in the directory in which we want the archive unpacked. Of course, your version and path may vary.&lt;br /&gt;
&lt;br /&gt;
In the uncompressed '''sendmail-8.13.5''' directory is everything you need. The INSTALL document outlines the simple steps, but we feel there are a couple missing, so we'll outline it here.&lt;br /&gt;
&lt;br /&gt;
There's a simple Build shell script that will compile everything for you. Of course, this assumes you've got your machine set up with a compiler (we use '''gcc'''). The Build script will go into each subdirectory and build the required libraries and executables for both sendmail and its helper tools. Simply run that with '''sh Build''' in that directory.&lt;br /&gt;
&lt;br /&gt;
Then you need to configure the rules file. In the '''cf/cf''' subdirectory is a list of simple default ''.mc'' files. Find the one that matches your system, copy it to '''sendmail.mc''' in the '''cf/cf''' directory. Edit the file to match your desired bits of help. &lt;br /&gt;
&lt;br /&gt;
This is the tricky part. Here's where you tell sendmail what features you want to use, what paramters to use for certain functions, and generally how to act. Your needs may vary, but here's how we configure ours, mostly:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
OSTYPE(`solaris2')dnl&lt;br /&gt;
DOMAIN(`generic')dnl&lt;br /&gt;
define(`LOCAL_MAILER_FLAGS', LOCAL_MAILER_FLAGS`'P)dnl&lt;br /&gt;
define(`confCW_FILE', `-o /etc/mail/sendmail.cw')dnl&lt;br /&gt;
define(`confNO_RCPT_ACTION', `add-to-undisclosed')dnl&lt;br /&gt;
define(`confMAX_MIME_HEADER_LENGTH', `256/128')dnl&lt;br /&gt;
define(`confPID_FILE',`/etc/mail/sendmail.pid')dnl&lt;br /&gt;
FEATURE(`access_db',`dbm -T&amp;lt;TMPF&amp;gt; /etc/mail/access')&lt;br /&gt;
FEATURE(`always_add_domain')dnl&lt;br /&gt;
FEATURE(`dnsbl',`list.dsbl.org',`&amp;quot;550 Rejected &amp;quot; $&amp;amp;{client_addr} &amp;quot; found in list.dsbl.org&amp;quot;')dnl&lt;br /&gt;
FEATURE(`dnsbl',`sbl-xbl.spamhaus.org',`&amp;quot;550 Rejected &amp;quot; $&amp;amp;{client_addr} &amp;quot; found in sbl-xbl.spamhaus.org&amp;quot;')dnl&lt;br /&gt;
FEATURE(`dnsbl', `dnsbl.sorbs.net', `&amp;quot;550 Rejected &amp;quot; $&amp;amp;{client_addr} &amp;quot; found in dnsbl.sorbs.net&amp;quot;')dnl&lt;br /&gt;
FEATURE(`dnsbl', `relays.ordb.org', `&amp;quot;550 Rejected &amp;quot; $&amp;amp;{client_addr} &amp;quot; found in relays.ordb.org -- Due to sending server misconfiguration - see http://www.ordb.org/faq/\#why_rejected&amp;quot;')dnl&lt;br /&gt;
FEATURE(`dnsbl', `bl.spamcop.net', `&amp;quot;550 Rejected &amp;quot; $&amp;amp;{client_addr} &amp;quot; found in b1.spamcop.net -- Spam blocked see: http://spamcop.net/bl.shtml?&amp;quot;$&amp;amp;{client_addr}')dnl&lt;br /&gt;
FEATURE(`nouucp', `reject')dnl&lt;br /&gt;
FEATURE(`mailertable',`dbm /etc/mail/mailertable')dnl&lt;br /&gt;
FEATURE(`redirect')dnl&lt;br /&gt;
FEATURE(`virtusertable',`dbm /etc/mail/virtusers')dnl&lt;br /&gt;
FEATURE(`smrsh')&lt;br /&gt;
MAILER(`local')dnl&lt;br /&gt;
MAILER(`smtp')dnl&lt;br /&gt;
dnl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Some bits are kind of self-explanitory. The '''define''' lines simply set up some parameters that sendmail will use when executing. The '''FEATURE''' lines tell the configuration file to add some predefined scripts to the final configuration&lt;br /&gt;
&lt;br /&gt;
We're not going to outline what these do; read the README in the '''cf''' directory for details. Read these details with each install, as some of the feature and definitions change, both in the name and paramters, and even in function.&lt;br /&gt;
&lt;br /&gt;
The configuratin we use offers some protection from abuse, allows us to accept and send mail from multiple domains on one server, and allows us some facility to rename (alias) users and redirect mail (virtusers).&lt;br /&gt;
&lt;br /&gt;
Our favorites are the '''dnsbl''' lines, which help set up the anti-spam. There are lists out there, and we may update this to include some details about it. We  run every mail through five filters before giving up. These filters don't check the content of mail, simply the sending servers; if a server's been reported as allowing spam through it to one of these lists, our check will find out and not allow us to receive mail from those servers.&lt;br /&gt;
&lt;br /&gt;
One line left out of this installation configuration allows the use of [http://procmail.org procmail], a strong mail processing program that allows users to configure some of their own post-acceptance mail handling, including the use of content-based anti-spam mechanisms such as [http://spamassassin.apache.org SpamAssassin]. We've left it out of this discussion in order to streamline the sendmail topic.&lt;br /&gt;
&lt;br /&gt;
In the '''cf''' directory (the parent to '''cf/cf''') is another Build script. Run that by '''sh Build sendmail.cf'''.&lt;br /&gt;
&lt;br /&gt;
After building the libraries and executables, and making the base configuration, you're ready to go.&lt;br /&gt;
&lt;br /&gt;
== Installing ==&lt;br /&gt;
&lt;br /&gt;
The installation is where the process differs from the INSTALL file.&lt;br /&gt;
&lt;br /&gt;
=== Upgrade Installation ===&lt;br /&gt;
&lt;br /&gt;
If you're replacing an old installation of sendmail, back-up the information before you start. This is just common sense. It has been our fortune to have never needed to back out of an installation, but it's always good practice.&lt;br /&gt;
&lt;br /&gt;
Something simple like the following should cover the bases. Either look at the previous installation's directories, or this installations directories to see what needs archiving. That is, because we are going to replace '''vacation''' we should archive the old one.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -cf sendmail.tar \&lt;br /&gt;
/etc/mail/* /usr/lib/mail/* \&lt;br /&gt;
/usr/lib/sendmail /usr/lib/smrsh \&lt;br /&gt;
/usr/sbin/makemap /usr/bin/mailstats \&lt;br /&gt;
/usr/bin/praliases /usr/bin/rmail \&lt;br /&gt;
/usr/bin/test /usr/bin/vacation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Your file locations may vary, but these are the defaults from the '''Build''' scripts.&lt;br /&gt;
&lt;br /&gt;
Additionally, after you complete the installation below, remember to update any of the '''/etc/mail''' databases like '''aliases''' and '''virtusers''' with the new tools, to ensure they're compatible.&lt;br /&gt;
&lt;br /&gt;
=== New Installation ===&lt;br /&gt;
&lt;br /&gt;
If this is the first time you're installing sendmail on a server, there's just a few things you need to do to get set up. Most critically is to ensure that sendmail doesn't get the ability to run amok on your system.&lt;br /&gt;
&lt;br /&gt;
Crete a new user and group named smmsp by whatever method makes sense for your system. The user '''smmsp''' should have its primary and only group be '''smmsp'''. Likewise, the group '''smmsp''' should have only one member, the '''smmsp''' user. It is recommended the group ID be '''25''', although it is unclear why; since it's easy to do, just do it.&lt;br /&gt;
&lt;br /&gt;
Create a few directories for the mail to exist. For the most part, access can be limited to the root user and the super-user group appropriate for your system. Create the following directories if they don't exist.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/spool/clientmqueue&lt;br /&gt;
/var/spool/mqueue&lt;br /&gt;
/etc/mail&lt;br /&gt;
/var/mail&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The '''/var/spool/clientmqueue''' should belong to the smmsp user and group, so give it a quick '''chown -R smmsp:smmsp /var/spool/clientmqueue'''. If you don't change these permissions, sendmail may complain or even fail to start.&lt;br /&gt;
&lt;br /&gt;
=== Install ===&lt;br /&gt;
&lt;br /&gt;
Then you can install by simply doing '''sh Build install''' in the sendmail source root directory to install the executables, and '''sh Build install-cf''' in the '''cf''' directory to install the configuration file.&lt;br /&gt;
&lt;br /&gt;
The '''cf''' directory should also be copied to '''/usr/lib/mail''' to facilitate recreating the configuration files. If you don't do this, make sure to hold onto the sendmail source directory so you have access to the '''cf/cf''' directory. Simply '''cp -Rp cf /usr/lib/mail''' and you'll have all of the goods for later.&lt;br /&gt;
&lt;br /&gt;
If you've done a new installation, create the following files.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /etc/mail&lt;br /&gt;
touch aliases&lt;br /&gt;
touch access&lt;br /&gt;
touch mailertable&lt;br /&gt;
touch virtusers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
These are needed as they're named in our '''sendmail.mc''' file. Note even in an upgrade, the touch won't hurt anything.&lt;br /&gt;
&lt;br /&gt;
Whether you're doing a new or updated installation, you need to make the databases that sendmail will use, even if the files are empty (as in after the creation above). The files named above are plain-text, but they need to be made into databases with the new version of '''makemap'''. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /etc/mail&lt;br /&gt;
newaliases&lt;br /&gt;
makemap dbm access &amp;lt; access&lt;br /&gt;
makemap dbm mailertable &amp;lt; mailertable&lt;br /&gt;
makemap dbm virtusers &amp;lt; virtusers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Note the '''newaliases''' is used to regenerate the aliases instead of makemap.&lt;br /&gt;
&lt;br /&gt;
=== Startup Script ===&lt;br /&gt;
&lt;br /&gt;
Curiously, a start-up script doesn't come with sendmail. The various documents in the sendmail source directories spread out the bits needed to start sendmail, including the various command line options. This script can be inserted in your '''/etc/init.d''' directory, and symlinked to start in your '''/etc/rc2.d''' directory, or whatever the directory name or structure your installation requires.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/sbin/sh&lt;br /&gt;
#&lt;br /&gt;
# Copyright (c) 1992 - 2001 by Sun Microsystems, Inc.&lt;br /&gt;
# All rights reserved.&lt;br /&gt;
#&lt;br /&gt;
#ident  &amp;quot;@(#)sendmail   1.19    01/12/05 SMI&amp;quot;&lt;br /&gt;
&lt;br /&gt;
ERRMSG1='WARNING: /var/mail is NFS-mounted without setting actimeo=0,'&lt;br /&gt;
ERRMSG2='this can cause mailbox locking and access problems.'&lt;br /&gt;
SERVER_PID_FILE=&amp;quot;/etc/mail/sendmail.pid&amp;quot;&lt;br /&gt;
CLIENT_PID_FILE=&amp;quot;/var/spool/clientmqueue/sm-client.pid&amp;quot;&lt;br /&gt;
DEFAULT_FILE=&amp;quot;/etc/default/sendmail&amp;quot;&lt;br /&gt;
ALIASES_FILE=&amp;quot;/etc/mail/aliases&amp;quot;&lt;br /&gt;
&lt;br /&gt;
check_queue_interval_syntax()&lt;br /&gt;
{&lt;br /&gt;
        default=&amp;quot;15m&amp;quot;&lt;br /&gt;
        if [ $# -lt 1 ]; then&lt;br /&gt;
                answer=$default&lt;br /&gt;
                return&lt;br /&gt;
        fi&lt;br /&gt;
        if echo $1 | egrep '^([0-9]*[1-9][0-9]*[smhdw])+$' &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
                answer=$1&lt;br /&gt;
        else&lt;br /&gt;
                answer=$default&lt;br /&gt;
        fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;$1&amp;quot; in&lt;br /&gt;
'restart')&lt;br /&gt;
        [ -f $SERVER_PID_FILE ] &amp;amp;&amp;amp; kill -1 `head -1 $SERVER_PID_FILE`&lt;br /&gt;
        [ -f $CLIENT_PID_FILE ] &amp;amp;&amp;amp; kill -1 `head -1 $CLIENT_PID_FILE`&lt;br /&gt;
        ;;&lt;br /&gt;
&lt;br /&gt;
'start')&lt;br /&gt;
        if [ -f /usr/lib/sendmail -a -f /etc/mail/sendmail.cf ]; then&lt;br /&gt;
                if [ ! -d /var/spool/mqueue ]; then&lt;br /&gt;
                        /usr/bin/mkdir -m 0750 /var/spool/mqueue&lt;br /&gt;
                        /usr/bin/chown root:bin /var/spool/mqueue&lt;br /&gt;
                fi&lt;br /&gt;
                if [ ! -f $ALIASES_FILE.db ] &amp;amp;&amp;amp; [ ! -f $ALIASES_FILE.dir ] \&lt;br /&gt;
                    &amp;amp;&amp;amp; [ ! -f $ALIASES_FILE.pag ]; then&lt;br /&gt;
                        /usr/sbin/newaliases&lt;br /&gt;
                fi&lt;br /&gt;
                MODE=&amp;quot;-bd&amp;quot;&lt;br /&gt;
                [ -f $DEFAULT_FILE ] &amp;amp;&amp;amp; . $DEFAULT_FILE&lt;br /&gt;
                #&lt;br /&gt;
                # * MODE should be &amp;quot;-bd&amp;quot; or null (MODE= or MODE=&amp;quot;&amp;quot;) or&lt;br /&gt;
                #   left alone.  Anything else and you're on your own.&lt;br /&gt;
                # * QUEUEOPTION should be &amp;quot;p&amp;quot; or null (as above).&lt;br /&gt;
                # * [CLIENT]QUEUEINTERVAL should be set to some legal value;&lt;br /&gt;
                #   sanity checks are done below.&lt;br /&gt;
                # * [CLIENT]OPTIONS are catch-alls; set with care.&lt;br /&gt;
                #&lt;br /&gt;
                if [ -n &amp;quot;$QUEUEOPTION&amp;quot; -a &amp;quot;$QUEUEOPTION&amp;quot; != &amp;quot;p&amp;quot; ]; then&lt;br /&gt;
                        QUEUEOPTION=&amp;quot;&amp;quot;&lt;br /&gt;
                fi&lt;br /&gt;
                if [ -z &amp;quot;$QUEUEOPTION&amp;quot; -o -n &amp;quot;$QUEUEINTERVAL&amp;quot; ]; then&lt;br /&gt;
                        check_queue_interval_syntax $QUEUEINTERVAL&lt;br /&gt;
                        QUEUEINTERVAL=$answer&lt;br /&gt;
                fi&lt;br /&gt;
                check_queue_interval_syntax $CLIENTQUEUEINTERVAL&lt;br /&gt;
                CLIENTQUEUEINTERVAL=$answer&lt;br /&gt;
                /usr/lib/sendmail $MODE -q$QUEUEOPTION$QUEUEINTERVAL $OPTIONS &amp;amp;&lt;br /&gt;
                /usr/lib/sendmail -Ac -q$CLIENTQUEUEINTERVAL $CLIENTOPTIONS &amp;amp;&lt;br /&gt;
                #&lt;br /&gt;
                # ETRN_HOSTS should be of the form&lt;br /&gt;
                # &amp;quot;s1:c1.1,c1.2        s2:c2.1 s3:c3.1,c3.2,c3.3&amp;quot;&lt;br /&gt;
                # i.e., white-space separated groups of server:client where&lt;br /&gt;
                # client can be one or more comma-separated names; N.B. that&lt;br /&gt;
                # the :client part is optional; see etrn(1M) for details.&lt;br /&gt;
                # server is the name of the server to prod; a mail queue run&lt;br /&gt;
                # is requested for each client name.  This is comparable to&lt;br /&gt;
                # running &amp;quot;/usr/lib/sendmail -qRclient&amp;quot; on the host server.&lt;br /&gt;
                #&lt;br /&gt;
                # See RFC 1985 for more information.&lt;br /&gt;
                #&lt;br /&gt;
                for i in $ETRN_HOSTS; do&lt;br /&gt;
                        SERVER=`echo $i | /usr/bin/sed -e 's/:.*$//'`&lt;br /&gt;
                        CLIENTS=`echo $i | /usr/bin/sed -n -e 's/,/ /g' \&lt;br /&gt;
                            -e '/:/s/^.*://p'`&lt;br /&gt;
                        /usr/sbin/etrn $SERVER $CLIENTS &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&lt;br /&gt;
                done&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
        if /usr/bin/nawk 'BEGIN{s = 1}&lt;br /&gt;
            $2 == &amp;quot;/var/mail&amp;quot; &amp;amp;&amp;amp; $3 == &amp;quot;nfs&amp;quot; &amp;amp;&amp;amp; $4 !~ /actimeo=0/ &amp;amp;&amp;amp;&lt;br /&gt;
            $4 !~ /noac/{s = 0} END{exit s}' /etc/mnttab; then&lt;br /&gt;
&lt;br /&gt;
                /usr/bin/logger -p mail.crit &amp;quot;$ERRMSG1&amp;quot;&lt;br /&gt;
                /usr/bin/logger -p mail.crit &amp;quot;$ERRMSG2&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        ;;&lt;br /&gt;
&lt;br /&gt;
'stop')&lt;br /&gt;
        [ -f $SERVER_PID_FILE ] &amp;amp;&amp;amp; kill `head -1 $SERVER_PID_FILE`&lt;br /&gt;
        if [ -f $CLIENT_PID_FILE ]; then&lt;br /&gt;
                kill `head -1 $CLIENT_PID_FILE`&lt;br /&gt;
                rm -f $CLIENT_PID_FILE&lt;br /&gt;
        fi&lt;br /&gt;
        /usr/bin/pkill -x -u 0 sendmail&lt;br /&gt;
        ;;&lt;br /&gt;
&lt;br /&gt;
*)&lt;br /&gt;
        echo &amp;quot;Usage: $0 { start | stop | restart }&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
        ;;&lt;br /&gt;
esac&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This is the file from our server. We had to change the SERVER_PID_FILE variable to match the one outlined in our sendmail.cf file, but otherwise it's untouched.&lt;br /&gt;
&lt;br /&gt;
Later we might replace this with our own.&lt;br /&gt;
&lt;br /&gt;
== Maintaining ==&lt;br /&gt;
&lt;br /&gt;
The degree of difficulty in the maintenance of sendmail installations varies with the initial configuration, the options used, and the skill level of the maintainer.&lt;br /&gt;
&lt;br /&gt;
Simply, the configuration file above requires very little in terms of maintenance. As new users are added to the machine, sendmail will automatically handle them. If the database files '''aliases''' or '''virtusers''' and the like are modified, simply rebuild the databases as above, and sendmail will recongize them.&lt;br /&gt;
&lt;br /&gt;
If you replace sendmail with a new version, simply repeat the above.&lt;/div&gt;</description>
			<pubDate>Sat, 24 Sep 2005 17:06:38 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Sendmail</comments>		</item>
		<item>
			<title>Dynamically Retreive Method Name</title>
			<link>http://www.softwarebyjeff.com/index.php/Dynamically_Retreive_Method_Name</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;When writing a Java class, it happens that you may need to have the method name. Most typically this is needed for something like logging an error or writing a diagnostic message.&lt;br /&gt;
&lt;br /&gt;
By far, the most common pattern is to use a string literal (or even a variable) with the method's name in readable text. This is easy, and just plain works. &lt;br /&gt;
&lt;br /&gt;
But what if you want to find the method name somewhere else? What if you want to survive a refactoring where your method is copied or moved to a different class or gets renamed?&lt;br /&gt;
&lt;br /&gt;
=== Get Current Method ===&lt;br /&gt;
&lt;br /&gt;
The following super-simple example Utilities class has a method that can be called by any other method to get the current method. To restate that a little...a method can call the '''getCurrentMethodName()''' created below, and receive its own name.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Utilities {&lt;br /&gt;
   public static String getCurrentMethodName() {&lt;br /&gt;
       ByteArrayOutputStream &lt;br /&gt;
           byteArrayOutputStream = new ByteArrayOutputStream();&lt;br /&gt;
       PrintWriter &lt;br /&gt;
           printWriter = new PrintWriter(byteArrayOutputStream);&lt;br /&gt;
       (new Throwable()).printStackTrace(printWriter);&lt;br /&gt;
       printWriter.flush();&lt;br /&gt;
       String stackTrace = byteArrayOutputStream.toString();&lt;br /&gt;
       printWriter.close();&lt;br /&gt;
&lt;br /&gt;
       StringTokenizer &lt;br /&gt;
           stringTokenizer = new StringTokenizer(stackTrace, &amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
       // Line 1 -- java.lang.Throwable&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Line 2 -- &amp;quot;at thisClass.thisMethod(file.java:line)&amp;quot;&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Line 3 -- &amp;quot;at callingClass.callingMethod(file.java:line)&amp;quot;&lt;br /&gt;
       String methodName = stringTokenizer.nextToken();&lt;br /&gt;
       stringTokenizer = new StringTokenizer(methodName.trim(), &amp;quot; (&amp;quot;);&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
       methodName = stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Return callingClass.callingMethod&lt;br /&gt;
       return methodName;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The execution of this couldn't be more simple:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class UtilitiesTest {&lt;br /&gt;
    public static void main(String[] args){&lt;br /&gt;
        System.out.println(&amp;quot;Method name: &amp;quot; + Utilities.getCurrentMethodName());&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This will trivially print the method's name to the console as ''Method name: UtilitiesTest.main''. Of course, we'd rather do something more important.&lt;br /&gt;
&lt;br /&gt;
=== Get Calling Method ===&lt;br /&gt;
&lt;br /&gt;
Another, related problem, is how to get the name of the method that called the currently executing method. Something invoked the method we're concerned about, and we don't really have a Java helper for that.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Utilities {&lt;br /&gt;
   public static String getCallingMethodName() {&lt;br /&gt;
       ByteArrayOutputStream &lt;br /&gt;
           byteArrayOutputStream = new ByteArrayOutputStream();&lt;br /&gt;
       PrintWriter &lt;br /&gt;
           printWriter = new PrintWriter(byteArrayOutputStream);&lt;br /&gt;
       (new Throwable()).printStackTrace(printWriter);&lt;br /&gt;
       printWriter.flush();&lt;br /&gt;
       String stackTrace = byteArrayOutputStream.toString();&lt;br /&gt;
       printWriter.close();&lt;br /&gt;
&lt;br /&gt;
       StringTokenizer &lt;br /&gt;
           stringTokenizer = new StringTokenizer(stackTrace, &amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
       // Line 1 -- java.lang.Throwable&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Line 2 -- &amp;quot;at thisClass.thisMethod(file.java:line)&amp;quot;&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Line 3 -- &amp;quot;at callingClass.callingMethod(file.java:line)&amp;quot;&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Line 4 -- &amp;quot;at originalClass.callingMethod(file.java:line)&amp;quot;&lt;br /&gt;
       String methodName = stringTokenizer.nextToken();&lt;br /&gt;
       stringTokenizer = new StringTokenizer(methodName.trim(), &amp;quot; (&amp;quot;);&lt;br /&gt;
       stringTokenizer.nextToken();&lt;br /&gt;
       methodName = stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
       // Return originalClass.callingMethod&lt;br /&gt;
       return methodName;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Why would you want to know who called you? Perhaps your method was expecting something to have been prepared, and you want to know where to point the finger. Maybe you're abstracting a logging or error class, and you want to know who caused your action. In a logging class, for example, you don't want the name of the logging method, but the method that called the logging method.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class UtilityTest {&lt;br /&gt;
    public static void main(String[] args){&lt;br /&gt;
        System.out.println(log(&amp;quot;hello&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static String log(String message){&lt;br /&gt;
        return Utilities.getCallingMethodName() + &amp;quot; says &amp;quot; + message;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This trivial example should display ''UtilityTest.main says hello'' in the console. Note the call to '''getCallingMethodName()''' didn't return ''UtilityTest.log'' as the method we were seeking, because we wanted the method that called '''log()''', which is '''main()'''.&lt;br /&gt;
&lt;br /&gt;
Note this even works if that '''log()''' is in a different class, and, of course, has a different name. In that case, the Utility methods could even be part of the same class.&lt;br /&gt;
&lt;br /&gt;
=== Common code ===&lt;br /&gt;
&lt;br /&gt;
Of course, the above samples are drawn out to show what we expect in the stack trace. Additionally, if you're going to implement both of the above methods, we can see they have much common code that can be reduced for maintenance and readablity.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Utilities {&lt;br /&gt;
    public static String getCurrentMethodName() {&lt;br /&gt;
        return getMethodName(0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static String getCallingMethodName() {&lt;br /&gt;
        return getMethodName(1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private static String getMethodName(int stackLevel) {&lt;br /&gt;
        ByteArrayOutputStream &lt;br /&gt;
            byteArrayOutputStream = new ByteArrayOutputStream();&lt;br /&gt;
        PrintWriter printWriter = new PrintWriter(byteArrayOutputStream);&lt;br /&gt;
        (new Throwable()).printStackTrace(printWriter);&lt;br /&gt;
        printWriter.flush();&lt;br /&gt;
        String stackTrace = byteArrayOutputStream.toString();&lt;br /&gt;
        printWriter.close();&lt;br /&gt;
&lt;br /&gt;
        StringTokenizer stringTokenizer = new StringTokenizer(stackTrace, &amp;quot;\n&amp;quot;);&lt;br /&gt;
        String methodName = &amp;quot;&amp;quot;;&lt;br /&gt;
        for (int i = 0; i &amp;lt; stackLevel + 4; i++) {&lt;br /&gt;
            methodName = stringTokenizer.nextToken();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        stringTokenizer = new StringTokenizer(methodName.trim(), &amp;quot; (&amp;quot;);&lt;br /&gt;
        stringTokenizer.nextToken();&lt;br /&gt;
        methodName = stringTokenizer.nextToken();&lt;br /&gt;
&lt;br /&gt;
        return methodName;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Now we have just one terse method responsible for parsing the stack trace, giving us just one place to improve the code with cleaner parsing. You may prefer something to StringTokenizer, or you may not want the fully-qualified class name; the package will be part of the name returned. You might want the filename (inner classes are not in their own .java file) and line number, which are trimmed out at the end, there.&lt;br /&gt;
&lt;br /&gt;
== JDK 1.5 ==&lt;br /&gt;
&lt;br /&gt;
As with many other things, the new JDK 1.5 provides us with enhancements that make this easier. The previous method took some tome to execute, and generated an Exception (surely you noticed that ''new Throwable()'' got us the stack trace. This is a potentially expensive operation that could impact performace. Certainly, the benefit during debugging or concise error trapping during execution can outweigh the impact on performance, but it might be a bit too much just to make a running log of things happening. While we can't do much different in JDK 1.4, we can as we move to JDK 1.5.&lt;br /&gt;
&lt;br /&gt;
=== Thread ===&lt;br /&gt;
&lt;br /&gt;
The Thread class has been given a new method ''getStackTrace()'' that can pull the same information we got from our Throwable before. This is just information that's hanging around already, and doesn't have the impact of generating exceptions.&lt;br /&gt;
&lt;br /&gt;
The following snippet can be used in a method to get its names, both class and method. Of course, ''getClass().getName()'' will return a class' name, but that only works in instance methods; that is, you can't do that in a static method.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
StackTraceElement[] stackTraceElement = Thread.currentThread().getStackTrace();&lt;br /&gt;
String className = stackTraceElement[2].getClassName();&lt;br /&gt;
String methodName = stackTraceElement[2].getMethodName();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Or shortened, that could be one of the following.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get the class.method&lt;br /&gt;
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];&lt;br /&gt;
String name = stackTraceElement.getClassName() &lt;br /&gt;
    + &amp;quot;.&amp;quot; &lt;br /&gt;
    + stackTraceElement.getMethodName();&lt;br /&gt;
&lt;br /&gt;
// If you only care about the method&lt;br /&gt;
Thread.currentThread().getStackTrace()[2].getMethodName();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
If you wanted to move this to your utility class, as above, just bump up the element you're looking for. Here's the same Utilities class as above, written with the improved methods provided by JDK 1.5.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class Utilities {&lt;br /&gt;
    public static String getCurrentMethod() {&lt;br /&gt;
        return getCurrentMethodNameFromThread(0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static String getCallingMethodName() {&lt;br /&gt;
        return getCurrentMethodNameFromThread(1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private static String getCurrentMethodNameFromThread(int stackLevel) {&lt;br /&gt;
        /*&lt;br /&gt;
         * 0 - dumpThreads&lt;br /&gt;
         * 1 - getStackTrace&lt;br /&gt;
         * 2 - thisMethod =&amp;gt; getCurrentMethodNameFromThread&lt;br /&gt;
         * 3 - callingMethod =&amp;gt; method calling thisMethod &lt;br /&gt;
         * 4 - method calling callingMethod&lt;br /&gt;
         */&lt;br /&gt;
        StackTraceElement &lt;br /&gt;
            stackTraceElement = &lt;br /&gt;
                Thread.currentThread().getStackTrace()[4 + stackLevel];&lt;br /&gt;
&lt;br /&gt;
        String className = stackTraceElement.getClassName();&lt;br /&gt;
        String methodName = stackTraceElement.getMethodName();&lt;br /&gt;
&lt;br /&gt;
        return className + &amp;quot;.&amp;quot; + methodName;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
If you desire the file name and line number, the '''StackTraceElement''' contains methods for retrieving them as well.&lt;/div&gt;</description>
			<pubDate>Fri, 23 Sep 2005 16:46:36 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Dynamically_Retreive_Method_Name</comments>		</item>
		<item>
			<title>Tomcat</title>
			<link>http://www.softwarebyjeff.com/index.php/Tomcat</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://jakarta.apache.org/tomcat Tomcat] is the Apache project's Java JSP and Servlet server. It gives web hosts and developers a way to host programs and scripts for delivery to the web. It comes packaged and ready to handle web requests. If desired, Tomcat can be used without Apache and meet all of a web site's needs.&lt;br /&gt;
&lt;br /&gt;
The Apache web server offers a lot more than simple web page delivery. Its ability to let other scripting engines easily hook in provides it tremendous power. An Apache web server may be required to handle static web pages, simple shtml included files, PERL scripting, PHP, Python, Ruby, and Java, as well as CGI executables written in any language. Additionally, it may be required to do so for virtual domains, or as part of a server farm. The Apache web server provides an excellent front-end for Tomcat, but its setup is not as easy as it might need to be.&lt;br /&gt;
&lt;br /&gt;
This discussion is based on using source to build the system; binaries are typically available, and that simply means installing instead of building. This assumes you have a C compiler installed, and are comfortable with the '''make; install''' methods used to create software.&lt;br /&gt;
&lt;br /&gt;
The discussion is also based on a simple Apache installation; that is, no virtual domains nor multiple Apache hosts on the one server.&lt;br /&gt;
&lt;br /&gt;
First, make sure you have [http://java.sun.com Java] on your system; get a JDK--it'll be necessary for Tomcat to compile JSP pages on the fly. Then get the latest stable Apache [http://httpd.apache.org/download.cgi HTTP Server]. You'll need the [http://jakarta.apache.org/site/downloads/downloads_tomcat-connectors.cgi mod_jk] connector for Apache to know how to deliver requests to Tomcat. Then  download [http://jakarta.apache.org/site/downloads/downloads_tomcat-5.cgi Tomcat]. &lt;br /&gt;
&lt;br /&gt;
Then install each. Finally, configure each to work together.&lt;br /&gt;
&lt;br /&gt;
Make sure to get the latest stable releases. For this discussion, on this day, the latest JDK is v1.5, Apache is v2.0, mod_jk is 1.2, and Tomcat is v5.5. The minor revision numbers change too frequently, and for the purposes of our discussion don't provide enough difference to be of concern.&lt;br /&gt;
&lt;br /&gt;
== JDK ==&lt;br /&gt;
&lt;br /&gt;
It may be the case that there is a Java installation on the system already, but it's probably the Standard Environment installation--the Java runtime. It is important to install a JDK--the Java Development Kit. Fundamentally the difference is important to us only because Tomcat will need to be able to compile the JSP pages. Tomcat is written in Java, and will run on the Standard Edition. If you are not going to be hosting JSP pages, the SE install may meet your needs.&lt;br /&gt;
&lt;br /&gt;
Alternatively, the [http://jikes.sourceforge.net Jikes] project provides a just-in-time compiler alternative to the one from Sun. Since we're advocating Open Source at this site, it behooves us to mention this option. We leave it to you, for now, to download, install, and configure Jikes if you prefer that route to installing a JDK.&lt;br /&gt;
&lt;br /&gt;
The documentation continues assuming you've installed the JDK. Currently from Sun there are only binary releases of the JDK. This helps us since that means that the installation will pretty much be limited to extracting and copying files; which the installation program or script actually does for us.&lt;br /&gt;
&lt;br /&gt;
Download and extract/execute the program from Sun. Depending on the version and OS used, the program will either extract everything right in your directory, or offer to install in a directory of your choice. Accordingly, be in the right place, with the right privliges. Ensure the new installation is in your environment's PATH, and that you have the rights to use the software.&lt;br /&gt;
&lt;br /&gt;
== Apache ==&lt;br /&gt;
&lt;br /&gt;
Although binaries are available, we're going to discuss installing from source. This is done to provide us a little control over the resulting binary, including at the very least ensuring that dynamic module loading is enabled. Additionally, we may be forced to compile plug-ins and add-ons from souce, as we will with the mod_jk module we'll need to connect the web server to Tomcat.&lt;br /&gt;
&lt;br /&gt;
Download and extract the source. Configure the build using a statement at the minimum like the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;./configure --prefix=/usr/local/apache-2.0 --enable-http --enable-so&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This tells the make process to eventually install the program in the /usr/local/apache-2.0 directory; techincally this is not required, but we want to know where it goes--the default installs everything in /usr/local, potentially clouding this installation with the installation of past or future releases. The program will have compiled-in capabilities to handle HTTP requests, and the ability to dynamically load DSO modules. This is the barest-bones configuration we can live with. &lt;br /&gt;
&lt;br /&gt;
For a few more friendly features let's expand our installation with the following configuration line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;./configure --prefix=/usr/local/apache-2.0 --enable-http --enable-so --enable-dav --enable-speling --enable-cgi --enable-rewrite&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This gives us Apache's built-in ability to do WebDAV and to correct minor URI errors.  The ability to use other CGI programs is enabled, giving us the ability to run pre-compiled programs and compliant scripts. Additionally, we've enabled URI re-writing, which we may want to &amp;quot;spoof&amp;quot; incoming requests when we cannot just easily map them.&lt;br /&gt;
&lt;br /&gt;
Do the normal '''make; make install''' process and the installation will finish with everything in /usr/local/apache-2.0 and almost ready to use.&lt;br /&gt;
&lt;br /&gt;
=== mod_jk ===&lt;br /&gt;
&lt;br /&gt;
Arguably, installing the mod_jk before we install Tomcat seems fruitless. It is true that the settings between the two must mesh, but really the mod_jk has more to do with Apache than with Tomcat, so we install it at the same time. Additionally, it is the case that mod_jk will work without a Tomcat installed; nothing will happen, but it will work.&lt;br /&gt;
&lt;br /&gt;
There are contributed binaries of mod_jk installed, but they're not only not endorsed by Apache, but the use of those binaries removes our ability to control them.&lt;br /&gt;
&lt;br /&gt;
Download and extract the v1.2 mod_jk source. There was a movement to create a v2, but that has since been abandoned. The features promised in v2 were not delivered, and maintenance on v1.2 is much more current. Curiously, the protocol used is '''ajp13''', so don't confuse that protocol name with the version number of the module that makes it available.&lt;br /&gt;
&lt;br /&gt;
Building is as simple as downloading, extracting, building, and adding the finished mod_jk library (.dll on Windows .so on other OSs) to the Apache modules directory.&lt;br /&gt;
&lt;br /&gt;
Configuring mod_jk before installing Tomcat will simply result in &amp;quot;500 Server Error&amp;quot; messages when a mapping use is attempted. In fact, this will happen any time Apache is running but Tomcat is not.&lt;br /&gt;
&lt;br /&gt;
=== httpd.conf ===&lt;br /&gt;
&lt;br /&gt;
Configuration of mod_jk requires editing the Apache's httpd.conf file. This is usually located in the Apache installation's conf directory, but on some OSs may be installed in /etc/apache instead. Find and add lines like the following near the other LoadModules. The module needs to be loaded before it can be used, of course, but has no other dependencies so it can be anywhere in the LoadModules list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;IfModule !mod_jk.c&amp;gt;&lt;br /&gt;
  LoadModule jk_module modules/mod_jk.so&lt;br /&gt;
&amp;lt;/IfModule&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then add a mapping so that Apache knows when to send things to Tomcat. These details can be anywhere in the httpd.conf file, after the LoadModule above, and is often placed immediately following; keep the IfModule block, though, in case a failure occured in trying to load the module.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;IfModule !mod_jk.c&amp;gt;&lt;br /&gt;
  JkWorkersFile modules/workers.properties&lt;br /&gt;
  JkMountFile modules/uriworkermap.properties  &lt;br /&gt;
&amp;lt;/IfModule&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other options are available to control the placement of log files and whatnot, but we'll leave that to the industrious reader to add into this section.&lt;br /&gt;
&lt;br /&gt;
The first line, '''JkWorkersFile''', sets the workers file that will contain the configuration for workers. Workers are simply the configuration items that Apache will use to communicate with a Tomcat server. Apache can talk to one or more local or remote Tomcat servers, and can even be configured to load balance these worker connections.&lt;br /&gt;
&lt;br /&gt;
The second line, '''JkMountFile''', sets the file in which the URL mappings will occur.&lt;br /&gt;
&lt;br /&gt;
Note that you can provide the mappings contained in these files within the Apache httpd.conf, but by having them external, they can be altered and automatically reloaded by the mod_jk module without restarting Apache!&lt;br /&gt;
&lt;br /&gt;
=== workers.properties ===&lt;br /&gt;
&lt;br /&gt;
Make a workers.properties file that will hold the specifics for your workers. Put the file in the location specified in the httpd.conf file. Workers are tied to specific Tomcat instances, and can therefore be configured to direct work to different machines or even different Tomcat installations or host settings on the same machine.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
worker.list=tomcat&lt;br /&gt;
worker.tomcat.port=8009&lt;br /&gt;
worker.tomcat.host=localhost&lt;br /&gt;
worker.tomcat.type=ajp13&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This configures a worker named ''tomcat'' (the name can be anything you want) to which Apache will forward requests to the host (localhost) and port (8009) using the protocol type (ajp13) as specified; these can indicate another machine or port, but use the ajp13 protocol. Other workers can be added by simply adding them (comma delimited) to the worker.list property, and adding the sections (worker.''name''.) as appropriate. The name needs to match the one to which work is sent indicated on the httpd.conf '''JkMount'''.&lt;br /&gt;
&lt;br /&gt;
=== uriworkermap.properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*.jsp=tomcat&lt;br /&gt;
/servlets/*=tomcat&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first mount is easy and obvious; we want our JSP scripts interpreted by Tomcat, not served as text by Apache. Nothing more than JSP will be handled because of this mount, and any JSP request on any path will be sent to Tomcat with this mount directive.&lt;br /&gt;
&lt;br /&gt;
The second mount causes some concern and a little more care. Any request sent to the server's /servlets/ directory (e.g., http://your.server/servlets/foo) will be sent to the Tomcat server as specified in the workers.properties file. This includes /servlets/image.gif as well as /servlets/page.jsp and /servlets/servletClass. The configuration of what happens with the request is inside Tomcat, not inside Apache, so be careful when expecting other resources like images to be in the same path.&lt;br /&gt;
&lt;br /&gt;
== Tomcat ==&lt;br /&gt;
&lt;br /&gt;
Installation of Tomcat is the easiest part of all of the installations necessary. Building Tomcat from source is a painful process, and since it's all Java and not C, we like the binary installation. Simply download the latest, and unpack it. The archive typically creates a ''tomcat'' directory; our habit is to rename that ''tomcat-version'' as appropriate, and if possible, make a symlink using ''tomcat''. The directory into which this is installed is referred throughout the Tomcat documentation as ''$TOMCAT_HOME''; therefore, if you installed this into /usr/local/tomcat, that is what they mean by $TOMCAT_HOME.&lt;br /&gt;
&lt;br /&gt;
Inside the Tomcat directory, we're concerned mostly with the conf directory. We're going to make sure that AJP is enabled, and configured to match the Apache configuration. Additionally, we'll make sure our server is configured to handle our software where we want it to happen.&lt;br /&gt;
&lt;br /&gt;
=== AJP ===&lt;br /&gt;
&lt;br /&gt;
Inside the server.xml of the Tomcat conf directory is a line not unlike the following. Very simply ensure that it is not within an XML comment (generally it is not, but we want to be sure), and that the port matches that which was configured in the mod_jk file. This can be any port, as long as it's unique from other ports in use on the system, but typically the default of 8009 is sufficient.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;Connector port=&amp;quot;8009&amp;quot; enableLookups=&amp;quot;false&amp;quot; redirectPort=&amp;quot;8443&amp;quot; protocol=&amp;quot;AJP/1.3&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's it. The Tomcat can now handle AJP calls from Apache servers with mod_jk installed.&lt;br /&gt;
&lt;br /&gt;
=== Host ===&lt;br /&gt;
&lt;br /&gt;
For our simple setup, we want to ensure that the Host is correctly configured, too. A Host is Tomcat's way of relating requests to the file system. Here's the default Host from a freshly unpacked Tomcat installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      &amp;lt;Host name=&amp;quot;localhost&amp;quot; appBase=&amp;quot;webapps&amp;quot;&lt;br /&gt;
       unpackWARs=&amp;quot;true&amp;quot; autoDeploy=&amp;quot;true&amp;quot;&lt;br /&gt;
       xmlValidation=&amp;quot;false&amp;quot; xmlNamespaceAware=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that the Tomcat server will look in its '''webapps''' directory for the contents of what it will serve. If this is acceptable, leave it alone. If you wish to have your applications in a directory outside of the Tomcat directory structure, which we recommend, change the '''appBase''' attribute to the absolute path of your applications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      &amp;lt;Host name=&amp;quot;localhost&amp;quot; appBase=&amp;quot;/var/tomcat/webapps&amp;quot;&lt;br /&gt;
       unpackWARs=&amp;quot;true&amp;quot; autoDeploy=&amp;quot;true&amp;quot;&lt;br /&gt;
       xmlValidation=&amp;quot;false&amp;quot; xmlNamespaceAware=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this manner, we simply need to make sure to make the change to the '''appBase''' attribute whenever we update Tomcat, and the web apps take care of themselves.&lt;br /&gt;
&lt;br /&gt;
Note that we've left the '''unpackWARs''' and '''autoDeploy''' both set to ''true''. This allows us to simply deploy applications by putting new WAR files in the directory, or &lt;br /&gt;
&lt;br /&gt;
=== Context ===&lt;br /&gt;
&lt;br /&gt;
The idea of contexts takes a little getting used to. A &amp;quot;context&amp;quot; is what Tomcat calls an application; it is essentially all of the goods within a directory that make a thing tick. Typically within a context directory you'd have at least a ''WEB-INF'' directory and the JSP pages that are required for the application to run, in whatever directory structure makes sense; the compiled classes and libraries and such go into the WEB-INF directory of the context.&lt;br /&gt;
&lt;br /&gt;
Simply, any directory under the ''webapps'' directory defined in the Host ''appBase'' that contains a WEB-INF directory is a context. Note that the ''webapps'' directory can itself be a context (the root) by having a WEB-INF directory in it. And contexts can contain contexts; the magic appearance of the WEB-INF directory spawns a context from that directory on. That way you can have webapps/app1 and webapps/app2 as wholly separate appliactions, each with their own resources, server as http://server.name/servlets/app1 and http://server.name/servlets/app2, given our previous Apache mod_jk discussion.&lt;br /&gt;
&lt;br /&gt;
''We'll touch on the XML way to define contexts in a later edit...''&lt;br /&gt;
&lt;br /&gt;
=== Other Tips ===&lt;br /&gt;
&lt;br /&gt;
Ten things you should do to your Tomcat installation: [http://www.onjava.com/pub/a/onjava/2003/06/25/tomcat_tips.html onjava.com]&lt;/div&gt;</description>
			<pubDate>Thu, 28 Jul 2005 17:42:20 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Tomcat</comments>		</item>
		<item>
			<title>Thin Clients</title>
			<link>http://www.softwarebyjeff.com/index.php/Thin_Clients</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;For most users and networks, a PC on the desk is the ideal. In far too many environments, this is the norm, even if it isn't ideal. In a lot of cases, the PC on the desk is going to relative waste. It may be a waste of resources in terms of the cost of the system, or in terms of the idle and underused computing power or drive space.&lt;br /&gt;
&lt;br /&gt;
Why isn't there a system out there that can use all of that idle computing power, and make the office into a &amp;quot;super comptuer&amp;quot;?&lt;/div&gt;</description>
			<pubDate>Thu, 28 Jul 2005 17:02:05 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Thin_Clients</comments>		</item>
		<item>
			<title>Best LINUX Distribution</title>
			<link>http://www.softwarebyjeff.com/index.php/Best_LINUX_Distribution</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Picking the best distribution really depends on what you use to quantify &amp;quot;best.&amp;quot; This is related heavily to your objectives, environment, skill level, and desire to maintain the system with more control or more ease.&lt;br /&gt;
&lt;br /&gt;
== Ubuntu ==&lt;br /&gt;
&lt;br /&gt;
[http://ubuntu.org Ubuntu] is a distribution based on the cutting-edge code that is the [http://debian.org Debian] distribution. Also based on Debian is [http://knoppix.net Knoppix], one of the first successful &amp;quot;live&amp;quot; versions that would run from CD without impacting anything previously installed on your system. For the purposes of our discussion, these distributions are the same. Debian is a free, community-backed distribution of LINUX that usually rides the cutting-edge of what's available, taking very close to the latest sources in their releases.&lt;br /&gt;
&lt;br /&gt;
Debian distributions come with an fairly easy to use package management system, which is why it's high on my list. I put Ubuntu over the raw Debian, only because they have a more aggressive release plan, and a flurry of new support. With that wave of activity comes rapid improvement.&lt;br /&gt;
&lt;br /&gt;
Ubuntu has a very large repository of packages that is maintained and &amp;quot;approved&amp;quot; by the Ubuntu community. It also has an even larger repository that is maintained by the LINUX community at large, that provides nearly every allowable software package. The upside to the repositories is that you're fairly free from having to worry about licensing conflicts, as this is one of the things they review and maintain for you. Most LINUX software is released under one of a few flexible licenses that largely proclaim that you can use the software without fee as long as you abide by some very simple rules if you choose to redistribute the software. If you're installing the software for your use, these simple rules do not even apply, in most cases.&lt;br /&gt;
&lt;br /&gt;
Additionally, the Ubuntu distribution is available on a live CD or DVD, and that comes with plenty of software to turn any grumpy PC into an adequate LINUX workstation for simple needs. It's an excellent way to get started to see if LINUX might meet your needs, as well as to determine if your PC can handle it (some drivers and systems have trouble--usually because the distribution doesn't contain them, not because they don't exist); the Ubuntu live CD comes with a huge range of drivers.&lt;br /&gt;
&lt;br /&gt;
== SuSE ==&lt;br /&gt;
&lt;br /&gt;
I do like, and actually recommend Ubuntu for Windows converts. On my desk, however, I use [http://suse.org SuSE]. SuSE is a distribution from Germany, that is now owned or marketed by Novell. Novell is known for their file server operating system, which is far from open, that lingers still although arguably more functionality is available from Microsoft Windows and the various UNIX derivatives. The SuSE distribution comes with as much selection as the Debian distributions. They're released fairly quickly, although sometimes the cutting-edge releases aren't available in the mainstream, and finding how to add the more up-to-date repositories is a bit tricky. One of these days I'll document it.&lt;br /&gt;
&lt;br /&gt;
SuSE also has a LiveCD that contains a very wide variety of drivers. In fact, in the release-before-last, I had to use the LiveCD to determine which audio driver I needed to use to configure my system; while the versions were the same, the install wizard didn't choose the same one, and the one chosen by the install wizard didn't work while the LiveCD did. Go figure. This hasn't happened in any installation of the latest version (9.3 Professional).&lt;br /&gt;
&lt;br /&gt;
== RedHat ==&lt;br /&gt;
&lt;br /&gt;
Previously I had great success with [http://redhat.com RedHat]. It's changed so that now all RedHat releases are commercial. They do have an open source version, [http://redhat.com/fedora Fedora], that they use as a test bed for fixes and enhancements. As such, Fedora tends to be a little ahead when compared to RedHat, but RedHat offers a touch more stability, and since it's commercial, there is support available.&lt;br /&gt;
&lt;br /&gt;
The trouble with all of these distributions is that you either need to use the releases of software that they prepare and offer. Sometimes these releases can be a version or two back, forcing you to work without the latest fix or enhancement, or to go outside their distribution and get a version compiled by someone else, or to build it yourself.&lt;br /&gt;
&lt;br /&gt;
As a simple example, [http://mozilla.org Mozilla] recently released version 1.0.5 of their FireFox web browser. It has some security fixes in it that help stop malicious websites from harming your PC. The latest version I was able to get from SuSE was 1.0.3. Two whole releases back. I downloaded and installed from source, but now I'm outside their package manager for that software. It'll be the case that when they release 1.0.4 that it'll either try to &amp;quot;update&amp;quot; and incorrectly replace 1.0.5, or it'll not try at all, and should I not notice the release of later software it won't either.&lt;br /&gt;
&lt;br /&gt;
== Gentoo ==&lt;br /&gt;
&lt;br /&gt;
For a closer to the core distribution, try [http://gentoo.org Gentoo], which installs a bootstrap installation of LINUX, and then downloads and builds the entire system from source. As much as I'm able to work with all of the other software, I couldn't get Gentoo to like my PC. I could always get the software to install, and the system to boot, but I couldn't get a GUI up and running. For a server I might be satisfied with this, but not for my desktop. I like a good GUI (I'm a [http://kde.org KDE] guy...but I can work just fine in [http://gnome.org Gnome]), allowing me to flip between processes and applications as my work mandates. I'm sure with a little more tinkering I could get it to work. It's probably related to my nVidia graphics card which gives me pain even with Ubuntu and SuSE.&lt;br /&gt;
&lt;br /&gt;
== Raw Source ==&lt;br /&gt;
&lt;br /&gt;
If you're looking to get really close to the core, just go get the LINUX [http://www.kernel.org kernel] direct from the source, and add the tools you need from the main [http://gnu.org GNU] repository.&lt;br /&gt;
&lt;br /&gt;
The [http://linuxfromscratch.org LINUX From Scratch] project outlines how to do this yourself. It helps you set up your own bootstrap installation of LINUX (you have to start from something running, even a LiveCD from another distribution), and discusses how to optimize the system to your liking.&lt;br /&gt;
&lt;br /&gt;
== BSD ==&lt;br /&gt;
&lt;br /&gt;
While the discussion until now has been strictly LINUX, the idea of free and open operating systems and environments is certainly not limited to the LINUX community. Originally licensed straight from the AT&amp;amp;T Labs where UNIX was developed, [http://bsd.org BSD], or Berkeley Software Design, was a commercial version of that foundation. I haven't kept on top of it, but I believe the core of the work done at BSD is now on the free version, which used to feed the commercial version released as BSDI. Regardless, unlike LINUX, which was sort of reverse engineered from UNIX, BSD was licenced and ported directly from the UNIX root.&lt;br /&gt;
&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
&lt;br /&gt;
The free version of the BSD software is the appropriately named [http://freebsd.org FreeBSD], a distribution of open source software that continues straight from those roots. It was completely rewritten some time ago, and is completely separate from the Bell Labs UNIX from which it spawned. It's open, free, and just as full-featured as any LINUX distribution. In fact, it not only will compile all of the GNU software, and most software targeted directly to LINUX, but it will run most pre-compiled LINUX binaries. Like the plentiful choices of LINUX distributions, there are plenty of forks, derivations, and branches of BSD software, too, including [http://openbsd.org OpenBSD] and [http://netbsd.org NetBSD], both of which portend to be more secure, while remaining open. &lt;br /&gt;
&lt;br /&gt;
The package management on the BSD distribution is done through what they call the &amp;quot;ports&amp;quot; system. This is a system that grabs the latest reported source and then compiles it on your system. The slight trouble is if the reported version isn't the latest, but this compromise is handy because you could actually change the reported version in the port, if you find it's not up-to-date. Later, the ports package management system will update it, and it will eventually match or exceed the version you're working with.&lt;br /&gt;
&lt;br /&gt;
=== MacOS/Darwin ===&lt;br /&gt;
&lt;br /&gt;
One fork of BSD that is more plentiful than most people realize is the Mac OS. The newest versions of Mac OS use an OS named [http://developer.apple.com/darwin/ Darwin], whose open source version is OpenDarwin (now defunct, since replaced with [http://puredarwin.org PureDarwin]?). Sadly, the Mac look-and-feel interface, Coacoa, is not available as an open source release; they're still holding on to the software over the machine. The interface aside, the opening of the underlying operating system offers Apple the opportunity to similarly benefit from the open source's community, just like any other BSD or LINUX distribution.&lt;br /&gt;
&lt;br /&gt;
== Solaris ==&lt;br /&gt;
&lt;br /&gt;
Latest in the open source game is [http://www.sun.com/software/solaris/ Sun Solaris]. While for some time the Solaris OS has been available for &amp;quot;free,&amp;quot; with some constraints, it has not been open and truly free. Recently they've launched a totally free and open version of the same software aptly named [http://opensolaris.org OpenSolaris]. This release offers tremendous opportunity for those looking for a rock-solid system on which they can blend the security and features offered by Sun with the flexibility and selection offered by the LINUX community.&lt;/div&gt;</description>
			<pubDate>Tue, 26 Jul 2005 16:41:48 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Best_LINUX_Distribution</comments>		</item>
		<item>
			<title>SSH</title>
			<link>http://www.softwarebyjeff.com/index.php/SSH</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Using SSH has all kinds of benefits. You can tunnel through firewalls to do all manner of helpful things. (Of course, we don't approve of malicious things.)&lt;br /&gt;
&lt;br /&gt;
When you're on your local machine, and you want to get to a remote machine, and you type '''ssh user@hostname.domain.ext''' it can be annoying to get prompted for a password, especially if it's a host you use a lot.&lt;br /&gt;
&lt;br /&gt;
SSH has easy self-authentication using encrypted keys. This isn't a discussion of SSH encryption, PGP, or anything like that. It's just a step-by-step on how to get your server set up to accept your client's connection without a password.&lt;br /&gt;
&lt;br /&gt;
This can also help when using RSH commands as they piggy-back on SSH and with this authentication you can do '''rcp filename user@host.domain.ext:/path/file''' and the password-less authentication will protect you from the annoyance of the password.&lt;br /&gt;
&lt;br /&gt;
== Assumption ==&lt;br /&gt;
&lt;br /&gt;
First, we assume you're using some native SSH client and a common SSH server. The program and directory names may be different for you, depending on your specific client and server. Generally this works for Mac, LINUX, Solaris, BSD, and Cygwin with little modification. Windows with programs like PuTTY or other command-line SSH might work differently, and that won't be addressed on this first pass.&lt;br /&gt;
&lt;br /&gt;
Additionally, we assume you're authorized on the machine you're trying to do this to. Once you get this set up, you can use the same client key no matter which client; we don't suggest this approach, and we'll note why when we get to that bit. You will need to SSH into the server as part of the process, so this is a safe assumption.&lt;br /&gt;
&lt;br /&gt;
If you can SSH using '''ssh user@hostname.domain.ext''' at a command prompt, with your remote username, to the correct host (not the fictitous one), our assumptions will be pretty well met.&lt;br /&gt;
&lt;br /&gt;
== Client Setup ==&lt;br /&gt;
&lt;br /&gt;
In your favorite shell window, enter the command '''ssh-keygen -t rsa -C user@localhost.domain.ext''' using your local username and fully-qualified system name. &lt;br /&gt;
&lt;br /&gt;
Technically, the '''-C comment''' is not required, and will default to your username@hostname. We recommend using the -C with the fully-qualified domain name as some systems don't provide the full domain name with the hostname. This can be a bother if you move to multiple locations and end up with the same computer name; for example, the default setup of Ubuntu tries to name the system ''ubuntu''--which can be a bother if you do this at home and at the office and don't think about accessing a third machine with this method.&lt;br /&gt;
&lt;br /&gt;
While the comment doesn't technically need to be different for each client from which you'll use, it does help when it comes time to clean up the server and disallow the access from that client.&lt;br /&gt;
&lt;br /&gt;
The ssh-keygen program will prompt you for a file location; typically the location it suggests is the one that SSH will use when it looks for your side of the key-pair. If you have other reasons for moving the directory, do so, but usually the default will suffice.&lt;br /&gt;
&lt;br /&gt;
The next thing ssh-keygen does is prompt you for a passphrase. This is where things get tricky. You can provide a passphrase, but then every time you use the key you'll have to provide it to allow SSH to decrypt the key. Not much different than providing a password. It's a little more convenient, however, than trying to keep all of your passwords synchronized; if you have different passwords on different systems, you can generate keys with the same passphrase, therein reducing what you have to remember.&lt;br /&gt;
&lt;br /&gt;
Since this is a discussion of how to use SSH without a password, don't use a passphrase.&lt;br /&gt;
&lt;br /&gt;
Confirm your passphrase (or lack of one), and ssh-keygen will create two files for you. In your '''~/.ssh''' directory you'll find something like '''id_rsa''' and '''id_rsa.pub''' have been created. They are the two key-pairs necessary to authenticate you using the SSH authentication.&lt;br /&gt;
&lt;br /&gt;
That's it for the client side requirements. Leave the '''~/.ssh/id_rsa''' file alone. Depending on how you want to propogate the ''id_rsa.pub'' key you might want to put it in other locations; copy that file, don't move it. If you lose the file you'll have to recreate and redistribute your keys.&lt;br /&gt;
&lt;br /&gt;
== Server Configuration ==&lt;br /&gt;
&lt;br /&gt;
The '''~/.ssh/id_rsa.pub''' key is a simple one-line file of gibberish. Here's one generated for the article (and trimmed for further obscurity and readability):&lt;br /&gt;
&lt;br /&gt;
'''ssh-rsa AAAAB3...1yAuy4gC28...nhYH...3t/tqGPqS+...xhPuVOZ8= user@host'''  &lt;br /&gt;
&lt;br /&gt;
Whee!&lt;br /&gt;
&lt;br /&gt;
This one line of text needs to end up on the server in your '''~/.ssh/authorized_keys''' file. &lt;br /&gt;
&lt;br /&gt;
Use whatever method you find comfortable for moving files and text between systems, and whatever editor you like. Here's a quickie three step way to do it in one command window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
'''cat ~/.ssh/id_rsa.pub''' Select and copy into your system's buffer the whole line (on most X systems, highlighting it can be enough--KDE and Gnome allow you to right-click and copy, like Windows and Mac do).&amp;lt;br&amp;gt;&lt;br /&gt;
'''ssh user@host''' Login to your remote system.&amp;lt;br&amp;gt;&lt;br /&gt;
'''vim ~/.ssh/authorized_keys''' Edit the file on the remote system, paste the line to a new line in the file (X allows middle-click--for the others, right-click and paste), saving the file when you're done.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure that the pasted line is the same that you copied, and that you didn't paste something else from your clipboard. Also make sure the line is actually one line in the resulting file, or the key won't be recognized by the server.&lt;br /&gt;
&lt;br /&gt;
That's it. Test it by disconnecting and issuing the '''ssh user@host''' again. You should bypass the prompt for a password.&lt;br /&gt;
&lt;br /&gt;
=== Alternative Method ===&lt;br /&gt;
&lt;br /&gt;
The ''id_rsa.pub'' file is safe to move around. If you put the file in a convenient to retreive location (i.e., served by a web server, or on a system supporting remote copy), you can prepare a server without the cutting and pasting.&lt;br /&gt;
&lt;br /&gt;
Something like this would work, too:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
'''cd ~''' Do NOT go to the ''.ssh'' directory!&amp;lt;br&amp;gt;&lt;br /&gt;
'''wget http://keystoreserver/~username/id_rsa.pub''' Get the remotely hosted key. Also ''rcp user@remoteclient:/home/.ssh/id_rsa.pub .'' works, if the system's exposed (many firewalls stop rcp).&amp;lt;br&amp;gt;&lt;br /&gt;
'''cat id_rsa.pub &amp;gt;&amp;gt; ~/.ssh/authorized_keys''' Add the line to the correct file.&amp;lt;br&amp;gt;&lt;br /&gt;
'''rm id_rsa.pub''' Stay tidy...&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is an excellent way of configuring a server as an afterthought. You've already logged into the server, probably for the too-many-th time in one sitting, and think to do it. Rather than log out, copy the text, and log back in to edit, just do the bits above, and the next time you connect you'll be password-free.&lt;br /&gt;
&lt;br /&gt;
== Removing authentication ==&lt;br /&gt;
&lt;br /&gt;
When you're done using a system, it's a good idea to remove the keys that allow passwordless authentication. Once you've established the authentication, it doesn't matter what the password becomes on the remote system; the keys provide a way around that authentication.&lt;br /&gt;
&lt;br /&gt;
On the server, simply find the appropriate line in the '''~/.ssh/authorized_keys''' file and remove it. From that moment forward, that system will require a password again. This is a must-do!&lt;br /&gt;
&lt;br /&gt;
On the client, simply remove or recreate the '''~/.ssh/id_rsa''' file. Note that this only works for that system. &lt;br /&gt;
&lt;br /&gt;
== Security Gotcha ==&lt;br /&gt;
&lt;br /&gt;
As mentioned, you can use the same key-pair for multiple systems; people do it 'cause they're too lazy to generate keys for different systems.&lt;br /&gt;
&lt;br /&gt;
You'll note that the ''id_rsa'' file is protected so that only the user that created it can access it; keep it that way. Don't share this file! If someone were to get your ''id_rsa'' file, they would be able to connect to your servers, as you, without knowing your password. &lt;br /&gt;
&lt;br /&gt;
The idea is to copy the information in the ''id_rsa.pub'' to multiple servers, not move the ''id_rsa'' to other systems. This does work, but it is poor management.&lt;br /&gt;
&lt;br /&gt;
Make sure to remove the key-pair line from the ''authorized_keys'' when you're done with the server, and it will render all copies of the ''id_rsa'' file useless.&lt;br /&gt;
&lt;br /&gt;
== SSH Differences ==&lt;br /&gt;
&lt;br /&gt;
If you use SSH2 on your server or client, the file you need to modify on the server might be '''~/.ssh/authorized_keys2''' or even the '''~/.ssh''' directory might be '''~/.ssh2''' depending on the installation of SSH on the system.&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
That's about it. Using the simple steps of ssh-keygen and a little text file editing you can seamlessly move from machine to machine.&lt;br /&gt;
&lt;br /&gt;
Now set up an alias (if you use Bash) like '''alias hostname='ssh user@hostname.domain.ext' '''and you'll be able to do it in one word! Of course, don't use ''hostname'' but instead the name of the server to which you connect...'''hostname''' is already useful...&lt;/div&gt;</description>
			<pubDate>Fri, 03 Jun 2005 17:23:42 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:SSH</comments>		</item>
		<item>
			<title>Unnecessary Conditions</title>
			<link>http://www.softwarebyjeff.com/index.php/Unnecessary_Conditions</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The following is an exact copy of Visual C++ code pulled from a real project. Only the class name (CMyDlg) has been changed to protect the innocent; the client didn't write the code, but they have this kind of bad code in their source:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void CMyDlg::OnFileStartJob()&lt;br /&gt;
{&lt;br /&gt;
	if ( !StartJob(NULL) )  //Added if and return VJH V1.3 &lt;br /&gt;
		return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you're the VJH who wrote this, shame on you.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;What's wrong with this?&amp;quot; you may ask. Obviously it's valid C++ code; it compiles and runs.&lt;br /&gt;
&lt;br /&gt;
First, we can assume from this bit of code that StartJob() is returning something we're treating as boolean. We call StartJob() and validate its return. Fair enough.&lt;br /&gt;
&lt;br /&gt;
When it returns, all we do is also return. So if StartJob() returns false or some value that evaluates to zero we return because of the return statement. If StartJob() returns true or some non-zero value we skip the return statement, and then return because we're done anyway.&lt;br /&gt;
&lt;br /&gt;
So, why bother with the if()? There is no good reason. Really. There is no good reason.&lt;br /&gt;
&lt;br /&gt;
Let's look at what the code should be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void CMyDlg::OnFileStartJob()&lt;br /&gt;
{&lt;br /&gt;
	StartJob(NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Woah! Much better. Yeah, the comment could have remained, but really, since it attempted to explain the if(), there's no reason to keep it.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Why bother even having OnFileStartJob() call StartJob()?&amp;quot; you may ask. Well, the NULL parameter for one; it may (nay, better) be the case that StartJob() is called somewhere else with a different parameter list. In that case, having our method (which looks like, and actually is, the action taken because of a menu selection or button activiation) wrap the call is perfectly valid. We assume, then, that the other calls fill the parameter list with something appropriate for that call.&lt;br /&gt;
&lt;br /&gt;
If, however, this is the only place that StartJob() is called, then the entirety of StartJob() should be in OnFileStartJob(). There are asthetically valid reasons to call the one from the other, but no technically good reasons (that is, no reason to call a method just because you can). Perhaps StartJob() is really long and convoluted and therefore kept in its own .cpp file Fair enough. If this is the case, however, loose the parameter list.&lt;br /&gt;
&lt;br /&gt;
Remember: never write more than is necessary. Someone will curse your name for the Internet to see...&lt;/div&gt;</description>
			<pubDate>Thu, 28 Apr 2005 20:34:07 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Unnecessary_Conditions</comments>		</item>
		<item>
			<title>Stupid code</title>
			<link>http://www.softwarebyjeff.com/index.php/Stupid_code</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A large part of what is done here is software development. Much of that is modifying or maintaining software written by others. That is to say that by the time I got to the code, it had been modified, and quite possibly not by the original author.&lt;br /&gt;
&lt;br /&gt;
While negativity is discouraged, venting is sometimes required. Perhaps seeing some examples and explanations will help someone else write more intelligent code.&lt;br /&gt;
&lt;br /&gt;
[[Unnecessary Conditions]]&lt;br /&gt;
&lt;br /&gt;
[[Unnecessary Assignments]]&lt;br /&gt;
&lt;br /&gt;
[[Unnecessary Try Catch]]&lt;/div&gt;</description>
			<pubDate>Thu, 28 Apr 2005 20:20:19 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Stupid_code</comments>		</item>
		<item>
			<title>Simple unit testing</title>
			<link>http://www.softwarebyjeff.com/index.php/Simple_unit_testing</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So you recognize the value of testing your code, but you don't want to go through the rigors of learning [[http://junit.org JUnit]] or another testing engine. You're comfortable with making whatever your environment needs in the way of object set-up and calling the right methods. You still want a fairly automated way to run the tests, perhaps even adding tests without having to remember to put the test method in some stream of calls.&lt;br /&gt;
&lt;br /&gt;
Try something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class MyTest {&lt;br /&gt;
    public static void main(String[] args) {&lt;br /&gt;
        MyTest myTest = new MyTest();&lt;br /&gt;
&lt;br /&gt;
        Method[] methods = myTest.getClass().getMethods();&lt;br /&gt;
        for (int i = 0; i &amp;lt; methods.length; i++) {&lt;br /&gt;
            Method method = methods[i];&lt;br /&gt;
            if (Modifier.isPublic(method.getModifiers()) &amp;amp;&amp;amp; method.getName().startsWith(&amp;quot;test&amp;quot;)) {&lt;br /&gt;
                try {&lt;br /&gt;
                    method.invoke(myTest, null);&lt;br /&gt;
                } catch (Exception e) {&lt;br /&gt;
                    System.err.println(&amp;quot;Method &amp;quot; + method.getName() + &amp;quot; failed:&amp;quot;);&lt;br /&gt;
                    e.getCause().printStackTrace();&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void testThatFails() throws Exception {&lt;br /&gt;
        throw new Exception(&amp;quot;FAIL&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It's a very primative class, I'll grant you that. But it's got a little ease and elegance, thanks to our good friend Java Reflection.&lt;br /&gt;
&lt;br /&gt;
This simple MyTest class contains one test method, aptly named testThatFails. The class has a main method that lets us run it as an application. It requires no additional classes or libraries as it stands.&lt;br /&gt;
&lt;br /&gt;
Simply adding a public, parameterless method, whose name begins with &amp;quot;test&amp;quot; will be sufficient to ensure that the test is run the next time this class is executed.&lt;br /&gt;
&lt;br /&gt;
You can make those methods do anything that you're comfortable with. The only key is to throw an execption, write a log, or pop-up a dialog box when you error...whatever makes you comfortable. The simple loop will capture and dump all Exceptions and dump them to the error console.&lt;br /&gt;
&lt;br /&gt;
Of course, remove the testThatFails method, or you'll always have a failure.&lt;br /&gt;
&lt;br /&gt;
Easy.&lt;/div&gt;</description>
			<pubDate>Fri, 01 Apr 2005 18:55:28 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Simple_unit_testing</comments>		</item>
		<item>
			<title>Setting Environment Variables in Bash</title>
			<link>http://www.softwarebyjeff.com/index.php/Setting_Environment_Variables_in_Bash</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A work in progress.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
The Network File System, or NFS, is one of the things that should have been pulling the UNIX-like operating environments ahead for some time. Sadly, it's a tricky beast to tame, and its implementation isn't always so rock-solid.&lt;br /&gt;
&lt;br /&gt;
== Comparing to Windows ==&lt;br /&gt;
&lt;br /&gt;
Of course, we're not trying to position Windows as the environment to follow. We use it here only as the system that works differently than the others, and one with which most readers are familiar (market share has benefits). Solaris, BSD, LINUX, Mac, AIX, and the real UNIX and others all have NFS support. For the most part they interact well, too.&lt;br /&gt;
&lt;br /&gt;
In fact, Windows is lacking in this area, and has to rely on Samba to connect to those other environments without additional software on the Windows systems. This is not discussed here, as the focus of this documentation is to get two NFS-friendly systems behaving together (one server, one client). Once we get through that, adding more clients or servers should be trivial.&lt;br /&gt;
&lt;br /&gt;
== Only Root Allowed ==&lt;br /&gt;
&lt;br /&gt;
One major obstacle is that only root can mount resources on most systems. In small groups this might not be a problem, as users can be trusted with root access. &lt;br /&gt;
&lt;br /&gt;
Contrary to this, Microsoft Windows allows users to connect to shares as long as that user has permission on the remote system. &lt;br /&gt;
&lt;br /&gt;
Similarly, only root can share directories to be mounted. This can be worked around by allowing mounting of subdirectories, but some security is risked.&lt;br /&gt;
&lt;br /&gt;
Again, Windows allows almost anyone to set up a share, and then there are permissions per-share and within the directory structure shared.&lt;br /&gt;
&lt;br /&gt;
=== How to get around this? ===&lt;br /&gt;
&lt;br /&gt;
The easiest solution is to have root mount the directory, but specify it as user mounted. When the user accesses the share their permissions will be compared to those of the user with the same ID on the server. What?&lt;br /&gt;
&lt;br /&gt;
== Automounting By User Is Difficult ==&lt;br /&gt;
&lt;br /&gt;
Similar to only allowing root to mount is the issue of per-user mounting.  Using centralized authentication helps. To be fair, this is often required on Windows systems.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
Linux Documentation Project's [http://www.tldp.org/HOWTO/NFS-HOWTO NFS How-to]&lt;br /&gt;
&lt;br /&gt;
Troubleshooter.com's [http://www.troubleshooters.com/linux/nfs.htm NFS Overview and Gotchas]&lt;/div&gt;</description>
			<pubDate>Wed, 23 Feb 2005 19:34:10 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Setting_Environment_Variables_in_Bash</comments>		</item>
		<item>
			<title>Setting environment variables</title>
			<link>http://www.softwarebyjeff.com/index.php/Setting_environment_variables</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It happened one time too many that some shell script modified the PATH to insert its bin directory, and then incorrectly exported the environment variable. Two crimes were committed here. First the path is incorrectly exported, persisting its changes longer than nessecary, and the second is blindly adding paths to the environment.&lt;br /&gt;
&lt;br /&gt;
The first and easier to dismiss was the unnecessary export of the environment variable. When your shell script has a block that looks something like the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;#! /bin/bash&lt;br /&gt;
PATH=/path/to/app:${PATH}&lt;br /&gt;
export PATH&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
you've done a disservice with the export. I'm sure the intent was to make sure the PATH correctly allowed the &amp;quot;application&amp;quot; to run, but the export isn't necessary. The PATH is altered, and will remain so for the life of the script. &lt;br /&gt;
&lt;br /&gt;
== Use Export Correctly ==&lt;br /&gt;
&lt;br /&gt;
At the end of the script, without the export, the PATH will not affect the rest of your session. We want to do this so we don't end up with very long PATH environment variables while we work. This can be corrected by removing the export.&lt;br /&gt;
&lt;br /&gt;
At the end of the script, with the export, the PATH will remain and replace the PATH of the environment that called the script. This might be desired, but probably not. If we call this script several times, our PATH will end up looking like '''/path/to/app:/path/to/app:/path/to/app:/path/to/app:${PATH}''' after calling it for the fourth time. Certainly not what anyone wanted.&lt;br /&gt;
&lt;br /&gt;
== Prevent PATH Repetition ==&lt;br /&gt;
&lt;br /&gt;
This brings us to the second disservice. By simply adding your directory to the PATH, exported or not, there is no way to ensure that it's in there only once. Sure the second and even third addition might not make a big deal, and maybe someone will argue that bash is smart enough to figure out that you've done it, but let's come up with a way to keep the PATH clean.&lt;br /&gt;
&lt;br /&gt;
It may be the case that your script exists purely to update an environment variable like this. Consider your ''.bash_profie'' that builds on the system's defaults. Innocently you may end up with a PATH like '''/usr/bin:/usr/bin''' because both the default and your script update the PATH with the same items.&lt;br /&gt;
&lt;br /&gt;
In the bash shell we have a powerful tool to help us avoid this. We can use string replacement to remove the previous instance or instances of a directory in the PATH, or other similar variable.&lt;br /&gt;
&lt;br /&gt;
Consider '''PATH=/usr/bin:${PATH//\/usr\/bin/}''' when writing your new PATH, instead of simply '''PATH=/usr/bin:${PATH}''' to put your desired directory in the list. The string replacement will remove all previous instances of '''/usr/bin''' from the variable, and then the assignment will insert the directory in the beginning of the path.&lt;br /&gt;
&lt;br /&gt;
In detail, the shell has incredible processing power, and we'll use some of that here. Looking at the '''${''variable''//''string''/''string''}''' form we'll see how we can turn that to our advantage and keep our PATH and other variables neat and tidy.&lt;br /&gt;
&lt;br /&gt;
Normally we just put '''$PATH''' or '''${PATH}''' (which helps delimit the variable name, especially useful when in the middle of other text) in our script and let the shell expand that for us.&lt;br /&gt;
&lt;br /&gt;
Adding the slash-style search-and-replace we can better control our output. The format takes our variable name (PATH), and notes we want to do a string replace with the first slash (or two slashes for a global replace, as we have) and second slash noting the string we're searching for, replacing it with the string after the second slash through the end of our string. That is, ${PATH/foo/fee} would look through our PATH variable and give us the string with the first instance of &amp;quot;foo&amp;quot; with &amp;quot;fee&amp;quot; instead, while ${PATH//foo/fee} would replace all &amp;quot;foo&amp;quot; with &amp;quot;fee&amp;quot; throughout the string. Note that this does not change the PATH, but returns what it would be. Let's work through an example.&lt;br /&gt;
&lt;br /&gt;
The old way might have the statement '''PATH=~/bin:/usr/local/bin:/usr/bin:${PATH}''' to put our home directory's bin directory before the /usr/local/bin directory before the /usr/bin directory, before whatever the path was before. Very common. If the PATH was empty before, we get the new PATH of '''/home/username/bin:/usr/local/bin:usr/bin:''' while if the PATH were '''/bin''' before it is now '''/home/username/bin:/usr/local/bin:usr/bin:/bin'''. Nothing new here. Running this a second time, however, we get '''/home/username/bin:/usr/local/bin:usr/bin:/home/username/bin:/usr/local/bin:usr/bin:''' plus whatever PATH was originally. As noted before, ugly and unnecessary.&lt;br /&gt;
&lt;br /&gt;
If we instead break out our PATH reassignment in a few lines instead of one, we get a neater, better-controlled PATH.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;PATH=/usr/bin:${PATH//\/usr\/bin/}&lt;br /&gt;
PATH=/usr/local/bin:${PATH//\/usr\/local\/bin/}&lt;br /&gt;
PATH=~/bin:${PATH//${HOME}\/bin/}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's step through this one bit at a time. Assume that we've done it once, and our PATH when we start is '''/home/username/bin:/usr/local/bin:usr/bin:/bin''' when we start.&lt;br /&gt;
&lt;br /&gt;
The first line, '''PATH=/usr/bin:${PATH//\/usr\/bin/}''' takes our PATH and removes the /usr/bin from it. Note the goofy '''\/''' in the search part of our search-and-replace string. This is to help the shell know that the slash is part of our string, and not the delimiter of search-and-replace. Note also that after the final '''/''' (following \/bin) there is nothing before the ending brace. This tells us we're replacing our string, when found, with nothing. Note also that we're using a global search and replace, so it'll find all instances of /usr/bin in the PATH and remove them. This gives us '''/home/username/bin:/usr/local/bin::/bin''' as a result, to which we prepend '''/usr/bin''' finally resulting in '''usr/bin:/home/username/bin:/usr/local/bin::/bin''' that we assign to the PATH variable.&lt;br /&gt;
&lt;br /&gt;
The next line, '''PATH=/usr/local/bin:${PATH//\/usr\/local\/bin/}''' likewise, removes /usr/local/bin from the path, and prepends it. First the string from the previous assigment '''usr/bin:/home/username/bin:/usr/local/bin::/bin''' is searched and the string /usr/local/bin is replaced with nothing, resulting in '''usr/bin:/home/username/bin:::/bin''' to which we prepend /usr/local/bin again, resulting in '''/usr/local/bin:usr/bin:/home/username/bin:::/bin''' that is assigned to PATH.&lt;br /&gt;
&lt;br /&gt;
The final line, '''PATH=~/bin:${PATH//${HOME}\/bin/}''' will then remove our home directory's bin from the PATH and prepend it back again. First the string from the previous assignment '''/usr/local/bin:usr/bin:/home/username/bin:::/bin''' is searched. Note that the ${HOME} is used in the search portion instead of ~/bin as the ~ replacement doesn't seem to happen in the middle of that evaluation, but we are allowed to use the HOME variable, which should be expanded the same way; inconsistently, however, the tilde is expanded during the assignment, and the PATH will reflect the full path and not the tilde shortcut. Searching and replacing this with nothing results in the string '''/usr/local/bin:usr/bin::::/bin''' to which we prepend the path and assign it to our PATH resulting in '''/home/username/bin:/usr/local/bin:usr/bin::::/bin''' instead of the repeated paths.&lt;br /&gt;
&lt;br /&gt;
Annoyingly, there are all of the colons in the block where our strings were removed. You can experiment with where to place the colons in your search and replace string, but you might find that you either duplicate searching before and after, like '''PATH=~/bin:${PATH//:${HOME}\/bin/};''' '''PATH=~/bin:${PATH//${HOME}\/bin:/}''' or you may miss the string. Or you may end up with more complicated searches than I want to discuss. This might be required for something that would be part of a smaller string, like '''/bin''' which could logically require '''PATH=/bin:${PATH//:\/bin/}''' but probably should be avoided for other path entries. See the later discussion on /bin.&lt;br /&gt;
&lt;br /&gt;
The method presented leaves a few doubled-up colons that can be removed with the one-liner loop '''while [ -n &amp;quot;`echo $PATH | grep ::`&amp;quot; ]; do PATH=${PATH//::/:}; done''' which would give us ultimately '''/home/username/bin:/usr/local/bin:usr/bin:/bin''' from the last result above. Nice and neat. The while loop searches the PATH for &amp;quot;::&amp;quot; by enlisting the help of grep. The search and replace then removes all double-colons replacing them with single colons. However, the search and replace isn't terribly intelligent, so after the first pass of our sample string we end up with '''/home/username/bin:/usr/local/bin:usr/bin::/bin''' as it replaces first the first pair, then the second pair out of the original string, not seeing that it has created a pair as a result. Looping allows us to keep going until it's all done.&lt;br /&gt;
&lt;br /&gt;
What happens, however, if the search-and-replace removes the last entry, leaving the path ending in a colon? This tidbit, '''PATH=${PATH%:}''' will remove the colon by using the string truncating '''${PATH%''string''}''' where the percent symbol says take off the string if it ends with the character. Note if the string ended with a different string, nothing is done.&lt;br /&gt;
&lt;br /&gt;
Now, combining those things we can end up with this bit instead, which puts only one instance of a directory in the PATH, and makes sure it's neat and tidy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;PATH=/usr/bin:${PATH//\/usr\/bin/}&lt;br /&gt;
PATH=/usr/local/bin:${PATH//\/usr\/local\/bin/}&lt;br /&gt;
PATH=~/bin:${PATH//${HOME}\/bin/}&lt;br /&gt;
while [ -n &amp;quot;`echo $PATH | grep ::`&amp;quot; ]; do PATH=${PATH//::/:}; done&lt;br /&gt;
PATH=${PATH%:}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course, you can prepend and postpend as you would normally. If you, for example, always wanted '''/usr/bin''' at the end of the path, simply change its line to '''PATH=${PATH//\/usr\/bin/}:/usr/bin'''&lt;br /&gt;
&lt;br /&gt;
Note that this works for other similarly formatted directory-based paths like the lD_LIBRARY_PATH and MANPATH. On my system I use the PATH as a guide and make sure, by the same method, that my LD_LIBRARY_PATH and MANPATH contain the related directories in the same order; that way if there is an over-riding entry (/usr/local/bin/tar always comes to mind...without care I get the less feature-filled /bin/tar), I'll get the corresponding information.&lt;br /&gt;
&lt;br /&gt;
== Replacement Alternative ==&lt;br /&gt;
&lt;br /&gt;
Instead of rearranging the path every time, which is certainly guaranteed to maintain your order only when all of the items are introduced, we can use the string replacement to conditionally add items to the PATH. It may be the case that we don't care where the desired directory is in the order of the PATH, just that it's there.&lt;br /&gt;
&lt;br /&gt;
Try '''if [ &amp;quot;${PATH}&amp;quot; = &amp;quot;${PATH//\/usr\/local\/ant\/bin/}&amp;quot; ]; then PATH=/usr/local/ant/bin:${PATH}; fi''' to make sure that your directory exists, adding it only if necessary.&lt;br /&gt;
&lt;br /&gt;
== What to do About /bin ==&lt;br /&gt;
&lt;br /&gt;
After reviewing this, I thought about how to handle the special case of '''/bin''' which is a valid directory, but is a substring of most other valid directories. We can't simply search and replace all '''/bin''' entries and remove them, because then '''/usr/local/bin''' becomes '''/usr/local''', which is not the same thing. We can't assume that it's in the middle, either and search only for ''':/bin:''' nor do we want to assume that it's at the beginning or end.&lt;br /&gt;
&lt;br /&gt;
Sadly, the only solution I could deliver is to do all three.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Eliminate :/bin from the end of the path&lt;br /&gt;
PATH=${PATH%:/bin}&lt;br /&gt;
# Remove /bin: from the beginning of the path&lt;br /&gt;
PATH=${PATH#/bin:}&lt;br /&gt;
# Replace :/bin: from the middle of the path&lt;br /&gt;
PATH=${PATH//:\/bin:/:}:/bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since, as mentioned, /bin contains the base for programs that are often replaced, I always put this at the end of my path. If the program I'm looking for is no where else, then use the /bin copy.&lt;br /&gt;
&lt;br /&gt;
== Special thanks to ==&lt;br /&gt;
[http://www.tldp.org The Linux Documentation Project] on which I found the [http://www.tldp.org/LDP/abs/html/refcards.html Reference Card] that showed the bash string manipulations.&lt;/div&gt;</description>
			<pubDate>Wed, 23 Feb 2005 16:01:23 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Setting_environment_variables</comments>		</item>
		<item>
			<title>System Setup Tips and Tricks</title>
			<link>http://www.softwarebyjeff.com/index.php/System_Setup_Tips_and_Tricks</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Maintaining version control of installed software&lt;br /&gt;
&lt;br /&gt;
[[Setting Bash Environment Variables]] to avoid duplicated entries&lt;br /&gt;
&lt;br /&gt;
Using [[NFS]] for sharing files across systems&lt;br /&gt;
&lt;br /&gt;
Configuring [[SSH]] for password-less access&lt;br /&gt;
&lt;br /&gt;
Using [[Thin Clients]] in SOHO deployments&lt;br /&gt;
&lt;br /&gt;
Use [[screen]] for multiple and persistent shell sessions&lt;/div&gt;</description>
			<pubDate>Wed, 23 Feb 2005 15:50:53 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:System_Setup_Tips_and_Tricks</comments>		</item>
		<item>
			<title>Testing Static Members and Methods</title>
			<link>http://www.softwarebyjeff.com/index.php/Testing_Static_Members_and_Methods</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It's very difficult to test static members fields. Easy to test methods, but hard to test the fields.&lt;br /&gt;
&lt;br /&gt;
Thankfully, Java provides Reflection, which allows us to access any of an object's members, no matter what the suggested accessor.&lt;br /&gt;
&lt;br /&gt;
Let's make a simple test that tries to get class' private static memeber, and look at it with Reflection interjected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public void testSomething(){&lt;br /&gt;
    Field field = SomeClass.class.getDeclaredField(&amp;quot;someField&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    boolean wasAccessible = field.isAccessible();&lt;br /&gt;
    if (!wasAccessible)&lt;br /&gt;
        field.setAccessible(true);&lt;br /&gt;
&lt;br /&gt;
    Object object = field.get(null);&lt;br /&gt;
&lt;br /&gt;
    if (!wasAccessible)&lt;br /&gt;
        field.setAccessible(false);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This basic test, which really only tests that Reflection can find the member and retreive a value for us, shows us what we need to do with to get the private static member. The test is broken in four bits.&lt;br /&gt;
&lt;br /&gt;
The first uses getDeclaredField() to extract the field from SomeClass. We need to provide the class SomeClass represents; without it, compile time errors occur.&lt;br /&gt;
&lt;br /&gt;
If the field exists, the test moves on to where we check to see if it's visible. Visibility is determined by the field's accessor, and is visible if it's assigned public (in which case we don't need to use Reflection), or package (and we're in the same package--again, Reflection is not required). If the accessor is package (and we're not in the same package), protected (and we're not extending that class), or private (no matter what we want), it will be unavailable, and we'll tell Reflection to let us at it.&lt;br /&gt;
&lt;br /&gt;
The next bit pulls the value from the static member and returns it wrapped in a Java Object. This means we can access primatives, too, like int, long, and boolean, and Reflection will wrap them to the appropriate class, like Integer, Long, and Boolean, respectively. We can hand the field.get() a null value because we're working with a static member field. If the field is not static, Reflection will complain and we'll stop the test here.&lt;br /&gt;
&lt;br /&gt;
At this point, we should do whatever it is we want to do. Compare the value with an expected value, do something to change it, or whatever. We can also use the Relflection set(Object, Object) to put a value in the field, if we want to configure our static in a particular way.&lt;br /&gt;
&lt;br /&gt;
Finally, we're polite and return the field to the appropriate access. Basically, if we futzed with it, we futz it back the way it was. This protects future tests from inappropraite access to the field.&lt;/div&gt;</description>
			<pubDate>Mon, 07 Feb 2005 20:35:32 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Testing_Static_Members_and_Methods</comments>		</item>
		<item>
			<title>Writing Effective Tests</title>
			<link>http://www.softwarebyjeff.com/index.php/Writing_Effective_Tests</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;On topic of much debate is how to write an effective JUnit test. Unit testing is new to many people, and often the impulse to write as little code as necessary is overwhelming, resulting in poor test implementation. Consider this code snippet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public void testSomething(){&lt;br /&gt;
    SomeClass.someMethod();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before wondering why such a simple test is used as an example, consider that this was taken from a real test class I saw, changing the names as they'd otherwise be irrelevant here.&lt;br /&gt;
&lt;br /&gt;
What exactly does this test? Well, it makes certain that a class named SomeClass is in the class path, and that it contains a static method named someMethod() that takes no parameters, as missing any of these would cause compile failures. One could argue that it also tests that the method does not throw an Exception when executed, as that would cause a runtime failure, but that's not evident by the declaration of the test.&lt;br /&gt;
&lt;br /&gt;
Thus, based on the test, this is all that we know we have:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class SomeClass{&lt;br /&gt;
    public static void someMethod() {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And, using Test Driven Development, that should be all that is there. If we want more, we need to have a better test or set of tests.&lt;br /&gt;
&lt;br /&gt;
Since our method is obviously static, the items it affects are likewise static, or short lived and internal completely to someMethod(). Does this mean there's nothing to test? Absolutely not. It just might be harder than some.&lt;br /&gt;
&lt;br /&gt;
Let's not dally with the obvious headaches of dealing with static methods. See the discussion [[Testing Static Members and Methods]] for information on how to get to the things a static method might affect.&lt;br /&gt;
&lt;br /&gt;
== Useful Methods ==&lt;br /&gt;
&lt;br /&gt;
We can assume that when we write code we want to make useful bits along the way. Therefore, we should be right in assuming that someMethod() does something useful. However, as written, nothing is tested.&lt;br /&gt;
&lt;br /&gt;
When we write our useful methods, it is almost certain that our object is in some state before we begin, and it is in a different state when we finish, or that it thusly affects some other object. The appropriate test should check the state before and after the method call, and ensure the anticipated change is made.&lt;br /&gt;
&lt;br /&gt;
=== Changing State ====&lt;br /&gt;
&lt;br /&gt;
Here's an example of a more useful, yet still simple, test.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public void testSomethingUseful(){&lt;br /&gt;
    SomeClass someClass = new SomeClass();&lt;br /&gt;
    int originalValue = someClass.getCount();&lt;br /&gt;
&lt;br /&gt;
    someClass.someMethod();&lt;br /&gt;
&lt;br /&gt;
    assertEquals(originalValue + 1, someClass.getCount());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There's a bit of a better test. In this test, we can infer that someMethod is going to affect the int value that getCount() will return, apparently incrementing the integer by one.&lt;br /&gt;
&lt;br /&gt;
Very simple and straight forward. We get the value before we do the work, do the work, and compare the value with expected results. How does our class look now?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class SomeClass{&lt;br /&gt;
    private static int count = 0;&lt;br /&gt;
&lt;br /&gt;
    public static void someMethod() {&lt;br /&gt;
        count++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public int getCount() {&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is all our test tells us to expect, so that's all we can infer about the methods.&lt;br /&gt;
&lt;br /&gt;
Note, we had to take the liberty of assigning count with an initial value. Nothing in our test says this has to be zero. Perhaps another test would be in order to ensure the creation is zero. Since it's static, though, testing this can be difficult as we can't be certain that we're always the first test affecting this class.&lt;br /&gt;
&lt;br /&gt;
Writing test does not have to be cryptic by any means. A very simple rule of '''a test should test something''' should be followed no matter what the test is for. If all you're doing is asserting a class name and method, we can hope that the next line of the test is more important.&lt;/div&gt;</description>
			<pubDate>Mon, 07 Feb 2005 20:22:46 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Writing_Effective_Tests</comments>		</item>
		<item>
			<title>Good Base Test Case</title>
			<link>http://www.softwarebyjeff.com/index.php/Good_Base_Test_Case</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here's a fine test to start with for making sure all of your JUnit tests are exceuted. It comes with a test to ensure that all of your tests are included. Simply extend AllLocalTests with your tests in the same package, and you're good to go. If you forget to add your new test to AllLocalTests.suite(), the testEnsureAllLocalTestsAreIncluded() will notify you of your oversight. Then simply execute the one test, AllLocalTests, and all of the tests in your project would be excuted.&lt;br /&gt;
&lt;br /&gt;
Additionally, as this test class is expected to be extended by all of your tests, it is an excellent place to set up your pre-test environment (attach to a database, for example) add additional methods that might be helpful throughout your tests (notice there's an AssertEquals(Object, Object), but not AssertNotEquals(Object, Object) in JUnit's TestCase?&lt;br /&gt;
&lt;br /&gt;
The recommended use is to put this class in the root of your test's package tree. If you have separate groups of tests, put multiple copies of this base class in separate &amp;quot;root&amp;quot; points in your test packages, and include the tests in those trees in the appropriate instance of this test.&lt;br /&gt;
&lt;br /&gt;
For example, if our packages included com.softwarebyjeff.packageA and com.softwarebyjeff.packageB, we could put this in com.softwarebyjeff to cover both packageA and packageB. Alternatively, if we had cause for making separate test cores (perhaps one is purely local while the other requires an application server to be running), we'd put two copies of this class, one in packageA containing only tests there and deeper, and one in packageB.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
import java.util.Arrays;&lt;br /&gt;
import java.util.Collection;&lt;br /&gt;
import java.util.Enumeration;&lt;br /&gt;
import java.util.Iterator;&lt;br /&gt;
import java.util.Vector;&lt;br /&gt;
&lt;br /&gt;
import junit.framework.Test;&lt;br /&gt;
import junit.framework.TestCase;&lt;br /&gt;
import junit.framework.TestSuite;&lt;br /&gt;
import junit.runner.LoadingTestCollector;&lt;br /&gt;
import junit.textui.TestRunner;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
* Use this as a base class for local tests to ensure complete coverage&lt;br /&gt;
*/&lt;br /&gt;
public class AllLocalTests extends TestCase&lt;br /&gt;
{&lt;br /&gt;
   /**&lt;br /&gt;
    * Start and run the JUnit tests as an application&lt;br /&gt;
    *&lt;br /&gt;
    * @param args&lt;br /&gt;
    */&lt;br /&gt;
   public static void main(String args[])&lt;br /&gt;
   {&lt;br /&gt;
       TestRunner.run(suite());&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   /**&lt;br /&gt;
    * Add all of the tests here&lt;br /&gt;
    *&lt;br /&gt;
    * @return suite of all tests to run&lt;br /&gt;
    */&lt;br /&gt;
   public final static Test suite()&lt;br /&gt;
   {&lt;br /&gt;
       TestSuite suite = new TestSuite();&lt;br /&gt;
&lt;br /&gt;
       // Recommend leaving this last for console output&lt;br /&gt;
       suite.addTestSuite(AllLocalTests.class);&lt;br /&gt;
&lt;br /&gt;
       return suite;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   /**&lt;br /&gt;
    * Test to Ensure All Local Tests Are Included&lt;br /&gt;
    *&lt;br /&gt;
    * @throws Exception&lt;br /&gt;
    */&lt;br /&gt;
   public void testEnsureAllLocalTestsAreIncluded() throws Exception&lt;br /&gt;
   {&lt;br /&gt;
       System.out.println(&amp;quot;Verifying all test classes extend AllLocalTests and are in AllLocalTests.suite()&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
       // Only put tests here that might extend AllLocalTests although they don't have executable tests&lt;br /&gt;
       Collection excludedClassNames = Arrays.asList(new Object[] { &amp;quot;AllLocalTests&amp;quot; });&lt;br /&gt;
&lt;br /&gt;
       // Should just reflect and steal the list, but we'll be nice...&lt;br /&gt;
       Collection currentTests = new Vector();&lt;br /&gt;
       TestSuite testSuite = (TestSuite) (AllLocalTests.suite());&lt;br /&gt;
       for (int i = 0; i &amp;lt; testSuite.testCount(); i++)&lt;br /&gt;
       {&lt;br /&gt;
           TestSuite testSuiteAt = (TestSuite) (testSuite.testAt(i));&lt;br /&gt;
           String className = testSuiteAt.getName();&lt;br /&gt;
           currentTests.add(className);&lt;br /&gt;
       }&lt;br /&gt;
&lt;br /&gt;
       // Iterate through each class in classpath and ensure they're correctly implemented&lt;br /&gt;
       Collection missingTests = new Vector();&lt;br /&gt;
       Collection incorrectlyImplemented = new Vector();&lt;br /&gt;
       String packageName = this.getClass().getPackage().getName();&lt;br /&gt;
       Enumeration enumeration = new LoadingTestCollector().collectTests();&lt;br /&gt;
       while (enumeration.hasMoreElements())&lt;br /&gt;
       {&lt;br /&gt;
           String object = enumeration.nextElement().toString();&lt;br /&gt;
&lt;br /&gt;
           if ((object != null)&lt;br /&gt;
               &amp;amp;&amp;amp; (object.indexOf(packageName) == 0)&lt;br /&gt;
               &amp;amp;&amp;amp; (!excludedClassNames.contains(object.substring(object.lastIndexOf(&amp;quot;.&amp;quot;) + 1))))&lt;br /&gt;
           {&lt;br /&gt;
               if (AllLocalTests.class.isAssignableFrom(Class.forName(object)))&lt;br /&gt;
               {&lt;br /&gt;
                   if (!currentTests.contains(object))&lt;br /&gt;
                       missingTests.add(object);&lt;br /&gt;
               }&lt;br /&gt;
               else if (Test.class.isAssignableFrom(Class.forName(object)))&lt;br /&gt;
               {&lt;br /&gt;
                   incorrectlyImplemented.add(object);&lt;br /&gt;
               }&lt;br /&gt;
           }&lt;br /&gt;
       }&lt;br /&gt;
&lt;br /&gt;
       // Loop through failure collections to list failures&lt;br /&gt;
       if (!missingTests.isEmpty())&lt;br /&gt;
       {&lt;br /&gt;
           System.err.println(&amp;quot;Not all classes that extend AllLocalTests are in AllLocalTests.suite():&amp;quot;);&lt;br /&gt;
           Iterator missingTestsIterator = missingTests.iterator();&lt;br /&gt;
           while (missingTestsIterator.hasNext())&lt;br /&gt;
           {&lt;br /&gt;
               String className = missingTestsIterator.next().toString();&lt;br /&gt;
               System.err.println(&amp;quot;Missing test: &amp;quot; + className);&lt;br /&gt;
           }&lt;br /&gt;
       }&lt;br /&gt;
       if (!incorrectlyImplemented.isEmpty())&lt;br /&gt;
       {&lt;br /&gt;
           System.err.println(&amp;quot;Not all test classes extend AllLocalTests:&amp;quot;);&lt;br /&gt;
           Iterator incorrectlyImplementedIterator = incorrectlyImplemented.iterator();&lt;br /&gt;
           while (incorrectlyImplementedIterator.hasNext())&lt;br /&gt;
           {&lt;br /&gt;
               String className = incorrectlyImplementedIterator.next().toString();&lt;br /&gt;
               System.err.println(&amp;quot;Incorrectly implemented test: &amp;quot; + className);&lt;br /&gt;
           }&lt;br /&gt;
       }&lt;br /&gt;
&lt;br /&gt;
       // Redbar if failures&lt;br /&gt;
       assertTrue(&lt;br /&gt;
           &amp;quot;Check the console output for a complete list of errors&amp;quot;,&lt;br /&gt;
           missingTests.isEmpty() &amp;amp;&amp;amp; incorrectlyImplemented.isEmpty());&lt;br /&gt;
&lt;br /&gt;
       // Log compliance if success&lt;br /&gt;
       System.out.println(&amp;quot;All classes extend AllLocalTests and are in AllLocalTests.suite()&amp;quot;);&lt;br /&gt;
   } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;/div&gt;</description>
			<pubDate>Mon, 07 Feb 2005 19:01:11 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Good_Base_Test_Case</comments>		</item>
		<item>
			<title>Driving Development with Tests</title>
			<link>http://www.softwarebyjeff.com/index.php/Driving_Development_with_Tests</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here is a tried-and-true Test Driven Development technique. The methods discussed are using Eclipse, but a NetBeans discussion will be forthcoming. In general, the techniques are the same, but the menu items and other idiosyncracies are different.&lt;br /&gt;
&lt;br /&gt;
== Get a bug or task ==&lt;br /&gt;
&lt;br /&gt;
It is strongly suggested that some tracking tool be used. We have discussed Bugzilla as a tool. Find a task assigned to you or in your realm of expertise. Claim it, mark it as being worked on by you. This will let other developers know they can let that one go, and allow the tracking software to monitor progress. This is more important in a multi-developer setup, but is very helpful if you work alone, too. If you have to leave the work for a while, it's an easy way to remind yourself where to pickup.&lt;br /&gt;
&lt;br /&gt;
== Get a clean workset ==&lt;br /&gt;
&lt;br /&gt;
Synchronize before starting work. Always. Every time. Never doubt this step. If you've just finished delivering work, and you're in the middle of a development cycle, there might not be anything more to do, but test it out anyway.&lt;br /&gt;
&lt;br /&gt;
In Eclipse, using CVS, this is easy.&lt;br /&gt;
&lt;br /&gt;
Do not worry about re-synchronizing again until either something critical is needed or you are ready to deliver your work. Unless you are expecting something and it gets delivered before you're done, it will only add distraction if you run into merge conflicts on your files or if other things are broken that you don't really affect with your work. &lt;br /&gt;
&lt;br /&gt;
Synchronize infrequently, not often.&lt;br /&gt;
&lt;br /&gt;
== Do your work ==&lt;br /&gt;
&lt;br /&gt;
Write or modifiy a test before you make your changes to classes and files! There is some disagreement on how much to test first, and how much to test during development.&lt;br /&gt;
&lt;br /&gt;
This is key to test-driven-development--start with the test. Make your test expect the new changes. Expect it to fail. When it fails, make the appropriate changes to make it pass. Make changes in small steps, adding members and methods one at a time, testing between each addition, repeating as necessary. Only make changes when the compiler complains or the test you write fails. Never just because you want to or know that you'll have to. If you know you'll have to, write a test for it and prove it.&lt;br /&gt;
&lt;br /&gt;
If you make a new test class, add it to AllLocalTests. Somewhere in your test project, there should be a test that has a list of all of the tests that should be run by JUnit, we're calling it AllLocalTests. If you see a test that is not part of AllLocalTests, add it. Complete coverage is the key.&lt;br /&gt;
&lt;br /&gt;
See also:&lt;br /&gt;
[[Good Base Test Case]]&lt;br /&gt;
[[Writing Effective Tests]]&lt;br /&gt;
&lt;br /&gt;
=== Stubs and iterative development ===&lt;br /&gt;
&lt;br /&gt;
If your work requires additional classes or files that don't yet exist, make them, but only fill in what is absolutely necessary. &lt;br /&gt;
&lt;br /&gt;
For example, if you need to create a class to pass as a parameter, make the class without members. If you need to use a method in the class, make it, and only what is required for the class to intelligently use it (fields and properties). If you need to make a Hibernate mapping, make only what you require and what the database requires (not-nullable columns). &lt;br /&gt;
&lt;br /&gt;
Develop in baby steps. Don't work in such a manner that if you need a class as a parameter that you should feel you must make the complete model as in the sequence diagram, nor do you have to make the associated Hibernate mapping. This will all be done in due time, and with the tests driving the development, it should be completely covered as it's developed.&lt;br /&gt;
&lt;br /&gt;
=== Clean your code ===&lt;br /&gt;
&lt;br /&gt;
Organize your imports and format your code. This will aid merging more than anyone can possibly explain. Use agreed upon IDE settings, not your personal preferences. This should also be done as you work, but you can easily find the files you modified, and do it before you test for the last time. Unused imports should be in your Eclipse Task list, if nothing else--make sure there are none. &lt;br /&gt;
&lt;br /&gt;
Imports and formatting should not break the tests, but we want to find out before the tests run, not after. It could be the case, for example, that the imports get organized higher than the last class (i.e., java.lang.*, which is imported by default, not java.lang.String). It might be the case that collapsing all of the packages to this level causes ambiguity. Organizing the imports to support easy code-sharing should not impact compilation or running the classes. Find out early by organizing before you run the tests.&lt;br /&gt;
&lt;br /&gt;
== Run your tests ==&lt;br /&gt;
&lt;br /&gt;
Run AllLocalTests, or whatever you called your master test class. If you caused something to fail, find out why and fix it. Either your change was incomplete, had unexpected side effects, or the same bit of work was tested somewhere you weren't expecting. You broke it, you fix it. Repeat as necessary. Do not move on until all of the tests pass.&lt;br /&gt;
&lt;br /&gt;
Do not comment out tests just because they fail. Look at the JUnit output, identify the line that fails. Often, you'll find that it is because you forgot something (like inserting database data), changed something (perhaps as designed in your change), duplicated something (base data now exists and the test no longer needs to insert), or the like. If you make small enough changes, anything you break will be small enough to fix.&lt;br /&gt;
&lt;br /&gt;
The purpose of the test is to ensure that unfamiliar developmers respect intent. It may be that the intent has changed, and therefore a bunch of tests have to be fixed. If you break a test, however, and don't correct it, you've lost control of the project and potentially caused gaps in test coverage.&lt;br /&gt;
&lt;br /&gt;
If you break the rule to not comment out tests, make the next task you work on after delivery fixing the test you commented out. If you commented out a test after discussing it with someone else and they said they'd fix it for you, make sure to follow up with the now missing test.  &lt;br /&gt;
&lt;br /&gt;
Do not abandon commented code. Mark the test with a TODO comment so that you can find it easily and others will be able to see that it was commented out with reason (put that in the TODO comment). If you're tracking changes and bugs, add a bug to remind and alert that this test is broken and needs to be repaired.&lt;br /&gt;
&lt;br /&gt;
== Merge your code == &lt;br /&gt;
&lt;br /&gt;
Yes, this could be the only synchronization you need to do! Get all of the other changes. This is easy if you're working alone, but if your on a collective project, others may have made changes that you need to have. Synchronize as you did when starting work. &lt;br /&gt;
&lt;br /&gt;
Since you have made changes, however, this may result in conflicts.&lt;br /&gt;
&lt;br /&gt;
Merge wisely. There are better merge tools, but this is the one that we have. Try to note what you had to merge, and verify that the merge didn't break the Java. Automated merging is included in this; it can be the case that style, formatting, or even order of elements will be merged and form duplicates. The merger won't complain, but Java will. &lt;br /&gt;
&lt;br /&gt;
Overlap will happen! If you have to throw away your work because you stubbed an Entity that someone else delivered, you should have only a bit of work to discard. Be mindful when merging that your temporary work is meant to be discarded, and that the expected delivery from someone else may not be the same as yours--you will have to fix any problems that come from bad merging or design differences.&lt;br /&gt;
&lt;br /&gt;
Verify that everything compiles. Have Eclipse refresh to ensure the changes are recognized. Possibly rebuild the project or related projects, if the project you're working on delivers archives to other projects.&lt;br /&gt;
&lt;br /&gt;
Run AllLocalTests again, to make sure nothing broke. If it did, it's likely because of merge problems. If there are errors, correct them, returning in the process to the steps above, repeating as necessary.&lt;br /&gt;
&lt;br /&gt;
== Commit ==&lt;br /&gt;
&lt;br /&gt;
Now that everything works, and no more changes are necessary to synchronize, commit your code. This will tell the version control software to take everything that you've worked on, and add it to the repository. This will make it available when the next developer tries to synchronize.&lt;br /&gt;
&lt;br /&gt;
== Mark your bug or task done ==&lt;br /&gt;
&lt;br /&gt;
If you're tracking work in Bugzilla or something similar, go back to your task, mark it complete, or whatever level you've agreed to mark tasks leaving development. This will let others know that it's ready, and allow the tracking to identify one less thing outstanding.&lt;br /&gt;
&lt;br /&gt;
== Finish ==&lt;br /&gt;
&lt;br /&gt;
Move on to the next task. Always start with a clean baseline. If you were able to succesfully commit, you probably have the latest baseline. If you start on the next task right away, you can skip the first step of resynchronizing, although a better practice is to do it anyway--if there's nothing new, you'll get that result almost immediately.&lt;/div&gt;</description>
			<pubDate>Fri, 21 Jan 2005 20:15:52 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Driving_Development_with_Tests</comments>		</item>
		<item>
			<title>NetBeans</title>
			<link>http://www.softwarebyjeff.com/index.php/NetBeans</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A very popular Open Source Java Integrated Development Environment is NetBeans.&lt;br /&gt;
&lt;br /&gt;
[http://netbeans.org NetBeans home]&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:55:28 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:NetBeans</comments>		</item>
		<item>
			<title>Eclipse</title>
			<link>http://www.softwarebyjeff.com/index.php/Eclipse</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The more popular Open Source Java Integrated Development Environment is Eclipse.&lt;br /&gt;
&lt;br /&gt;
[http://eclipse.org Eclipse home]&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:54:45 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Eclipse</comments>		</item>
		<item>
			<title>E-Groupware</title>
			<link>http://www.softwarebyjeff.com/index.php/E-Groupware</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;An interesting collection of tools, eGroupWare provides an integrated set of tools, much more than we can try to list as easily. Integrated e-mail, bug tracking, calendaring, project management, trouble tickets, wiki, and more.&lt;br /&gt;
&lt;br /&gt;
[http://egroupware.org CVS Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:18:15 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:E-Groupware</comments>		</item>
		<item>
			<title>MediaWiki</title>
			<link>http://www.softwarebyjeff.com/index.php/MediaWiki</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A wiki is an open collaboration center. This document is written in a wiki. Wikis allow unique collaboration and history of collaboration by allowing all users to edit the pages. This allows for easy correction, robust discussion, and incredible opportunity for information dissemination.&lt;br /&gt;
&lt;br /&gt;
[http://wikipedia.sf.net MediaWiki Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:16:02 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:MediaWiki</comments>		</item>
		<item>
			<title>Bugzilla</title>
			<link>http://www.softwarebyjeff.com/index.php/Bugzilla</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Tracking problems is always a problem. While not designed as a project management tool, Bugzilla offers robust and interactive bug tracking, whose use can be modified for entering tasks and tracking their progress.&lt;br /&gt;
&lt;br /&gt;
[http://bugzilla.org Bugzilla Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
Installation is as easy as any other project from source. There are dependencies that need to be met, and a script to run, and possible post-installation configuration.&lt;br /&gt;
&lt;br /&gt;
=== Dependencies ===&lt;br /&gt;
&lt;br /&gt;
Bugzilla is written in PERL. As such you need to have PERL installed. PERL is typically installed on LINUX, Solaris, BSD, Mac, and other *NIX variants. Make sure your PERL version is current, and you'll be fine.&lt;br /&gt;
&lt;br /&gt;
Perl stores its information in MySQL. Make sure you have access to an installation of MySQL. Of course, it doesn't need to be on the system on which you're installing Bugzilla, but it does need to be accessible.&lt;br /&gt;
&lt;br /&gt;
Bugzilla runs as CGI scripts on your web server, therefore it must be installed on the webserver. Apache is easy, and others are almost as easy. &lt;br /&gt;
&lt;br /&gt;
If you don't have access to change the configuration of Apache, you can install Bugzilla in any directory normally accessible, that can execute PERL scripts.&lt;br /&gt;
&lt;br /&gt;
On Apache, simply add an alias to your config file and restart the server. Of course, this might make more sense after you install Bugzilla, or at least create the directory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alias /bugzilla &amp;quot;/usr/local/bugzilla&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Directory &amp;quot;/usr/local/bugzilla&amp;quot;&amp;gt;&lt;br /&gt;
    DirectoryIndex index.cgi index.html&lt;br /&gt;
    Options +FollowSymLinks +Indexes +Includes +ExecCGI&lt;br /&gt;
    AllowOverride All&lt;br /&gt;
    Order allow,deny&lt;br /&gt;
    Allow from all&lt;br /&gt;
&amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bugzilla Installation ===&lt;br /&gt;
&lt;br /&gt;
Download and install the source by uncompressing the downloaded file in the target directory. In the example above, use &amp;quot;/usr/local/bugzilla&amp;quot; as the target. &lt;br /&gt;
&lt;br /&gt;
Once the source is extracted, run the setup script as root, i.e, '''sudo . /checkup.pl''' to let Bugzilla query you for the required information. This step will create the MySQL tables and initial configuration.&lt;br /&gt;
&lt;br /&gt;
If you're missing any PERL modules that Bugzilla may need, you can use the PERL CPAN tool to install them by '''sudo perl -MCPAN -e 'install Bundle::Bugzilla' ''' to get them all at once. This might be a better place to start.&lt;br /&gt;
&lt;br /&gt;
Note the PERL CPAN install does not install Bugzilla itself--just the PERL modules on which it depends.&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
Creating users in Bugzilla is almost too easy. Anyone who visits the Bugzilla page that you set up can create their own account. While this may sound insecure, a few things follow that will show how to protect your projects from snoopy eyes.&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
There isn't a clear way to integrate Bugzilla in any of the IDEs that we discuss. It's really an external process. We still recommend its use as it does provide accountablity and tracking.&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;br /&gt;
&lt;br /&gt;
Of course, Bugzilla's most prominent user is [http://mozilla.org Mozilla]; they wrote it. They use it to publicly track bugs in Mozilla products at [http://bugzilla.mozilla.org Mozilla's bug tracking].&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:13:04 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Bugzilla</comments>		</item>
		<item>
			<title>Hibernate</title>
			<link>http://www.softwarebyjeff.com/index.php/Hibernate</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;To help ease Java-to-database development, the persistence manager Hibernate works.&lt;br /&gt;
&lt;br /&gt;
[http://hibernate.org Hibernate Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;br /&gt;
&lt;br /&gt;
An alternative is Apache's Turbine.&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:11:38 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Hibernate</comments>		</item>
		<item>
			<title>MySQL</title>
			<link>http://www.softwarebyjeff.com/index.php/MySQL</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The world's most popular Open Source database, their website proclaims, MySQL provides a robust, standards-oriented SQL server.&lt;br /&gt;
&lt;br /&gt;
[http://mysql.com MySQL Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:10:38 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:MySQL</comments>		</item>
		<item>
			<title>JUnit</title>
			<link>http://www.softwarebyjeff.com/index.php/JUnit</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If the bar is green, the code is clean! Using JUnit testing while developing software can provide many benefits in the long run. It can add time to the front-end of development planning, but the end result can be more reliable code and fewer bugs introduced.&lt;br /&gt;
&lt;br /&gt;
[http://junit.org JUnit Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
JUnit testing is enabled by both Eclipse v3 and NetBeans v4. This discussion of JUnit will touch on &lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:06:35 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:JUnit</comments>		</item>
		<item>
			<title>Microsoft Java</title>
			<link>http://www.softwarebyjeff.com/index.php/Microsoft_Java</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Microsoft does not have a JDK, but they do provide a Java runtime for use with their OS.&lt;br /&gt;
&lt;br /&gt;
Note that support for the Microsft JVM is scheduled to end in 2007. Evidently at that time no more releases of the JVM will be available from Microsoft.&lt;br /&gt;
&lt;br /&gt;
As we're focusing on using the JDK, it is recommended that you go instead to [[Sun Java]] for the version appropriate to your OS and project.&lt;br /&gt;
&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
[http://microsoft.com/java Microsoft Java]&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:03:52 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Microsoft_Java</comments>		</item>
		<item>
			<title>Mac Java</title>
			<link>http://www.softwarebyjeff.com/index.php/Mac_Java</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sun Microsystems doesn't provide a Mac version of the JDK, but Apple does.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
Installation of the Java Software Development Kit on Mac OSX is easy--it's already there. Apple has included the JDK in every installation of OSX as a means to ensure the features are available to all Mac users.&lt;br /&gt;
&lt;br /&gt;
Updating the JDK included with the OS is done using the System Updater, and should be done automatically for most systems.&lt;br /&gt;
&lt;br /&gt;
Note that the version of the J2SE included is 1.4, and that there is no J2EE or J2ME release for Mac. The features of J2EE must be added as extra libraries, and using a Mac to develop J2ME applications will simply use the target features of the JDK to ensure unavailable J2SE features are not used.&lt;br /&gt;
&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
[http://www.apple.com/macosx/features/java/ Mac OSX Java Features]&lt;br /&gt;
&lt;br /&gt;
[http://developer.apple.com/java Mac Java Developer Resources]&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 18:01:55 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Mac_Java</comments>		</item>
		<item>
			<title>SVN</title>
			<link>http://www.softwarebyjeff.com/index.php/SVN</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Noted as a compelling alternative for [[CVS]] is SVN, or Subversion.&lt;br /&gt;
&lt;br /&gt;
[http://subversion.tigris.org Subversion Home]&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;/div&gt;</description>
			<pubDate>Tue, 18 Jan 2005 17:58:28 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:SVN</comments>		</item>
		<item>
			<title>Investigate Private Inner Classes</title>
			<link>http://www.softwarebyjeff.com/index.php/Investigate_Private_Inner_Classes</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You have a good reason for doing it, I'm sure. Making a private inner class, that is. There are plenty of good reasons, I'm not going to dispute that. There's trouble with private inner classes--you can't see them from the outside. OK, so maybe that's not a problem where you're from, but when you're working on a test-driven-development project, you need to be able to investigate the objects for testing purposes.&lt;br /&gt;
&lt;br /&gt;
Consider the simple class InnerClass, shown below. Of course, examples barely have reasons for existing; our inner class is a goofy Map like classs, except that it doesn't do anything to keep you from putting more than one object with the same key. OK, maybe some simple value. That's not the point. The point is that there are exposed simple methods to add things to the list and get a dumb dump of the contents of the list. There's no way to get something out of the list or make changes to the list. Further, you could try to get the entries List from the class, but it wouldn't do you any good because it's filled with instances of an unavailable inner class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class InnerClass {&lt;br /&gt;
&lt;br /&gt;
    private java.util.List entries = new java.util.ArrayList();&lt;br /&gt;
&lt;br /&gt;
    public void put(Object key, Object object) {&lt;br /&gt;
        entries.add(new MapLike(key, object));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void iterate() {&lt;br /&gt;
        java.util.Iterator entriesIterator = entries.iterator();&lt;br /&gt;
        while (entriesIterator.hasNext()) {&lt;br /&gt;
            MapLike mapLike = (MapLike) entriesIterator.next();&lt;br /&gt;
            System.out.println(mapLike.getKey().toString() + &amp;quot;: &amp;quot; + mapLike.getObject().toString());&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private class MapLike {&lt;br /&gt;
        private Object key;&lt;br /&gt;
&lt;br /&gt;
        private Object object;&lt;br /&gt;
&lt;br /&gt;
        public MapLike() {&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        public MapLike(Object key, Object object) {&lt;br /&gt;
            this.key = key;&lt;br /&gt;
            this.object = object;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        public Object getKey() {&lt;br /&gt;
            return key;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        public Object getObject() {&lt;br /&gt;
            return object;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, although I've said we're doing this for test-driven-development, I invalidate that argument by showing you the class we want to work with first. Let me defend myself by saying that I'm trying to show what kind of thing we want to test; conceptually it's hard to explain in a terse wiki entry, such as this. Now you've got an idea of what we really want to work with.&lt;br /&gt;
&lt;br /&gt;
Oh, and yes, I know it's not really null-safe. This is an example, and I'm choosing to not burden the class with too many checks. Checks that I'd consider necessary in the real world, but not here in example world.&lt;br /&gt;
&lt;br /&gt;
Consider the following test class. The class simply makes an instance of our InnerClass, sticks something (yes, useless) in there, and asks for it back. TDD justification for the class and its put() and iterate() methods.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class InnerClassTest extends junit.framework.TestCase {&lt;br /&gt;
    public void testInnerClass() {&lt;br /&gt;
        InnerClass innerClass = new InnerClass();&lt;br /&gt;
        innerClass.put(&amp;quot;Key1&amp;quot;, &amp;quot;Object1&amp;quot;);&lt;br /&gt;
        innerClass.iterate();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When we run the test we expect to see output like '''Key1: Object1''' in wherever the standard output goes. All is good. Yes, this is bad testing because it requires you to read the output, but it's an example, sheesh.&lt;br /&gt;
&lt;br /&gt;
Now, we still don't have a way to make sure that we've got the InnerClass$MapLike, or what it's doing. Since our class doesn't have any way for us to get to the List, or more specifically its contents, we've got to turn to our friend, java.lang.reflect. Through reflection we'll zip right past that private stuff, and get what we need.&lt;br /&gt;
&lt;br /&gt;
Before we go on, I need to proclaim that fundamentally, I'm all for respecting the Java accessor. I wouldn't introduce this into production code unless I had very, very good reason.&lt;br /&gt;
&lt;br /&gt;
Consider this test class. It instantiates a new InnerClass, puts an Object pair in its inner class. Then we use some Java reflection tools to get to the inner class's members. We utilize Field and Method to read variables and call methods that are otherwise unavailable to us because InnerClass is not sharing MapLike.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
public class PrivateInnerClassTest extends junit.framework.TestCase {&lt;br /&gt;
    public void testPrivateInnerClass() throws Exception{&lt;br /&gt;
        InnerClass innerClass = new InnerClass();&lt;br /&gt;
        innerClass.put(&amp;quot;Key2&amp;quot;, &amp;quot;Object2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        java.lang.reflect.Field entriesField = innerClass.getClass().getDeclaredField(&amp;quot;entries&amp;quot;);&lt;br /&gt;
        entriesField.setAccessible(true);&lt;br /&gt;
        java.util.List entries = new java.util.ArrayList((java.util.List) (entriesField.get(innerClass)));&lt;br /&gt;
        entriesField.setAccessible(false);&lt;br /&gt;
        assertFalse(entries.isEmpty());&lt;br /&gt;
&lt;br /&gt;
        boolean tested = false;&lt;br /&gt;
        Class[] declaredClasses = innerClass.getClass().getDeclaredClasses();&lt;br /&gt;
        for (int i = 0; i &amp;lt; declaredClasses.length; i++)&lt;br /&gt;
        {&lt;br /&gt;
            Class declaredClass = declaredClasses[i];&lt;br /&gt;
            if (declaredClass.getName().indexOf(&amp;quot;InnerClass$MapLike&amp;quot;) &amp;gt;= 0)&lt;br /&gt;
            {&lt;br /&gt;
&lt;br /&gt;
                java.lang.reflect.Method mapLikeMethodGetKey = declaredClass.getMethod(&amp;quot;getKey&amp;quot;, null);&lt;br /&gt;
                java.lang.reflect.Method mapLikeMethodGetObject = declaredClass.getMethod(&amp;quot;getObject&amp;quot;, null);&lt;br /&gt;
&lt;br /&gt;
                java.util.Iterator entriesIterator = entries.iterator();&lt;br /&gt;
                while (entriesIterator.hasNext())&lt;br /&gt;
                {&lt;br /&gt;
                    Object entry = entriesIterator.next();&lt;br /&gt;
&lt;br /&gt;
                    Object key = mapLikeMethodGetKey.invoke(entry, null);&lt;br /&gt;
                    Object object = mapLikeMethodGetObject.invoke(entry, null);&lt;br /&gt;
&lt;br /&gt;
                    System.out.println(&amp;quot;Stolen &amp;quot; + key.toString() + &amp;quot;: &amp;quot; + object.toString());&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                tested = true;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            assertTrue(tested);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we see three of our favorite bits of reflection: Fields, Methods and Classes. OK, so Class is just plain Java, but our use here is indicative of reflection.&lt;br /&gt;
&lt;br /&gt;
Let's look at the test bit by bit, in hunks where we don't need discrete disection.&lt;br /&gt;
&lt;br /&gt;
In the beginning, we do like we did in the last test. That is, we make and populate our InnerClass. We could at this point call iterate() to see the results, but I can assure you it'd print '''Key2: Object2''' to the standard output. We don't need to do that because the other test does that for us. &lt;br /&gt;
&lt;br /&gt;
Next we try to grab the List of entries from InnerClass. As this is a private member, we can't just ask for it, so we get it using the getDeclaredField() method, and the following few lines allow us to copy its contents into our own. We could, of course, keep using the reference to the class, but we politely return the field to inaccessible. &lt;br /&gt;
&lt;br /&gt;
Then the trickier part. We try to find the private inner class. We do this using the getDeclaredClasses() method. Unfortunately there's not a simple getDeclaredClass(String) method like there is for fields and methods. We then iterate this collection (we could assume it's the first one, because it is, but this technique allows us to expand or change the InnerClass class without breaking the test. Of course, one of the reasons we unit test is to protect us from breakage, which is why we check the tested boolean when we're done--it lets us know that we've found and looked at the inner class.&lt;br /&gt;
&lt;br /&gt;
Finally, iterating through the List of entries (one in this test) we use reflection to call our getters. Normally we wouldn't want to test getters, but since that's all this class gives us, that's what we have to do. The ouput will be similar to before, except this time it'll say '''Stolen Key2: Object2''' in the standard output.&lt;br /&gt;
&lt;br /&gt;
There you have the fundamentals for getting to the bits hidden in other classes, including items in classes inside classes. The rudimentary tests herein would get you admonished if you that that was enough for thorough testing. In reality we'd have checked the private fields of the private inner classes to make sure that they contained the values we wanted; again, it's testing setters, but it's also testing the use of the inner class MapLike by the outer class, InnerClass.&lt;br /&gt;
&lt;br /&gt;
Note a small extension of reflection would allow us to instantiate our own copies of MapLike outside of InnerClass, perhaps to do other operations on it. That's another interesting techinque for another wiki entry.&lt;/div&gt;</description>
			<pubDate>Fri, 14 Jan 2005 21:21:33 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Investigate_Private_Inner_Classes</comments>		</item>
		<item>
			<title>Java Tips and Tricks</title>
			<link>http://www.softwarebyjeff.com/index.php/Java_Tips_and_Tricks</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Much of the development we do is in Java. We have strong, experienced developers savvy in C/C++, PERL, and a variety of other contemporary languages or environments, too. This area is reserved for Java.&lt;br /&gt;
&lt;br /&gt;
[[Equals and HashCode]]&lt;br /&gt;
&lt;br /&gt;
[[Investigate Private Inner Classes]]&lt;br /&gt;
&lt;br /&gt;
[[Good Base Test Case]]&lt;br /&gt;
&lt;br /&gt;
[[Simple unit testing]]&lt;br /&gt;
&lt;br /&gt;
[[Dynamically Retrieve Method Name]]&lt;br /&gt;
&lt;br /&gt;
[[Starting with Struts]]&lt;br /&gt;
&lt;br /&gt;
[[Mimicking External Actions with EasyMock]]&lt;br /&gt;
&lt;br /&gt;
[[How Do Annotations Work?]]&lt;/div&gt;</description>
			<pubDate>Fri, 14 Jan 2005 20:03:07 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Java_Tips_and_Tricks</comments>		</item>
		<item>
			<title>CVS</title>
			<link>http://www.softwarebyjeff.com/index.php/CVS</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Concurrent Versions System is the ''de facto'' standard for Open Source version control. Mature and widely accepted, CVS offers not only keen source control for your project, but also access to almost every Open Source project on the Internet.&lt;br /&gt;
&lt;br /&gt;
CVS comes in two components within the same program, a server and a client. A server is almost necessary in the case where multiple computers will be used to access the repository. The client is necessary to access a repository, whether hosted on a server or within the computer's local file system.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
The best place to get the latest release of CVS is directly from [http://www.cvshome.org CVS Home]. CVS is available as a binary release for nearly every operating system including Microsoft Windows, Sun Solaris, LINUX, and Mac OSX, as well as AIX, SGI, and HP. It is also available in source form, if you prefer to build it yourself. You can download the files from the website, or quite conveniently, from their CVS repository.&lt;br /&gt;
&lt;br /&gt;
Most LINUX systems come with CVS pre-installed--it is a ''de facto'' standard. Try '''cvs --version''' from a shell before downloading to see if it's even necessary to install. Configuration may be required to host a repository, but not to use the client.&lt;br /&gt;
&lt;br /&gt;
=== Binary Installation ===&lt;br /&gt;
&lt;br /&gt;
You can simply download the appropriate binary archive for your operating system and platform, and extract the archive. In addition to some potential README information, you'll have the CVS executable. Copy this file to somewhere on your path, like '''/usr/local/bin'''. You may need to rename the file or symlink '''cvs''' to it. You can test its installation by typing '''cvs --version''' and you should be rewarded with the correct version.&lt;br /&gt;
&lt;br /&gt;
=== Source Installation ===&lt;br /&gt;
&lt;br /&gt;
The source installation is much more difficult than the binary installation, but it does offer the piece of mind that something unwanted didn't get slipped in there, and ensures that any external libraries needed match your system.&lt;br /&gt;
&lt;br /&gt;
Get the source from [https://ccvs.cvshome.org/servlets/ProjectDocumentList CVS Documents &amp;amp; files]. Uncompress the file into its source. Perform the standard &amp;quot;configure and make&amp;quot; steps. Your method of retreival may be different, and you may prefer another compression over gzip. Additionally, they have GPG signatures for their files, so you can verify that the file you download is the file they present. The basic steps are as noted below. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;wget https://ccvs.cvshome.org/files/documents/19/744/cvs-1.11.19.tar.gz&lt;br /&gt;
tar -xzf cvs-1.11.19.tar.gz&lt;br /&gt;
cd cvs-1.11.19&lt;br /&gt;
./configure&lt;br /&gt;
make&lt;br /&gt;
make install&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course, this requires you have the necessary tools to compile C code on your system.&lt;br /&gt;
&lt;br /&gt;
Instructions would vary for Microsoft Windows source.&lt;br /&gt;
&lt;br /&gt;
== Repository Creation ==&lt;br /&gt;
&lt;br /&gt;
Once installed, you can begin working with CVS immediately. Whether working with CVS using filesystem access or as a client-server configuration, you need to start with a repository.&lt;br /&gt;
&lt;br /&gt;
Very simply, make a directory to hold your repository and initialize it. CVS will do this for you in one easy step. For example, I used '''/var/cvs''' initialized as a CVS repository by issuing the command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvs -d /var/cvs init&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will create the appropriate CVS control directories and files in the specified directory. Note the directory doesn't need to exist first, CVS will create it, but you must have permissions to creat the directory in the location chosen.&lt;br /&gt;
&lt;br /&gt;
If you are going to use the '''pserver''' set-up, which we recommend even when working only with one system, this must be done on the system that will run the server, not your workstation.&lt;br /&gt;
&lt;br /&gt;
Note that the repository is the storage location for your projects. You can hold many projects in one repository. You are not required to separate projects into separate repositories. You may want to, for security reasons, for example. You many have more than one repository on each system, and the only thing to be careful of is that you don't put one inside another; unwanted access may occur to &amp;quot;inner&amp;quot; repositories. The discussions that follow are related to each repository; therefore if you have more than one you will have to repeat these steps for each repository.&lt;br /&gt;
&lt;br /&gt;
== Server Configuration ==&lt;br /&gt;
&lt;br /&gt;
Setting up a pserver is key to having a CVS system for multiple developers. It is not so vital to set up a separate server when working on one system. CVS has the ability to access the file system as a repository, as well as accessing a server-based repository. We recommend the pserver use, however, as it is the only way to enforce user access, and the only way that allows that access to be independent from the operating system security.&lt;br /&gt;
&lt;br /&gt;
Typically this is done on UNIX-like systems, including AIX, HP, LINUX, Solaris, and Mac OSX. Running a server on Microsoft Windows is discouraged by the folks at CVS, although it can be done, and we'll show you how.&lt;br /&gt;
&lt;br /&gt;
=== Configuration on LINUX, Mac, Solaris, etc ===&lt;br /&gt;
&lt;br /&gt;
The primary key to getting a server-based configuration is to get the system acting as the server to listen for incoming connections. There are two widely used network service controllers, and one or both may be used; inetd and xinetd. &lt;br /&gt;
&lt;br /&gt;
Additionally, it may be necessary to add or uncomment a line to the '''/etc/services''' file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvspserver 2401/tcp&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This configures the port, normally 2401, on which the system will listen for connections from CVS clients. You may use a different port if you wish, although you need to be consistent throughout the configuration and client use. Port 2401 is the ''de facto'' standard port, so it should be used if you're going to offer your software to the worldwide communtiy.&lt;br /&gt;
&lt;br /&gt;
==== inetd Configuration ====&lt;br /&gt;
&lt;br /&gt;
On many LINUX distributions this information is already provided in the appropriate configuration file, but has been commented out. Simply un-comment, or enter your own line with the appropriate information.&lt;br /&gt;
&lt;br /&gt;
In the '''/etc/inetd.conf''' file, look for or add a line, all together, that looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvspserver stream tcp nowait root /usr/local/bin/cvs -f --allow-root=/var/cvs pserver cvs&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This provides the network service listener the program to which it will hand connections on the configured port. The '''/usr/local/bin/cvs''' is determined by where you put cvs. The '''--allow-root''' parameter also is determined by where you wish your repository to be. More than one '''--allow-root parameter''' is allowed, separated by spaces. These directories are the only ones that CVS will allow the users access.&lt;br /&gt;
&lt;br /&gt;
Now restart inetd in accordance with your system's requirements, so it can recognize the changes made, and the CVS pserver will be available.&lt;br /&gt;
&lt;br /&gt;
===== Too Many allow-root Parameters =====&lt;br /&gt;
&lt;br /&gt;
Some implementations of inetd have a limit on the size of the lines allowed in the inetd.conf file, and since everything must be on one line in the file, it can limit what you can do with allowing more than a small number of roots. Note that CVS does not have this limitation, so providing it a very long list is not a problem.&lt;br /&gt;
&lt;br /&gt;
Instead of directly using the cvs command in the inetd.conf file, create a script that will invoke the CVS as a server with the correct parameters, and invoke the script, not cvs, in the inetd.conf.&lt;br /&gt;
&lt;br /&gt;
First create a script to call cvs with your desired parameters Something like the following:&lt;br /&gt;
&lt;br /&gt;
'''/usr/local/bin/cvsserver'''&lt;br /&gt;
&amp;lt;pre&amp;gt;#! /usr/bin/bash&lt;br /&gt;
&lt;br /&gt;
ALLOWROOTS=&amp;quot;--allow-root=/var/cvs \&lt;br /&gt;
 --allow-root=/usr/local/cvs&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/usr/local/bin/cvs -f $ALLOWROOTS pserver&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then make the inetd.conf file reference that script as this example shows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvspserver stream tcp nowait root /usr/local/bin/cvsserver cvs&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the call to the script is still short enough to make inetd happy. The script also provides an easy location to maintain the roots, and the maintainer does not need to have access to the inetd.conf file.&lt;br /&gt;
&lt;br /&gt;
==== xinetd Configuration ====&lt;br /&gt;
&lt;br /&gt;
Configuration using xinetd is similar; add a bit to the configuration file, restart the server. The configuration format is a little different, and that's about it. Following the discussion above for what the bits mean, the addition of the following lines should suffice to get the installation configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;service cvspserver&lt;br /&gt;
{&lt;br /&gt;
   port        = 2401&lt;br /&gt;
   socket_type = stream&lt;br /&gt;
   protocol    = tcp&lt;br /&gt;
   wait        = no&lt;br /&gt;
   user        = root&lt;br /&gt;
   passenv     = PATH&lt;br /&gt;
   server      = /usr/local/bin/cvs&lt;br /&gt;
   server_args = -f --allow-root=/var/cvs pserver&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This block can be added to the '''/etc/xinetd.conf''' file directly, or as some installations use, as a separate file in a directory, typically '''/etc/xinetd.d''' (which would be noted in the '''/etc/xinetd.conf''' file). This has the benefit of allowing one to maintain the cvspserver file without requiring access to the other xinetd configuration files.&lt;br /&gt;
&lt;br /&gt;
Like the inetd discussion above, many roots are allowed, simply by adding them to the server_args line. Unlike inetd, xinetd does not seem to have the same length limit so the use of the script seems to be unnecessary.&lt;br /&gt;
&lt;br /&gt;
Now restart xinetd with the appropriate steps for your system.&lt;br /&gt;
&lt;br /&gt;
=== Configuration on Microsoft Windows ===&lt;br /&gt;
&lt;br /&gt;
When using CVS as a client on Microsoft Windows, the process is no more difficult than on other platforms. A precompiled binary is available from [https://ccvs.cvshome.org/servlets/ProjectDocumentList CVS Documents &amp;amp; files]. Download, unzip, and rename the resulting file. Put the file somewhere in your path, and you're now able to connect to CVS repositories.&lt;br /&gt;
&lt;br /&gt;
Using Microsoft Windows as a repository host is only recommended with file access. Paired with Window's built-in peer-to-peer networking, mapping to the repository file location should be easy to do, and will allow version control. It does not, however, offer any of the authentication and protection of the pserver method used by the rest of the CVS servers.&lt;br /&gt;
&lt;br /&gt;
The [http://cvshome.org CVS] software doesn't provide a server that operates natively on Windows. Their [https://www.cvshome.org/dev/codewindow.html documentation] points us instead to [http://cvsnt.org CVSNT].&lt;br /&gt;
&lt;br /&gt;
Installation is as with any Microsoft program, run the downloaded program and click the options you desire. If you install with administrative permissions, you'll be prompted to install CVSNT as service. Do this, and you're gold.&lt;br /&gt;
&lt;br /&gt;
== CVS Filesystem Security ==&lt;br /&gt;
&lt;br /&gt;
As with any other files on your system, some attention must be paid to filesystem security. In the case of CVS there are three things to note for each repository.&lt;br /&gt;
&lt;br /&gt;
The repository's CVSROOT directory needs only be readable by cvs and the maintainer.&lt;br /&gt;
&lt;br /&gt;
Note that while the configuration examples use the user '''root''', it should be noted that you can use a different user to run the CVS server. This is recommended for all services, not just CVS.&lt;br /&gt;
&lt;br /&gt;
Instead of allowing root to run cvs (which is not required since the port it uses is out of the root-only range), make a proxy user, which we name '''cvsproxy''' and in its own group, also named '''cvsproxy'''. Remove login access from the account (set it to the common '''/usr/bin/false''' script that promptly exits when login is attempted).&lt;br /&gt;
&lt;br /&gt;
Once you've created the cvsproxy user (and presumably matching group), change your inetd or xinetd configuration to use this user. Now if malicious code is attempted to be executed via the CVS pserver connection, it will be limited to whatever you give the cvsproxy user. If done correctly, this should limit the potential damage to only CVS files.&lt;br /&gt;
&lt;br /&gt;
Also, now that you have a user with which to limit access, you should limit the access. Change the owner of the CVS repository to the proxy user, with the command like '''chmod -R cvsproxy:cvsproxy /var/cvs''', using, of course, the appropriate names for the user, group, and directory. Do this for each of your repositories.&lt;br /&gt;
&lt;br /&gt;
Now that cvsproxy owns the repository directory, it should be protected from other users on the system. Generally, if you use CVS to create the directory structure, and you configure all of your CVS users as members of the cvsproxy group, or as we see, proxied to the cvsproxy user, no additional changes are required. Using CVS to control the files should automatically generate files and directories with the correct permissions.&lt;br /&gt;
&lt;br /&gt;
== User Configuration ==&lt;br /&gt;
&lt;br /&gt;
If you don't want to create user accounts for everyone who may need access to your CVS repository, there's a simple technique for allowing users to have only CVS accounts. We recommend this technique even if users have account access to the system on which the server will run. It offers protection from malicious CVS use.&lt;br /&gt;
&lt;br /&gt;
First, a user account must exist on the system that CVS can use instead. For example, we previously created a user named '''cvsproxy''', and give that user next to no access on the system.&lt;br /&gt;
&lt;br /&gt;
Then in the CVSROOT directory under the directory noted in the appropriate '''--allow-root''' parameter (in our case '''/var/cvs/CVSROOT'''), create a file named '''passwd'''. Also, in the same directory, edit the '''config''' file, finding the line that says '''SystemAuth=no''' and make sure it is uncommented (remove the # symbol). This now requires you to have an entry in the password file for users of CVS, and no longer allows any user on the system access by defult.&lt;br /&gt;
&lt;br /&gt;
The password file, '''passwd''', should have one line for each user in the form of '''user id''':'''encrypted password''':'''proxy''' where '''proxy''' is a real account id. If you wish to allow access to a user who already has an account, put their user ID in both places (or just the '''proxy''' position, if they wish a different ID for CVS), and copy their encrypted password from the '''/etc/passwd''' file. The '''encrypted password''' may be left blank, but that would mean that the user has no password, which is not recommended.&lt;br /&gt;
&lt;br /&gt;
In the case of users without a regular system account, you need to make an encrypted version of the password you wish to assign. Two simple tricks exist. The easy one is to change the password of the proxy user (in my example, '''cvsproxy'''). Since that user cannot log in anyway, their password is irrelevant. Use the '''passwd cvsproxy''' command as root to set a new password for the user, then copy it from the '''/etc/passwd''' file into the '''CVSROOT/passwd''' file. You can do the same thing by creating a new user, copy the password, and then delete the user.&lt;br /&gt;
&lt;br /&gt;
Optionally, while not requiring root privileges, an installation of PERL must exist, which is also the norm on LINUX and the like, this one-liner will generate an encrypted password favorable to your system. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;perl -e 'print crypt &amp;quot;PASSWORD&amp;quot;, &amp;quot;12&amp;quot;; print &amp;quot;\n&amp;quot;'&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Substitute '''PASSWORD''' with whatever is desired, and '''12''' with any two characters as a random seed. This will display the encrypted password in the console, and you can enter that string in the '''CVSROOT/passwd''' file.&lt;br /&gt;
&lt;br /&gt;
Now that users have access, it is important to note that without doing anything else everyone has the same access: read and write. While this may be desirable, it may be the case that you have the need to control who can read and who can write, or to allow anonymous, read-only access.&lt;br /&gt;
&lt;br /&gt;
Read-only access can be accomplished by putting the user's ID in a file in the CVSROOT directory named '''readers'''. Any user listed in this file will be allowed only read access, and cannot contribute to the repository. Any one not listed in the readers file will be allowed write access.&lt;br /&gt;
&lt;br /&gt;
Optionally, you can create a list for the users with write access, if that group is smaller, by making a file in the CVSROOT directory named, of course, '''writers'''. Anyone listed in this file will have write access to the repository. Anyone not listed in this file, if it exists, will have read-only access.&lt;br /&gt;
&lt;br /&gt;
You can have both files, and if a user is listed in both, the safety of the repository will be considered first, and the user will be considered read-only.  We recommend only listing readers, as the maintenance of writers can get cumbersome.&lt;br /&gt;
&lt;br /&gt;
To allow anonymous access, then, create an anonymous user in the passwd file by putting the user ID line of '''anonymous::cvsproxy''' noting there is no password, using whatever name you wish to have, although anonymous is the standard. Then create the readers file and include '''anonymous''' in there. All other users will have write access, but anonymous users will be able to only retreive from the repository.&lt;br /&gt;
&lt;br /&gt;
Let's therefore imagine we want to set up anonymous access to our repository with one real user (realuser) and one CVS only user (cvsonlyuser), using the procedures described above. Then your two files might look like this.&lt;br /&gt;
&lt;br /&gt;
'''/var/cvs/CVSROOT/passwd'''&lt;br /&gt;
&amp;lt;pre&amp;gt;anonymous::cvsproxy&lt;br /&gt;
cvsonlyuser:PWnf8nahwod:cvsproxy&lt;br /&gt;
realuser:HwEInp20ngpw:realuser&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''/var/cvs/CVSROOT/readers'''&lt;br /&gt;
&amp;lt;pre&amp;gt;anonymous&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that each file must end with an empty line; that is the last line should have a &amp;quot;new line&amp;quot; on it. &lt;br /&gt;
&lt;br /&gt;
== Quick Client Overview ==&lt;br /&gt;
&lt;br /&gt;
This is not intended to be a thorough investigation of CVS. Our use of CVS will be limited primarily to authentication, creating projects in our repositories, retrieving files from a repository, presumably to work on, and returning them to the repository, as new versions. Much of this is integrated into our IDEs, which is discussed in more detail. However, we will be using CVS in some scripts, like our [[Ant]] build scripts, so a brief overview is warranted.&lt;br /&gt;
&lt;br /&gt;
=== Connection and Authentication ===&lt;br /&gt;
&lt;br /&gt;
Once the pserver is setup and configured, connecting to the repository should be  the next test. There are two ways to do this, and the difference depends on how you want to note the server to which you're connecting.&lt;br /&gt;
&lt;br /&gt;
The primitive use of CVS for a filesystem-based access looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvs -d /repository command&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While the primitive use of CVS for pserver-based access looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvs -d :pserver:user@host:/repository command&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where ''user'' is the username used to authenticate the user (pserver only), ''host'' is the system name to which we're connecting (which may be localhost if we're in a one-system setup, again pserver only), and ''/repository'' is the actual path to the CVS repository (in our examples so far we've used '''/var/cvs'''). We say ''command'' although there may be more than one word there, depending on what it is you're trying to do.&lt;br /&gt;
&lt;br /&gt;
Before we go on, the ''-d :pserver:user@host:/repository'' can become cumbersome to type with every CVS command. This can be shortcut by setting an environment variable named CVSROOT, allowing this to be skipped in the command, as thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:user@host:/repository&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cvs command&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that unless overridden by using a &amp;quot;-d&amp;quot; string, all subsequent CVS commands will access the same repository. This is a blessing when always working with one repository, and the set/export can even be added to your login profile if it makes sense. It can be a curse, however, when working with more than one repository, as it often causes confusion.&lt;br /&gt;
&lt;br /&gt;
Note also that you can specify the password with the user ID by putting both in the &amp;quot;user&amp;quot; portion of the CVSROOT, separated by another colon, as in '''user:password'''. While there are risks of doing this (for one, your password ends up in your shell's command history), there are benefits as you are not prompted for a password. We recommend using this only when there is no password, like when using anonymous CVS access.&lt;br /&gt;
&lt;br /&gt;
With this understanding, let us login now to our newly configured server, using our example setup above. Note that logging in is only necessary for pserver use, and not for local filesystem access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvs -d :pserver:anonymous@localhost:/var/cvs login&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will be prompted for a password, to which you would just hit enter as there isn't one for our anonymous user. CVS should respond silently unless there is an error, in which case there would be an error message.&lt;br /&gt;
&lt;br /&gt;
You are authenticated with the server, and will remain so until you explicitly log out. A file is created in your home directory named .cvspass, containing the authentication key to the server that is valid for the life of the file.&lt;br /&gt;
&lt;br /&gt;
You do not need to be authenticated for any period of time longer than your CVS activity. That is, you can authenticate, check out, disconnect, and work without  a problem. Then you would authenticate, check in, and disconnect when finished with your bit of work.&lt;br /&gt;
&lt;br /&gt;
=== Project Creation ===&lt;br /&gt;
&lt;br /&gt;
Creating projects in the repository is, obviously, only necessary when making new projects. This can be done in a couple of ways; either from scratch or from an existing project structure.&lt;br /&gt;
&lt;br /&gt;
==== Using existing Projects ====&lt;br /&gt;
&lt;br /&gt;
From scratch is very straight forward. On your system, navigate to the directory in which you wish to work. For example, we'll go to '''~/projects''' and consider this our local project root. In this root directory, we have a project directory for our project already started, for example, '''~/projects/killerapp'''. Inside that directory are our source files, images, and other resources necessary for our application.&lt;br /&gt;
&lt;br /&gt;
Once we've authenticated with the server, we can add our project and its files to the repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cvs login&lt;br /&gt;
cd ~/projects/killerapp&lt;br /&gt;
cvs import -m &amp;quot;Creating Project&amp;quot; killerapp creation start&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CVS import command does the work of creating the project in the repository. It will recurse through all of the directories in your current working directory, and create all of the directories and files. It will do its best to determine text and binary files, and when completed, the repository will contain the contents of your current directory and the CVS files required to maintain the history and version control. Depending on the sise of your existing project,  and the speed of your server and network connection, this may take a while.&lt;br /&gt;
&lt;br /&gt;
Let's look in detail at the parameters. &lt;br /&gt;
&lt;br /&gt;
The optional -m parameter, followed by a comment, will put the comment in the CVS history. Strictly, the comment is not optional, although it can be blank, and if not provided with the -m parameter CVS will pause and prompt you to enter one. &lt;br /&gt;
&lt;br /&gt;
The ''killerapp'' in the import command is the project path in the CVS root that will be created. This does not need to match your directory name in any fashion, and is strictly for later use in identifying the This can be a deeper path (i.e. killerapp/source), but this is reflective of the directory created on the CVS repository, not the directory currentl working in. That is, it will create a path on the CVS repository matching this string and then insert your directories and files there. In our example, the directory '''/var/cvs/killerapp''' will be created on our server. Had we used the killerapp/source, the directory '''/var/cvs/killerapp/source''' would have been created and used.&lt;br /&gt;
&lt;br /&gt;
The ''creation'' in the import command is the ''vendor'' tag. This is just a string carried with the project in CVS. It is used in the CVS reporting, and should contain something more useful. If you want to use a string with spaces, simply put the tag in quites, as we did with &amp;quot;Creating Project&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Likewise, the ''start'' tag in the import command is the ''release'' tag. This is also carried with the project in CVS, and used in reporting, and project milestones, and can contain something more useful.&lt;br /&gt;
&lt;br /&gt;
==== Projects from Scratch ====&lt;br /&gt;
&lt;br /&gt;
Creating projects from scratch is not unlike the above existing projects, except that you'll be starting in an empty directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
mkdir -p ~/projects/killerapp&lt;br /&gt;
cd ~/projects/killerapp&lt;br /&gt;
cvs login&lt;br /&gt;
cvs import -m &amp;quot;Creating Project&amp;quot; killerapp creation start&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This import will go very quickly, as there are no files to import. However, the repository exists, and while empty, it can now be used to manage your project.&lt;br /&gt;
&lt;br /&gt;
=== Checking out ===&lt;br /&gt;
&lt;br /&gt;
Checking out files from CVS is really pulling the files from the repository. It does not specifically mark them in CVS as files that you will edit. &lt;br /&gt;
&lt;br /&gt;
Very simply call the checkout command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
mkdir ~/workspace&lt;br /&gt;
cd ~/workspace&lt;br /&gt;
cvs login&lt;br /&gt;
cvs checkout killerapp&lt;br /&gt;
cd killerapp&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will copy everything in the /var/cvs/killerapp directory into your ~/workspace/killerapp directory, creating the entire directory structure and all files previously committed to the repository. Inside the directory will also be CVS directories. These are internal directories used by your local CVS to coordinate your local file structure with the server's structure. This helps CVS, for example, know which files you modified, so at check-in time, only those files are committed.&lt;br /&gt;
&lt;br /&gt;
If this was done immediately after the project creation above, the directory structures should match, with the execption of the additional CVS directories in the ~/workspace/killerapp tree. At this point it is safe to remove or archive the ~/projects/killerapp tree as the files are stored in CVS.&lt;br /&gt;
&lt;br /&gt;
You would at this point work in the ~/workspace/killerapp directory as you would have previously worked in ~/projects/killerapp, except now the files are part of the version control system, and can more effectively be shared with other developers.&lt;br /&gt;
&lt;br /&gt;
Note that the checkout command has several options, allowing control over which files are retreived, which we are not going to discuss here, as this is a brief overview. See the additional resources below for detailed information.&lt;br /&gt;
&lt;br /&gt;
=== Working on Files ===&lt;br /&gt;
&lt;br /&gt;
Very simply, make whatever changes you find necessary. CVS, unlike some source control systems, does not restrict access to the files in your local workspace. It uses the internal CVS directories in each source directory to ascertain changes when you are ready to commit them to the repository.&lt;br /&gt;
&lt;br /&gt;
Feel free to add and alter files as necessary.&lt;br /&gt;
&lt;br /&gt;
=== Adding New Work ===&lt;br /&gt;
&lt;br /&gt;
Through the course of work you'll find that you need to add new files and directories to the repository. In your workspace, simply create the directories and files as you find it necessary. As you create the new items, either immediately (so you don't forget) or in bulk when you're ready, simply use the add command to add the files to the queue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cd ~/workspace/killerapp&lt;br /&gt;
mkdir something&lt;br /&gt;
touch something/somefile&lt;br /&gt;
cvs login&lt;br /&gt;
cvs add something&lt;br /&gt;
cvs add something/somefile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This simply queues the files and directories for addition to the repository, it does not actually add them at this point! Important to remember, also is that the command is not recursive; you must specify each file individually.&lt;br /&gt;
&lt;br /&gt;
=== Removing and Moving files ===&lt;br /&gt;
&lt;br /&gt;
Removing is very simple. Simply delete the file you no longer wish to have, and issue the '''cvs remove''' file in that directory, or a parent directory. This will note the change and CVS will disregard the file in future check-outs and updates, but maintain the file's versioning history.&lt;br /&gt;
&lt;br /&gt;
Moving or renaming files is almost as simple. Simply copy the file to the new location and delete it from the old location (or move it). Then issue a remove command to tell CVS to forget the old file, and an add command to tell CVS about the new file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cd ~/workspace/killerapp&lt;br /&gt;
cvs login&lt;br /&gt;
mv something/somefile something/otherfile&lt;br /&gt;
cvs remove&lt;br /&gt;
cvs add something/otherfile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Based on the previous example, the something/somefile exists, but we want to rename it something/other file. The remove command is recursive, so it will see the somefile is missing from the something directory, and make note of your intention to delete it. The add is not, so we must explicitly tell it which file we're adding.&lt;br /&gt;
&lt;br /&gt;
=== Refreshing Work ===&lt;br /&gt;
&lt;br /&gt;
CVS does not automatically synchronize the repository's contents to your system. You must tell CVS when you want to refresh your directory structure. This is done with the update command. It is also recursive, and will update from where you are in the directory structure, and not from the top. It is recommended, therefore, to do the update from the root of your project.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cd ~/workspace/killerapp&lt;br /&gt;
cvs login&lt;br /&gt;
cvs update -d -P&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By default, update is fairly gentle on your system. It removes files that have been marked as deleted in the repsository, adds new files from the repository, and updates existing and modified files. All of this work is only done in the directories previously existing. We need to use the -d and -P to ensure that directory changes are included in our update.&lt;br /&gt;
&lt;br /&gt;
The -d parameter adds new directories from the repository, and the files within them, recursing as necessary.&lt;br /&gt;
&lt;br /&gt;
The -P parameter removes any directories that have been deleted from the repository. More correctly, it removes empty directories, which is how CVS determines they've been deleted.&lt;br /&gt;
&lt;br /&gt;
At the completion of the update, your local structure should look like the repository, plus your changes. It's a good practice to do an update before you attempt to commit. This will help you avoid conflicts and keep missing work from other developers at a minimum.&lt;br /&gt;
&lt;br /&gt;
==== Conflict Handling ====&lt;br /&gt;
&lt;br /&gt;
As much as possible, CVS update will try to merge any changes made in files that you have modified that have also been modified in the repository since your last update. It does this by fairly robust, albiet not perfect, comparison. It may be the case that the resulting merge is not easy enough for CVS to do. Typically this is reserved for changes CVS perceives in the same location of a file. Merge also does not work on binary files. In this case CVS may complain and you'll have to do this merge manually. Inside the IDEs, this is more easily performed.&lt;br /&gt;
&lt;br /&gt;
Another thing to keep in mind, is that while the update merge may not find any conflicts, it might introduce merges that do not make computational sense. It is important, therefore, to review files that have merges in them, even if the merge did not report any errors or conflicts.&lt;br /&gt;
&lt;br /&gt;
Outside an IDE, however, we must do the merge manually. When CVS merges the file, it will mark conflicts with '''&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;''' and '''&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;''' markers to note what lines were in the repository and what lines are in your file. Left alone, these should cause compiler errors, at the least, helping you find the files that have merge errors. You can edit the lines as you see fit. Since the update is done, CVS will assume any change you make is to the version last committed to the repository.&lt;br /&gt;
&lt;br /&gt;
If you want to check before you update, to see what merge issues may result, CVS provides a tool for determining the differences in a file, as a method for you to find and correct doverlapping work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cd ~/workspace/killerapp&lt;br /&gt;
cvs login&lt;br /&gt;
cvs diff -w something/somefile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The -w tells CVS to ignore whitespace, to help reduce space-vs-tab conflicts and other formatting style changes.&lt;br /&gt;
&lt;br /&gt;
Note that the diff command is not recursive, so you must provide the file that you wish to compare.&lt;br /&gt;
&lt;br /&gt;
The command will display the lines were added or deleted or changed in either or both files. You then have to edit your local copy to either match or otherwise affect it to be correct.&lt;br /&gt;
&lt;br /&gt;
==== File Locking ====&lt;br /&gt;
&lt;br /&gt;
One alternative, that really removes the &amp;quot;C&amp;quot; from CVS, is to lock files instead of opening the system. In this case, a file can only be edited by one developer at a time. This is enforced by making the files read-only when checked out, and forcing developers to mark individual files as they modify them.&lt;br /&gt;
&lt;br /&gt;
First the repository needs to be so marked using the CVS watch command.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cd ~/workspace/killerapp&lt;br /&gt;
cvs login&lt;br /&gt;
cvs watch on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The watch command is recursive, so you should do this from the root of the project. You also only need to do it once, not whenever you access the repository. It is also possible to use watch on only specific files, for example, files that don't merge well like binaries or configuration files.&lt;br /&gt;
&lt;br /&gt;
Then to edit the file, simply issue the CVS edit command on the file. This will check to see if the file is available, and if so, let you edit the file. The file will remain locked as yours until you commit the file, at which time your lock will be freed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvs edit something/somefile&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If someone else has a file locked, you will be alerted, and &lt;br /&gt;
&lt;br /&gt;
If you decide you don't need to edit the file, you can undo the checkout with an unedit command. This will revert your local copy of the file to what is in the repository, and release your lock on the file, allowing other developers access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;cvs unedit something/somefile&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Checking in ===&lt;br /&gt;
&lt;br /&gt;
When you're ready you can commit your work to the repository. This can be done on individual files, for example as you create new directories, or on the whole project structure. CVS keeps track of the proper repository structure, so you don't even need to worry about which directory you're in, as long as you're somewhere within the project. Well, OK, if you have changes within different directories, it is important to remember that CVS only works from your current directory and deeper into the structure; it will not recurse to the top of your project and commit for you.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cd ~/workspace/killerapp&lt;br /&gt;
cvs login&lt;br /&gt;
cvs commit -m &amp;quot;Relevant Message&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will recurse through our directory structure finding files we've changed and add, remove, or update them as appropriate based on previous cvs commands and file modifications.&lt;br /&gt;
&lt;br /&gt;
The commit command, like the import, requires a message to add to the check-in. The message may be blank (&amp;quot;&amp;quot;), but that is discouraged. If you do not provide a -m parameter, you'll be provided with an editor in which to enter your comments.&lt;br /&gt;
&lt;br /&gt;
When to check-in is a topic of much debate. In iterative, test-driven development, it should be often, but after a successful compile and run of your code. It is certainly noteworthy that no one else can see your additions and changes until you commit your changes.&lt;br /&gt;
&lt;br /&gt;
=== Disconnecting ===&lt;br /&gt;
&lt;br /&gt;
When you're done using the repository, simply disconnect by logging out.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;set CVSROOT=:pserver:cvsonlyuser@localhost:/var/cvs&lt;br /&gt;
export CVSROOT&lt;br /&gt;
cvs logout&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will remove the information related to this repository from the .cvspass file in your home directory. Future CVS interactions will result in authentication errors until you reconnect and login.&lt;br /&gt;
&lt;br /&gt;
Note that you can span tremendous periods of times between logins and logouts. It has nothing to do with your network session or even the lifespan of the CVS server. The entry made in the .cvspass file simply does the authentication key for you each time CVS commands are issued.&lt;br /&gt;
&lt;br /&gt;
== IDE Integration ==&lt;br /&gt;
&lt;br /&gt;
CVS is integrated in most of the popular IDEs available. It is integrated into both Eclipse and NetBeans, which we whole heartedly recommend for both Java and C/C++ development. The application of the process is similar for each, and discussed in detail in the corresponding [[Eclipse]] and [[NetBeans]] sections.&lt;br /&gt;
&lt;br /&gt;
== Worldwide Use ==&lt;br /&gt;
&lt;br /&gt;
As mentioned, CVS is the Open Source ''de facto'' standard. All of our favorite Open Source repositories use anonymous CVS, allowing us to keep our local copies of their source up-to-date with their releases.&lt;br /&gt;
&lt;br /&gt;
[http://apache.org Apache Foundation] has direct access to their sources (CVSROOT=:pserver:anoncvs:@cvs.apache.org:/home/cvspublic). This is important as many Java tools we will be using come from Apache.&lt;br /&gt;
&lt;br /&gt;
The collections at [http://sf.net SourceForge], and [http://freshmeat.net FreshMeat] both have CVS access to download the sources of the projects that they host or participate in.&lt;br /&gt;
&lt;br /&gt;
Even the source for the IDEs Eclipse (CVSROOT=:pserver:anonymous:@dev.eclipse.org:/home/eclipse) and NetBeans (CVSROOT=:pserver:anoncvs:@cvs.netbeans.org:/cvs) are available via CVS. &lt;br /&gt;
&lt;br /&gt;
Other source, not being discussed here is available, too, like Mozilla's browsers and other clients (CVSROOT=:pserver:anonymous:@cvs-mirror.mozilla.org:/cvsroot).&lt;br /&gt;
&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
[https://www.cvshome.org/docs/manual CVS on-line manual]&lt;br /&gt;
&lt;br /&gt;
[https://ccvs.cvshome.org/servlets/ProjectDocumentList CVS &amp;quot;cederqvist&amp;quot; documentation]&lt;/div&gt;</description>
			<pubDate>Fri, 17 Dec 2004 22:11:26 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:CVS</comments>		</item>
		<item>
			<title>Ant</title>
			<link>http://www.softwarebyjeff.com/index.php/Ant</link>
			<description>&lt;p&gt;Summary: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [http://ant.apache.org/ Apache Ant] project offers very powerful methods for creating, maintaining, and even distributing software. Its XML build file format is intuitive once you're familar with it, and its acceptance by the community is widespread enough that Ant is integrated into the most common Open Source development environments.&lt;br /&gt;
&lt;br /&gt;
== Ant File Format ==&lt;br /&gt;
&lt;br /&gt;
Straight from the Ant documentation's [http://ant.apache.org/manual/using.html#example Example Buildfile] we see a simple build file that works for most simple projects without modification. This   text is put into a file named build.xml in the root of the project. It assumes the source is in a directory named &amp;quot;src&amp;quot; and it does the rest for us.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;project name=&amp;quot;MyProject&amp;quot; default=&amp;quot;dist&amp;quot; basedir=&amp;quot;.&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;description&amp;gt;&lt;br /&gt;
        simple example build file&lt;br /&gt;
    &amp;lt;/description&amp;gt;&lt;br /&gt;
  &amp;lt;!-- set global properties for this build --&amp;gt;&lt;br /&gt;
  &amp;lt;property name=&amp;quot;src&amp;quot; location=&amp;quot;src&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;property name=&amp;quot;build&amp;quot; location=&amp;quot;build&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;property name=&amp;quot;dist&amp;quot;  location=&amp;quot;dist&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;target name=&amp;quot;init&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;!-- Create the time stamp --&amp;gt;&lt;br /&gt;
    &amp;lt;tstamp/&amp;gt;&lt;br /&gt;
    &amp;lt;!-- Create the build directory structure used by compile --&amp;gt;&lt;br /&gt;
    &amp;lt;mkdir dir=&amp;quot;${build}&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;/target&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;target name=&amp;quot;compile&amp;quot; depends=&amp;quot;init&amp;quot;&lt;br /&gt;
        description=&amp;quot;compile the source &amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;!-- Compile the java code from ${src} into ${build} --&amp;gt;&lt;br /&gt;
    &amp;lt;javac srcdir=&amp;quot;${src}&amp;quot; destdir=&amp;quot;${build}&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;/target&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;target name=&amp;quot;dist&amp;quot; depends=&amp;quot;compile&amp;quot;&lt;br /&gt;
        description=&amp;quot;generate the distribution&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;!-- Create the distribution directory --&amp;gt;&lt;br /&gt;
    &amp;lt;mkdir dir=&amp;quot;${dist}/lib&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file --&amp;gt;&lt;br /&gt;
    &amp;lt;jar jarfile=&amp;quot;${dist}/lib/MyProject-${DSTAMP}.jar&amp;quot; basedir=&amp;quot;${build}&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;/target&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;target name=&amp;quot;clean&amp;quot;&lt;br /&gt;
        description=&amp;quot;clean up&amp;quot; &amp;gt;&lt;br /&gt;
    &amp;lt;!-- Delete the ${build} and ${dist} directory trees --&amp;gt;&lt;br /&gt;
    &amp;lt;delete dir=&amp;quot;${build}&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;delete dir=&amp;quot;${dist}&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;/target&amp;gt;&lt;br /&gt;
&amp;lt;/project&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To execute a build of this project it is simply done from the command line. Assuming Ant has been installed correctly and is in the execution path of the shell, simply type '''ant''' in the directory with the build.xml and the project will be comipled and a Java archive file (.jar file) will be built in the &amp;quot;dist&amp;quot; directory. &lt;br /&gt;
&lt;br /&gt;
== IDE integration ==&lt;br /&gt;
&lt;br /&gt;
Eclipse has used Ant as its build tool for some time. Its easy integration into the IDE is such that one doesn't even need to do anything and the tracking of changes and building of dependencies is done for you.&lt;br /&gt;
&lt;br /&gt;
NetBeans has had an Ant plug-in for some time, but as of Version 4 integrates Ant by default as its project management tool.&lt;/div&gt;</description>
			<pubDate>Fri, 17 Dec 2004 22:00:35 GMT</pubDate>			<dc:creator>Jekewa</dc:creator>			<comments>http://www.softwarebyjeff.com/index.php/Talk:Ant</comments>		</item>
	</channel>
</rss>
