Chris Lacy's Software Engineering Blog

Tuesday Jul 27, 2010

802.11s - I am disappoint

I continue to have high hopes for 802.11s and more advanced networking in general. ISPs are no longer needed. All we need are ways to securely and fairly share our connectivity. Imagine you're using your netbook and want to access google, but there's no hotspot nearby. Luckily, there's a guy on his smart phone 100 feet away, and from there he has a signal to a company's wifi (on which he's making the call). That company has a large antenna that reaches google.

I see lots of potential here. You can exchange bandwidth for equal bandwidth, and receive cash if you run a surplus. Municipalities would only need one connection to internet backbone instead of running wires to each residence. Anyone can run a website/server. It's the future.

Saturday Jun 26, 2010

object translators

in the year 2000, the buzz will be object translators, aka, property editors, aka closures, aka un/marshallers, aka de/serializers, aka reflected methods, etc. and everyone will conform to a standard api

we'll have libraries of translators, too; and we'll define the rules by which a given translator is chosen for conversion between types. and we'll have translators that don't have return values, and translators that take multiple values, etc

and what we'll do, is, we'll pipe data between these translators and translator libraries. REST will be piping data from a web request to a translator that converts http messages to sql (with maybe JSON or XML or AMF in between, plus validation/exception handling and security)

but we'll add these "plus" features (exception handling, security, caching, etc) with aop and additional translators/libraries

music test

package net.chrislacy.music.test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class MusicTest {
	
	
	private static final double[] NOTES = { 261.626, 293.665, 329.628, 349.228, 391.995, 440.000, 493.883, 523.251 };
	private static final Set<Integer> INCLUDE = new HashSet<Integer>();
	private static final double VOLUME = 0.03;
	private static final long LENGTH_MS = 1500;
	
	static {
		
		INCLUDE.add(1);
		INCLUDE.add(3);
		//INCLUDE.add(4);
		INCLUDE.add(5);
		//INCLUDE.add(8);
	}

	public static void main(String[] args) throws Throwable {
		
		// TODO add fails to "more often test" list
		// print solution after x (3) wrong guesses
		
		while(true) {
			
			List<Integer> notes = playRandomPhrase(3, VOLUME);
			InputStreamReader converter = new InputStreamReader(System.in);
			BufferedReader in = new BufferedReader(converter);
			
			boolean shouldRepeat = true;
			
			while(shouldRepeat) {
				
				String line = in.readLine();
				int index = 0;
				boolean passed = true;
				
				inner:
				for(Integer n : notes) {
					
					if(line.length() != notes.size() || Integer.parseInt(line.charAt(index++) + "") != n) {
						
						System.out.println("nope");
						playPhrase(VOLUME, createSimilarNotes(LENGTH_MS, 100, hertzFromOneBased(notes)));
						passed = false;
						break inner;
					}
				}
				
				shouldRepeat = !passed;
			}
		}
	}
	
	private static double[] hertzFromOneBased(List<Integer> oneBased) {
		
		double[] hertzs = new double[oneBased.size()];
		int index = 0;
		
		for(Integer oneB : oneBased) {
			
			hertzs[index++] = NOTES[oneB - 1];
		}
		
		return hertzs;
	}
	
	private static List<Integer> playRandomPhrase(int length, double volume) {

		List<Integer> result = new LinkedList<Integer>();
		double[] hertzs = new double[length];
		
		result.add(1);
		hertzs[0] = NOTES[0];
		
		for(int ii=1; ii<length; ii++) {
			
			int cur = randomIntLessThan(NOTES.length);
			int oneBasedCur = cur + 1;
			
			while(!INCLUDE.contains(oneBasedCur)) {
				
				cur = randomIntLessThan(NOTES.length);
				oneBasedCur = cur + 1;
			}
			
			result.add(oneBasedCur);
			hertzs[ii] = NOTES[cur];
		}
		
		playPhrase(volume, createSimilarNotes(LENGTH_MS, 100, hertzs));
		
		return result;
	}
	
	private static int randomIntLessThan(int max) {
		
		double r = Math.random();
		
		return (int) Math.round(r * max);
	}
	
	private static ComplexNote[] createSimilarNotes(long length, int relativeVolume, double... hertz) {
		
		ComplexNote[] result = new ComplexNote[hertz.length];
		
		for(int ii=0; ii<hertz.length; ii++) {
			
			result[ii] = new ComplexNote(hertz[ii], length, relativeVolume);
		}
		
		return result;
	}
	
	private static byte[] createOneWave(int numberOfBytes, double maxHeight) {
		
		byte[] oneWave = new byte[numberOfBytes];
		
		for(int i=0; i<numberOfBytes; i++) {
			
			double fractionalPos = ((double)i*2) / numberOfBytes;
			double angle = fractionalPos * Math.PI;
			double sineVal = Math.sin(angle);
			Double volSine = sineVal * maxHeight;
			byte b = volSine.byteValue();
			oneWave[i] = b;
		}
		
		return oneWave;
	}
	
	private static byte[] makeWaveTwoChannel(byte[] oneWave) {
		
		byte[] stereoWave = new byte[oneWave.length * 2];
		
		for(int i=0; i<oneWave.length; i++) {
			
			int stereoPos = i*2;
			stereoWave[stereoPos] = stereoWave[stereoPos+1] = oneWave[i];
		}
		
		return stereoWave;
	}
	
	private static byte[] repeatWave(byte[] original, int numberOfWaves) {
		
		byte[] multipleStereoWaves = new byte[original.length * numberOfWaves];
		
		for(int i=0; i < numberOfWaves; i++) {
			
			System.arraycopy(original, 0, multipleStereoWaves, original.length * i, original.length);
		}
		
		return multipleStereoWaves;
	}
	
	private static byte[] createMultipleStereoWaves(int bytesPerWave, double volume, int numberOfWaves) {
		
		byte[] oneWave = createOneWave(bytesPerWave, volume);
		byte[] stereoWave = makeWaveTwoChannel(oneWave);
		
		return repeatWave(stereoWave, numberOfWaves);
	}
	
	private static Clip createClip(AudioInputStream ais) {
		
		Clip clip;
		
		try {
			
			clip = AudioSystem.getClip();
			clip.open(ais);
			clip.setFramePosition(0);
		}
		catch (Exception e) {

			throw new RuntimeException(e);
		}
		
		return clip;
	}
	
	/**
	 * @param volume 1.000104 min vol headphones @ 220 hz
	 */
	private static AudioInputStream createAudioInputStream(double volume, ComplexNote... notes) {
		
		float bytesPerSecond = 48000f;
		
		AudioFormat af = new AudioFormat(bytesPerSecond, 
				8, // sample size in bits
				2, // channels
				true, // signed
				false // bigendian
		);
		
		List<byte[]> waveList = new LinkedList<byte[]>();
		int finalLength = 0;
		
		for(ComplexNote note : notes) {
		
			Double bpw = bytesPerSecond/note.getHertz();
			int bytesPerWave = bpw.intValue();
			int numberOfWaves = 800/bpw.intValue() + 5; // 763
			byte[] multipleStereoWaves = createMultipleStereoWaves(bytesPerWave, note.getRelativeVolume() * volume, numberOfWaves);
			
			int numBytesNeeded = ((Float) (bytesPerSecond * (note.getLength()/1000))).intValue();
			byte[] noteBuffer = new byte[numBytesNeeded];
			int repeatNum = numBytesNeeded / multipleStereoWaves.length;
			int ii=0;
			
			for(; ii<repeatNum; ii++) {
				
				System.arraycopy(multipleStereoWaves, 0, noteBuffer, multipleStereoWaves.length * ii, multipleStereoWaves.length);
			}
			
			System.arraycopy(multipleStereoWaves, 0, noteBuffer, multipleStereoWaves.length * ii, numBytesNeeded % multipleStereoWaves.length);
			
			waveList.add(noteBuffer);
			finalLength += noteBuffer.length;
		}
		
		byte[] finalBa = new byte[finalLength];
		int index = 0;
		
		for(byte[] cur : waveList) {
			
			System.arraycopy(cur, 0, finalBa, index, cur.length);
			index = index + cur.length;;
		}
			
		return new AudioInputStream(new ByteArrayInputStream(finalBa), af, finalBa.length/2);
	}
	
	private static void playPhrase(double volume, ComplexNote... notes) {
		
		Clip clip = createClip(createAudioInputStream(volume, notes));
		clip.loop(0);
		clip.drain();
		clip.flush();
		clip.close();
	}

}

Wednesday Jun 23, 2010

SingleResourceResolver

I don't know what others are doing for this, but try this for your rest implementations:

package net.chrislacy.webapp.util;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

public class SingleViewResolver implements ViewResolver {

	
	private View view;
	
	
	public SingleViewResolver(View view) {
		
		this.view = view;
	}
	
	@Override
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		
		return view;
	}

}

