Tuesday, September 14, 2010

Tynt programming assignment (Part 5)

Here is my implementation of a Hash collection for Integers:
package com.tynt.app.util;

public class IntegerHash {
public static final int BUFFER_SIZE = 100;

private IntegerList values;
private IntegerList valueCounts;

public IntegerHash() {
values = new IntegerList();
valueCounts = new IntegerList();
}

public boolean hasInteger(Integer value) {
if (value == null) {
return false;
}
for(int i=0;i < values.size();i++) {
if (values.get(i).compareTo(value) == 0) {
return true;
}
}
return false;
}

public Integer getIntegerCount(int index) {
return valueCounts.get(index);
}

public Integer getIntegerIndex(Integer value) {
for(int i=0;i < values.size();i++) {
if (values.get(i).compareTo(value) == 0) {
return i;
}
}
return -1;
}

public void incrementIntegerCount(Integer value) {
int integerIndex = getIntegerIndex(value);
valueCounts.put(integerIndex, getIntegerCount(integerIndex)+1);
}

public void addInteger(Integer value) {
if (!hasInteger(value)) {
values.concatenate(value);
valueCounts.concatenate(1);
} else {
incrementIntegerCount(value);
}
}

public IntegerList getIntegers() {
IntegerList vals = new IntegerList();
for(int i = 0; i < values.size(); i++) {
vals.concatenate(values.get(i));
}
return vals;
}

public Integer getIntegerFrequency(Integer value) {
if (hasInteger(value)) {
int index = getIntegerIndex(value);
return getIntegerCount(index);
} else {
return 0;
}
}
}

As you can see, I re-used the IntegerList collection in the Hash collection. Since I am not sorting the IntegerList, I have to iterate through the entire key IntegerList to check for a key in the hash, with a sorted key IntegerList, I could short circuit after the key was larger than the sorted key it was compared with. I am also not implementing a hash algorithm at all, so the collection was not named very well. Implementing a true hash collection was not part of the assignment so I didn't worry about it.

Here is assignment5 using the above collection:
package com.tynt.app;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import com.tynt.app.util.*;