for example:
	<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
		<property name="mediaTypes">
			<map>
				<entry key="json" value="application/json" />
				<entry key="xml" value="application/xml" />
				<entry key="html" value="text/html" />
			</map>
		</property>
		<property name="viewResolvers">
			<list>
				<bean class="net.chrislacy.webapp.util.SingleViewResolver">
					<constructor-arg name="view">
						<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
					</constructor-arg>
				</bean>
				<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
					p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
			</list>
		</property>
	</bean>

Tuesday Jun 22, 2010

Integration Testing Spring Web Applications through DispatcherServlet

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
import static org.springframework.web.util.WebUtils.*;

import java.util.Enumeration;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.chrislacy.utils.c.EasyEnumeration;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class DispatcherTestHelper {

	
	private DispatcherTestHelper() {}
	
	
	@SuppressWarnings("unchecked")
	public static HttpServletResponse test(String configLocation, String method, String path) throws Throwable {
		
		DispatcherServlet ds = new DispatcherServlet();
		ds.setContextConfigLocation(configLocation);
		
		ServletConfig sc = mock(ServletConfig.class);
		Enumeration e = mock(Enumeration.class);
			
		when(sc.getInitParameterNames()).thenReturn(e);
		ServletContext scon = mock(ServletContext.class);
		when(scon.getInitParameterNames()).thenReturn(e);
		when(scon.getAttributeNames()).thenReturn(e);
		when(sc.getServletContext()).thenReturn(scon);
		
		ds.init(sc);
		
		WebApplicationContext wac = ds.getWebApplicationContext();
		when(scon.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)).thenReturn(wac);
		
		HttpServletRequest hsr = mock(HttpServletRequest.class);
		when(hsr.getMethod()).thenReturn(method);
		
		when(hsr.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE)).thenReturn(path);
		when(hsr.getAttribute(INCLUDE_CONTEXT_PATH_ATTRIBUTE)).thenReturn(path);
		when(hsr.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE)).thenReturn(path);
		when(hsr.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)).thenReturn(wac);
		when(hsr.getAttributeNames()).thenReturn(new EasyEnumeration(INCLUDE_REQUEST_URI_ATTRIBUTE, 
INCLUDE_CONTEXT_PATH_ATTRIBUTE, INCLUDE_SERVLET_PATH_ATTRIBUTE, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
		when(hsr.getRequestURI()).thenReturn(path);
		RequestDispatcher rd = mock(RequestDispatcher.class);
		when(hsr.getRequestDispatcher(anyString())).thenReturn(rd);
		HttpServletResponse hsres = mock(HttpServletResponse.class);
		ds.service(hsr, hsres);
		
		verify(rd).include(hsr, hsres);
		
		return hsres;
	}
}


	@Test
	public void test() throws Throwable {
		HttpServletResponse hsres = DispatcherTestHelper.test("file:src/main/webapp/WEB-INF/spring/*-config.xml", "GET", "/welcome");
		System.out.println(hsres);
	}

Thursday Jun 17, 2010

finite state machines, domain specific languages, and abstract resources

prediction: we're going to see a convergence around a domain specific language for constructing finite state machines that act on abstract resources. for example, web flow "languages", enterprise service buses, and enterprise integration technologies will eventually share a common tongue. we may see this vertically as well: how many (virtual) machine languages do we need? and since machine languages are themselves programming languages, can we consolidate? do we really need multiple data access technologies (rest, sql, no-sql, hql, jpql)? how many times are we going to re-invent distributed middleware (corba, rmi, rmi-iiop, soap)?

back to finite state machines, though. just as there are enterprise integration patterns, there will be semi-solid "strategies" which take pluggable abstract resources. those resources may be determined by the environment ala osgi di.

Thursday Nov 26, 2009

Grails AJAJ Autocomplete

Currently only working with grails 1.2-M3 (grails-ui plugin fails in M4)

package net.chrislacy.grails

import grails.converters.JSON
import java.util.concurrent.ConcurrentSkipListSet

class AjajController {


    static names = ['Blaire','Carol','Chris','Christy','Conan','Heather','Olivia'] as ConcurrentSkipListSet


    def index = {[names:names]}


    def autoCompleteMe = {

        def query = params.query
        def next = query + Character.MAX_VALUE
        def foundNames = names.subSet(query, true, next, true).collect{[name:it]}

        render([result:foundNames] as JSON)
    }


    def add = {render names << params.name}

}
<%@ page contentType="text/html;charset=UTF-8" %>
<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>AJAJ Example</title>
    <gui:resources components="['autoComplete']"/>
    <g:javascript library="prototype" />
  </head>

  <body>
    <g:formRemote name="add" url="[controller:'ajaj', action:'add']" update="update">
      <gui:autoComplete name="name" controller="ajaj" action="autoCompleteMe" minQueryLength="1" />
      <g:submitButton style="display:none" name="add"/>
    </g:formRemote>
    <div id="update">${names}</div>
  </body>
</html>

Monday Nov 23, 2009

Grails Makes AJAX Too Easy

package net.chrislacy.grails

class AjaxController {