public class Assignment5 implements InputReader {

public String readFile(InputStream is) {
BufferedReader d = new BufferedReader(new InputStreamReader(is));
String line;
IntegerHash unsortedHash = new IntegerHash();
try {
line = d.readLine();
while (line != null) {
Integer value = null;
try {
value = new Integer(Integer.parseInt(line));
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
unsortedHash.addInteger(value);
line = d.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
StringBuilder sb = new StringBuilder();
IntegerList sortedList = IntegerList.quicksort(unsortedHash.getIntegers());
if (sortedList == null) {
sortedList = new IntegerList();
}
for (int i=0;i < sortedList.size();i++) {
sb.append(sortedList.get(i));
sb.append(" - ");
sb.append(unsortedHash.getIntegerFrequency(sortedList.get(i)));
sb.append(NEW_LINE);
}
return sb.toString();
}

}

Finally, with all of the Assignment objects implemented and the tests passing, I can implement the Driver for the project:
package com.tynt.app;

import java.io.*;
import com.tynt.app.util.*;

/**
* Tynt programming challenge
*
* @author Four Gables Guy
*
*/
public class App {
public static final String header = "********************";

public static void main(String[] args) throws IOException {
String choice = displayChoices();
while (choice.equals("1") || choice.equals("2") || choice.equals("ALL")) {
parseChoice(choice);
if (choice.equals("ALL")) {
break;
}
choice = displayChoices();
}
}

public static void parseChoice(String choice) throws IOException {
if (choice.equals("1")) {
doAssignment2();
} else if (choice.equals("2")) {
doAssignment3();
} else if (choice.equals("ALL")) {
doAssignment2();
doAssignment3();
}
}

public static String displayChoices() {
System.out.println(header);
System.out.println("Enter 1 to parse " + InputReader.INPUT_FILE
+ " and print numbers sorted ascending.");
System.out.println("Enter 2 to parse " + InputReader.INPUT_FILE
+ " and print numbers sorted ascending.");
System.out.println("Enter any other characters to quit.");
System.out.println(header);
Console console = System.console();
if (console == null) {
return "ALL";
} else {
String line = console.readLine();
line = line.trim();
return line;
}
}

public static void doAssignment2() throws IOException {
Assignment4 assignment4 = new Assignment4();
FileInputStream is = new FileInputStream(new File(
InputReader.INPUT_FILE));
System.out.println(assignment4.readFile(is));
}

public static void doAssignment3() throws IOException {
Assignment5 assignment5 = new Assignment5();
FileInputStream is = new FileInputStream(new File(
InputReader.INPUT_FILE));
FileOutputStream os = new FileOutputStream(new File(
InputReader.OUTPUT_FILE));
String output = assignment5.readFile(is);
System.out.println(output);
os.write(output.getBytes());
os.flush();
os.close();
}
}

It is a bit confusing that I named the method's doAssignment2 and doAssignment3 but use Assignment4 and Assignment5 objects. I should have named these assignment classes better. Again it was a small programming assignment, and an easy refactor. Overall, I do think I did a decent job with this assignment and benefitted from the effort.

I created a handy batch file to execute the code called tynt.bat which does this:
java -cp target\program_assignment-1.0-SNAPSHOT.jar com.tynt.app.App

Monday, September 13, 2010

Tynt programming assignment (Part 4)

Finally, here is where I have to implement the sorting algorithm and collection. I created a List collection I called IntegerList:
package com.tynt.app.util;

public class IntegerList {
public static final int BUFFER_SIZE = 100;
private Integer[] internalArray;
private int size;

public IntegerList() {
internalArray = new Integer[BUFFER_SIZE];
size = 0;
}

public IntegerList(int size) {
if (size < 0) {
throw new RuntimeException("Cannot create IntegerList with negative size");
}
internalArray = new Integer[size];
size = 0;
}

public int size() {
return size;
}

public void put(int index, Integer value) {
if (index > -1 && index < size) {
internalArray[index] = value;
} else {
throw new RuntimeException("Cannot put value "+value+" at index "+index+" IntegerList has size "+size);
}
}

public void concatenate(Integer value) {
if (value == null) {
throw new RuntimeException("Cannot concatenate null values");
}
if (size + 1 == internalArray.length) {
Integer[] temp = new Integer[internalArray.length + BUFFER_SIZE];
System.arraycopy(internalArray, 0, temp, 0,
internalArray.length);
internalArray = temp;
}
internalArray[size++] = value;
}

public void concatenate(IntegerList values) {
for (int i = 0 ; i < values.size(); i++) {
concatenate(values.get(i));
}
}

public Integer get(int index) {
if (index > -1 && index < size) {
return internalArray[index];
} else {
throw new RuntimeException("Cannot access value at "+index+" in IntegerList size "+size);
}
}

public Integer remove(int index) {
if (index > -1 && index < size) {
Integer result = get(index);
IntegerList integerList = new IntegerList();
for(int i = 0; i < size; i++) {
if (i != index) {
integerList.concatenate(get(i));
}
}
internalArray = integerList.internalArray;
size = size - 1;
return result;
} else {
throw new RuntimeException("Cannot access value at "+index+" in IntegerList size "+size);
}
}

public static IntegerList quicksort(IntegerList unsorted) {
IntegerList less = new IntegerList();
IntegerList greater = new IntegerList();
if (unsorted.size() <= 1) {
return unsorted;
}
Integer pivot = selectPivot(unsorted);
for(int i = 0; i < unsorted.size(); i++) {
Integer value = unsorted.get(i);
if (value <= pivot) {
less.concatenate(value);
} else {
greater.concatenate(value);
}
}
return concatenate(quicksort(less), pivot, quicksort(greater));
}

public static Integer selectPivot(IntegerList unsorted) {
if (unsorted == null || unsorted.size() < 2) {
throw new RuntimeException("Cannot select pivot on empty or single element list");
}
int pivotIndex = unsorted.size() / 2;
return unsorted.remove(pivotIndex);
}

public static IntegerList concatenate(IntegerList less, Integer pivot, IntegerList greater) {
if (less == null | pivot == null || greater == null) {
throw new RuntimeException("Cannot concatenate null values");
}
IntegerList finalList = new IntegerList(less.size()+1+greater.size());
finalList.concatenate(less);
finalList.concatenate(pivot);
finalList.concatenate(greater);
return finalList;
}
}

IntegerList implements a quicksort method which returns the IntegerList as a sorted IntegerList. My implementation of quicksort has several drawbacks:

  • I create additional collections (less,greater,etc...) for each invocation of quicksort and concatenate, so lots of memory could be used up with a long list to sort. It would be less memory to sort the list in-place without recursion/extra collections.
  • Speaking of recursion, too much recursion could cause a stack overflow as well.
With IntegerList implemented, I can use it to implement Assignment4:
package com.tynt.app;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import com.tynt.app.util.*;

public class Assignment4 implements InputReader {

public String readFile(InputStream is) {
BufferedReader d = new BufferedReader(new InputStreamReader(is));
String line;
IntegerList unsortedList = new IntegerList();
try {
line = d.readLine();
while (line != null) {
Integer value = null;
try {
value = new Integer(Integer.parseInt(line));
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
unsortedList.concatenate(value);
line = d.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
StringBuilder sb = new StringBuilder();
IntegerList sortedList = IntegerList.quicksort(unsortedList);
if (sortedList == null) {
sortedList = new IntegerList();
}
for (int i=0;i < sortedList.size();i++) {
sb.append(sortedList.get(i));
sb.append(NEW_LINE);
}
return sb.toString();
}

}

Still need to implement the Hash collection and driver.

Sunday, September 12, 2010

Tynt programming assignment (Part 3)

I glossed over creating the Assignment1-5 java files in the last post, this was done by first right clicking in eclipse and creating a package under this folder:
src\main\java\com\tynt\app
called
com.tynt.app
Then right clicking the empty package and creating new Class (Assignment1) adding the InputReader interface to the class and stubbing out the interface method:
public String readFile(InputStream is) {
return null;
}

Just so you know.

For Assignment1 I just wanted to get the code for reading and creating a string from the input stream working:

package com.tynt.app;

import java.io.*;

import com.tynt.app.util.InputReader;

public class Assignment1 implements InputReader {

/**
* @return contents of the InputStream as a String
*/
public String readFile(InputStream is) {
BufferedReader d = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder sb = new StringBuilder();
try {
line = d.readLine();
while (line != null) {
sb.append(line);
sb.append(NEW_LINE);
line = d.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
}
For Assignment2 I decided to use the built-in Java collections classes to test what the output of my custom collection should look like:
package com.tynt.app;

import java.io.*;
import java.util.*;

import com.tynt.app.util.InputReader;

public class Assignment2 implements InputReader {

/**
* @return is list of numbers sorted in ascending order
*/
public String readFile(InputStream is) {
BufferedReader d = new BufferedReader(new InputStreamReader(is));
String line;
SortedSet sortedList = Collections.synchronizedSortedSet(new TreeSet());
try {
line = d.readLine();
while (line != null) {
Integer value = null;
try {
value = new Integer(Integer.parseInt(line));
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
sortedList.add(value);
line = d.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
StringBuilder sb = new StringBuilder();
for(Integer i: sortedList) {
sb.append(i);
sb.append(NEW_LINE);
}
return sb.toString();
}

}
Again with Assignment3, I used built-in collections to check what the output should be for the histogram:
package com.tynt.app;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

import com.tynt.app.util.InputReader;

public class Assignment3 implements InputReader {

/**
* @return sorted list of numbers and their frequency
*/
public String readFile(InputStream is) {
BufferedReader d = new BufferedReader(new InputStreamReader(is));
String line;
SortedMap sortedMap = Collections.synchronizedSortedMap(new TreeMap());
try {
line = d.readLine();
while (line != null) {
Integer value = null;
try {
value = new Integer(Integer.parseInt(line));
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
Integer frequency = 1;
if (sortedMap.containsKey(value)) {
frequency = sortedMap.get(value);
frequency += 1;
}
sortedMap.put(value, frequency);
line = d.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
StringBuilder sb = new StringBuilder();
for(Integer key: sortedMap.keySet()) {
Integer frequency = sortedMap.get(key);
sb.append(key);
sb.append(" - ");
sb.append(frequency);
sb.append(NEW_LINE);
}
return sb.toString();
}

}
Assignment4 and Assignment5 actually adhered to the restriction of no built-in collections.

Saturday, September 11, 2010

Tynt programming assignment (Part 2)

Next I generated an eclipse project file for the maven project with this command:

mvn eclipse:eclipse

I opened the projet in eclipse. For the Junit tests I created some test input files here:
src\test\resources\TestData.txt
src\test\resources\BadInput.txt

I then created the unit test java file here:
src\test\java\com\tynt\app\AppTest.java

Here is the source for my unit test:
package com.tynt.app;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.io.*;
import java.util.*;

import com.tynt.app.util.*;

/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}

/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}

/**
* Test each assignment with input files
*/
public void testApp()
{
List readers = new ArrayList();
Assignment1 assignment1 = new Assignment1();
Assignment2 assignment2 = new Assignment2();
Assignment3 assignment3 = new Assignment3();
Assignment4 assignment4 = new Assignment4();
Assignment5 assignment5 = new Assignment5();
readers.add(assignment1);
readers.add(assignment2);
readers.add(assignment3);
readers.add(assignment4);
readers.add(assignment5);
readerTest(readers,"TestData.txt");
try {
readerTest(readers,"BadInput.txt");
fail("Bad input should throw RuntimeException");
} catch (RuntimeException e) {
assertTrue(true);
}
}

public void readerTest(List readers, String resource) {
for (InputReader inputReader : readers) {
InputStream is = getClass().getClassLoader().getResourceAsStream(resource);
String result = inputReader.readFile(is);
System.out.println(result);
}
}
}
I created an interface I called InputReader to help me test the different assignments against the same input files.

package com.tynt.app.util;

import java.io.*;

public interface InputReader {

public static final String INPUT_FILE = "THire_input.txt";
public static final String OUTPUT_FILE = "THire_histogram.txt";
public static final String NEW_LINE = System.getProperty("line.separator");

/**
* Reads the InputStream parameter and returns a string of the contents
*
* @param is
* InputStream to read from
* @return String of the processed contents of InputStream
*/
public String readFile(InputStream is);
}
I then stubbed out the Assignment1-5 java classes which implement the InputReader interface and ran the package goal for maven using a maven eclipse plugin to fix any typos and get a baseline for beginning implementation.

Friday, September 10, 2010

Tynt programming assignment (Part 1)

I was a approached by a recruiter for a position at a company in Draper called Tynt.

As part of the hiring process I was given this coding assignment.
Coding Assignment #1
Thank you for your interest in Tynt. As part of our recruitment process we ask candidates to complete a small programming assignment. The assignment is technically easy, but is intended to provide enough scope to allow you to demonstrate your knowledge of good programming practices. Assume that the code you are writing will reside in Tynt's main production codebase.
Development Notes
• There is no time limit to complete this exercise.
• If there are errors in the example, state your assumptions and continue.
• No user interface is required; the program will be run from the console and should
require no command line parameters to execute normally
• Assume that all files (input and output) will be found/created in the same directory as the program executes
• As part of the exercise, you may choose to write unit tests appropriate for the task.
• Use the development tools of your choice, but please write the software in Java.
• You may not use any external libraries with the exception of standard I/O and possibly a unit testing framework.
• You may not use any built-in data structures such as lists, vectors, queues etc. You
must build your solution using basic types such as integers, strings and arrays.
• You may not use any built-in sort or histogram functions.
• Please submit all available aspects of your work (source, buildfiles, executables, test
input/output etc)
• Have Fun!
====================================================================
1) Read an ascii text file named "THire_input.txt" that contains a list of numbers with integer
values, each number separated by a cr/lf
Sample input (THire_input.txt)
6
54
12
22
1
6
2) Sort the numbers in ascending order and write the results to an output file named
"THire_Ascending.txt". Format the file in the same manner as the input file (cr/lf delimiters).
Sample output (THire_Ascending.txt)
1
6
6
12
22
I completed the assignment and submitted my solution but never heard from them, then the recruiter told me they went with someone else. Since I never had the opportunity to hear back from Tynt's hiring team, I decided to blog about my solution and solicit feedback on how I could do a better job.

The assignment tied my hands by not allowing external libraries or built-in Java collections. This is done to make an otherwise simple programming assignment much more labor intensive and error prone in college CS classes. Java loses much of it's value as a programming language when you eliminate the vast libraries available. Ultimately, the code I ended up writing would not belong in an actual enterprise code base precisely because I was required to implement functionality that is provided by the language libraries. Typically you don't roll-your-own implementation of a collection unless you have a very specific need to do it, which this assignment lacks. There was no time limit to complete the exercise (a bit discouraging in that you would think Tynt would have an idea of when they needed to hire the candidate), but that didn't matter because I received five-ten nagging emails from the headhunter asking for status on my progress with it.

On to the implementation, I decided that since this was to be considered code I would put in production, Junit tests were mandatory, as well as a build script. Maven is a fantastic build tool for Java development so I created a new project using Maven as follows:

mvn archetype:create -DgroupId=com.tynt.app -DartifactId=program_assignment


This generated the structure of the project. I edited the maven pom.xml file to add a Junit dependency as well as configure the maven-pmd-plugin, which will evaluate your source and report on potential bugs or bad source formatting. Here is my final pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tynt.app</groupId>
<artifactId>program_assignment</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>program_assignment</name>
<description>FourGablesGuy's implementation of programming assignment for Tynt. Reading and parsing text files for numbers.</description>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<compilerVersion>1.5</compilerVersion>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>pmd_check</id>
<phase>package</phase>
<goals>
<goal>pmd</goal>
</goals>
</execution>
</executions>
<configuration>
<sourceEncoding>UTF-8</sourceEncoding>
<outputEncoding>UTF-8</outputEncoding>
<targetJdk>1.5</targetJdk>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>


To build/update the artifact Jar, you simply run:


mvn package


which will compile source files, run any unit tests, and package the final code.

Thursday, September 9, 2010

startSQLRepository can fail to mark ATG Content Administration projects as deployed

Maybe this will help someone dealing with issues in ATG Service 2006.3.2 or ATG Service 2007.1

Symptom:

The symptom is that a solution is reporting it is not visible in ATG SelfService.You search for the solution but cannot view it and get a message that it is not visible for the current user.

Also, if you look up the solution in dynamo administrator for the j2ee instance running SelfService, it will be missing the status property.

This is an indication that the solution is not marked as deployed in the SolutionSolutionStatus Item Descriptor of the ServiceSharedRepository.

The solution can have audience Everyone-External set, in both Knowledge and SelfService which can make the error a head scratcher.

One way to check this is by connecting to dynamo admin for SelfService and in the /atg/svc/shared/ServiceSharedRepository execute

solutionId = "soln_id" AND deployed = true

If you do not get any rows back, then you probably have this problem, unless you just have not invalidated the caches.

Resolution:

connect to the shared schema with a database tool

update svc_soln_sstatus

set deployed = 0

where soln_id = 'soln_id' and

soln_version = version

where soln_id is the solution having the issue.

and version is the number of the version of the solution that should have been marked as deployed. To find it checked the last_modify_date and status_id columns of the rows returned from the query:

select * from svc_soln_sstatus where

soln_id = 'soln_id'

Make sure to commit the update. And clear the repository caches before rechecking.

Really the two ways you will get the message that a solution is not visible are:

1 the audience property is empty or only contains audiences that the current SelfService user is not a member of.

2 the solution is not marked as deployed in the ServiceSharedRepository SolutionSolutionStatus deployed = 1

Cause:

I suspect bulk importing solutions in with repository XML using startSQLRepository can leave solutions in this state where they are deployed but their status property was not set as deployed. Also messing around with ATG Service data using straight SQL on the database is a dangerous game that can burn you in countless ways.

Disclaimer

I do not accept liability for the consequences of any actions taken on the basis of the information provided by this blog.

About Me

My photo
Lead Java Developer Husband and Father

Tags