    static Set names = new TreeSet()

    
    static {

        names << 'Al'
        names << 'Barry'
        names << 'Carol'
        names << 'Chris'
        names << 'Conan'
        names << 'Olivia'
        names << 'Sandy'
        names << 'Sam'
    }


    def index = {

        def model =[:]
        model.names = names

        model
    }


    def searchAJAX = {

        Set foundNames = names.collect(new TreeSet()) {

            it.startsWith(params.query) ? it : ''
        }

        foundNames.remove ''

        render(contentType: "text/xml") {
	    results() {
	        foundNames.each { n ->
		    result() {
		        name(n)
		    }
		}
            }
        }
    }


    def add = {

        names << params.name

        render names
    }

}
<%@ page contentType="text/html;charset=UTF-8" %>
<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Ajax Example</title>
    <resource:autoComplete skin="default"/>
    <g:javascript library="scriptaculous" />
  </head>

  <body>
    <g:formRemote name="add" url="[controller:'ajax', action:'add']" update="update">
      <richui:autoComplete name="name" action="${createLinkTo('dir': 'ajax/searchAJAX')}"/>
    </g:formRemote>
    <div id="update">${names}</div>
  </body>
</html>
Live: http://chrislacy.net/AjaxExample/

Sunday Apr 05, 2009

SOAP is to CORBA as REST is to _

Remember the SATs?

SOAP is to CORBA as REST is to _
A. Java
B. RPC
C. A Database language like SQL
D. Clean, sleepy snakes

Sunday Feb 01, 2009

Exception Handling

I'm a big fan of the Spring Framework. Their motto, when it comes to exception handling, is: throw unchecked exceptions unless you have a good reason not to (paraphrase). This allows you to place your error handling code at places that make sense, without the clutter of catching or declaring checked exceptions. Throwing unchecked exceptions keeps your code clean, and promotes the good practice of identifying key points within your code where exceptional events may be properly handled, often through a user message and a log action.

It would be nice to add some sort of warning to code that has a high likelihood of throwing an exception, without needing to explicitly handle the exception. For example, declared unchecked exceptions might produce compiler warnings if they are not caught or re-thrown. This would act as a friendly reminder to engineers that they need to account for exceptional conditions, and they might want to add extra error handling around this method if there's something they think they can do.

Another option my be a new class of exceptions that lie between checked and unchecked. Not handling them would produce warnings through the potential call stack.

Sunday Sep 07, 2008

Hibernate using JPA Annotations in One Eclipse Project

I finally put together a small Java web app that has all of my favorite tools: Spring, Hibernate, JPA annotations, and Maven (m2eclipse); all in one Eclipse project. If you've ever tried to get these to work together you know it can be a hassle. Recent improvements in m2eclipse have made things a lot easier. For this blog entry I want to share with you one of the last pieces of my puzzle.

My understanding is that the Spring/Hibernate combination works in two modes: Hibernate and JPA. In both modes you can scan for annotated entities, but in the JPA mode the scan depends on EJB3 package compilation artifacts. I could have tried creating a separate project for the EJBs, but imo that's too much work, especially when you get Maven involved. Instead, I dove into the Spring and Hibernate internals and hacked together my own solution. Then, of course, I read this blog post (warning, language), and followed up by exploring loom. They use a bean post processor, which is much cleaner than my hack. Here's the low down:

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    p:dataSource-ref="dataSource">
        
    <property name="persistenceUnitPostProcessors">
          <bean class="net.chrislacy.webapp.HibernateJpaPostProcessor" p:packages="net.chrislacy.webapp"/>
    </property>
        
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
            p:database="HSQL"
            p:showSql="false"
            p:generateDdl="true">
        </bean>
    </property>
        
    <property name="persistenceXmlLocation" value="/META-INF/persistence.xml" />
</bean>
public class HibernateJpaPostProcessor implements PersistenceUnitPostProcessor, InitializingBean {

	
	private ClassPathScanningCandidateComponentProvider provider;
	private String[] packages = new String[] {};

	
	/**
	 * Search for persistent classes under the configured folders and register
	 * them in the persistence unit.
	 */
	public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo unit) {
		
		for (String pakage : packages) {
			
			for (BeanDefinition bean : provider.findCandidateComponents(pakage)) {
				
				unit.addManagedClassName(bean.getBeanClassName());
			}
		}
	}

	public void afterPropertiesSet() {
		
		if (provider == null) {
			
			provider = new ClassPathScanningCandidateComponentProvider(false);
			provider.addIncludeFilter(new AnnotationTypeFilter(Entity.class, false));
		}
	}

	public void setProvider(ClassPathScanningCandidateComponentProvider provider) {
		
		this.provider = provider;
	}

	public void setPackages(String[] packages) {
		
		this.packages = packages;
	}

}

Calendar

Feeds

Search

Links

Navigation

Referrers