Wednesday, March 23, 2011

Migrating NetBeans jar references to Ivy dependencies

Search on http://mvnrepository.com for the library

Sometimes mvnrepository doesn't have the library version you want. In this case I go to the library's website and see if they have a maven repository URL listed there. If they do, you have to add it to your ivysettings.xml file. If your NetBeans project doesn't yet have/use a ivysettings.xml file, you'll have to add one:
  • Add ivysettings.xml file to your project's root
  • Set the ivysettings.xml file in Project Properties/Ivy

<?xml version="1.0" encoding="UTF-8"?>

<ivysettings>
<settings defaultResolver="myChain"/>
<caches checkUpToDate="true"/>
<resolvers>
<chain name="myChain">
<ibiblio name="lombok" m2compatible="true"
root="http://projectlombok.org/mavenrepo/"/>
<ibiblio name="jetty" m2compatible="true"
root="http://oss.sonatype.org/content/groups/jetty/"/>
<ibiblio name="ibiblio" m2compatible="true"/>
</chain>
</resolvers>
</ivysettings>

Tip: Start by replacing the libraries that might have dependencies themselves, e.g., hibernate, and remove all the jar refs they bring in. This will avoid redundant dependency specification for common libraries, e.g., commons-logging. Also, you might find that you were importing jars you didn't even need. (In truth, though, you might get some new ones you don't need. The lib you are depending on might not have an Ivy configuration or Maven scope specific enough to your usage, in which case you could get some unnecessary jars.)

I had another problem where all my tests would be run twice, the second run crashing with no indication as to why. Turns out commons-jxpath 1.2 was bringing in ant-optional-1.5.1.jar, which screws up NetBeans test runs. Changing to reference jxpath 1.3 and deleting build/jar solved the problem.

Autodownloading Ivy & Cleaning build/jar

Being a fan of automation, I figured out how to autodownload Ivy and it's dependencies. Also, I discovered that IvyBeans calculates the classpath based on all the jars it finds in build/jar, but doesn't clean it, which can lead to problems (e.g., http://groups.google.com/group/usr-ivybeans/browse_thread/thread/629c30771ec8192d). So, I also added code to clean build/jar on every build. Add this to your NetBeans build.xml:



<!-- Ivy -->
<property name="ivy.install.version" value="2.2.0" />
<property name="ivy.home" value="${user.home}/.ant/lib" />
<property name="ivy.jar.file" value="${ivy.home}/ivy-${ivy.install.version}.jar" />
<property name="ant-contrib.version" value="1.0b3" />
<property name="ant-contrib.lib" value="${ivy.home}/ant-contrib-${ant-contrib.version}.jar" />
<target name="-pre-init" depends="download-ivy,clean-build-libs"/>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
        uri="antlib:org.apache.ivy.ant" classpath="${ivy.jar.file}"/>
<target name="download-ivy">
<mkdir dir="${ivy.home}"/>
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
          dest="${ivy.jar.file}" usetimestamp="true" ignoreerrors="true" maxtime="15"/>
<get src="http://repo1.maven.org/maven2/ant-contrib/ant-contrib/${ant-contrib.version}/ant-contrib-${ant-contrib.version}.jar"
          dest="${ant-contrib.lib}" usetimestamp="true" ignoreerrors="true" maxtime="15"/>
</target>
<!-- Clean build libs so Ivy doesn't include stale jars in classpath -->
<target name="clean-build-libs">
<delete dir="${basedir}/${build.dir}/${lib.dir}"/>
</target>


Tuesday, March 22, 2011

Box/Glue/Struts in BoxLayout using NetBeans

Couldn't figure out how to add Box/Glue/Struts to a BoxLayout in NetBeans 6.9. From my research on the web, it can't be done w/ the designer.

A solution I found was to add an empty border to the control beside which I wanted to put a space, and set the insets accordingly. Meh.

Friday, March 04, 2011

Convert NetBeans 6.9 GWT web project to Ivy

My experience converting a NetBeans 6.9 GWT web project to Ivy.
  • Install IvyBeans via NetBeans plugin manager
  • Enabled Ivy in project properties
  • Had to edit the part of ivy-impl.xml that changes javac.classpath to put the ivy classpath first, as the GWT compile only seems to work with the GWT jars first in the classpath
  • Overwrite ant target that builds war as described here http://groups.google.com/group/usr-ivybeans/browse_thread/thread/f7716e6a189dbd83. WARNING: You have to be careful, though, as there are several war building ant tasks depending on whether you're using a custom manifest (whatever that means).
I'll describe how I replaced jar refs with Ivy dependencies in a subsequent post.

Labels:

IP change notifier



package ca.digitalrapids.net;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import ca.digitalrapids.util.concurrent.DRThreadFactory;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/** Monitors the local system's IP, and informs of any changes.
*/
public class IpChangeNotifier {

static public class IpChangeEvent {
private final IpChangeEventType type;
private final NetworkInterface networkInterface;
private final Set oldAddresses;
private final Set newAddresses;
public IpChangeEvent(IpChangeEventType type,
NetworkInterface networkInterface,
Set oldAddresses, Set newAddresses) {
super();
this.type = type;
this.networkInterface = networkInterface;
this.oldAddresses = oldAddresses == null ? null :
Collections.unmodifiableSet(oldAddresses);
this.newAddresses = newAddresses == null ? null :
Collections.unmodifiableSet(newAddresses);
}
public IpChangeEventType getType() {
return type;
}
public NetworkInterface getNetworkInterface() {
return networkInterface;
}
public Set getOldAddresses() {
return oldAddresses;
}
public Set getNewAddresses() {
return newAddresses;
}
@Override
public String toString() {
return "Event [type=" + type + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IpChangeEvent other = (IpChangeEvent) obj;
if (type != other.type)
return false;
return true;
}
}
static public enum IpChangeEventType {
INTERFACE_ADDED,
INTERFACE_REMOVED,
ADDRESSES_CHANGED
}
static private class InterfaceInfo {
public final NetworkInterface networkInterface;
public final Set lastAddresses;
public InterfaceInfo(NetworkInterface networkInterface,
Set lastAddresses) {
super();
this.networkInterface = networkInterface;
this.lastAddresses = lastAddresses;
}
}
public interface Listener {
void handleIpChange(IpChangeEvent event);
}
interface NetworkInterfacesFactory {
List get() throws SocketException;
}

static private final transient Logger logger = Logger
.getLogger(IpChangeNotifier.class.getName());

private final List listeners =
new CopyOnWriteArrayList();
private final ScheduledExecutorService executor =
Executors.newSingleThreadScheduledExecutor(
new DRThreadFactory("IpChangeNotifier", true));
private final long delayMillis;
private NetworkInterfacesFactory networkInterfacesFactory =
new NetworkInterfacesFactory()
{
@Override
public List get() throws SocketException {
return Collections.list(NetworkInterface.getNetworkInterfaces());
}
};
private Map interfaceInfosByName =
Maps.newHashMap();

public IpChangeNotifier(long delayMillis) {
super();
this.delayMillis = delayMillis;
}

public void setNetworkInterfacesFactory(
NetworkInterfacesFactory networkInterfacesFactory) {
this.networkInterfacesFactory = networkInterfacesFactory;
}

public void addListener(Listener listener) {
this.listeners.add(listener);
}

public void removeListener(Listener listener) {
this.listeners.remove(listener);
}

/**
* Remove all listeners
*/
public void clear() {
this.listeners.clear();
}

public void start() {
setBaseline();
executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
check();
} catch (Throwable e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}, 0, delayMillis, TimeUnit.MILLISECONDS);
}

public void stop() {
executor.shutdown();
}

public void check()
{
check(true);
}

private void check(boolean fire) {
try {
Collection removedInterfaces =
Sets.newHashSet(interfaceInfosByName.values());
for (NetworkInterface networkInterface : networkInterfacesFactory.get())
{
String interfaceName = networkInterface.getName();
Set currentAddresses =
getInetAddresses(networkInterface);
InterfaceInfo interfaceInfo =
interfaceInfosByName.get(interfaceName);
if ( interfaceInfo == null )
{
interfaceInfosByName.put(interfaceName,
new InterfaceInfo(networkInterface, currentAddresses));
if ( fire )
fireIpChanged(new IpChangeEvent(
IpChangeEventType.INTERFACE_ADDED,
networkInterface, null, currentAddresses));
} else {
removedInterfaces.remove(interfaceInfo);
if ( !interfaceInfo.lastAddresses.equals(currentAddresses) ) {
logger.info("interface "+interfaceName+" addresses " +
"changed from "+interfaceInfo.lastAddresses+" to "+
currentAddresses);
interfaceInfosByName.put(interfaceName,
new InterfaceInfo(networkInterface, currentAddresses));
if ( fire )
fireIpChanged(new IpChangeEvent(
IpChangeEventType.ADDRESSES_CHANGED,
networkInterface, interfaceInfo.lastAddresses,
currentAddresses));
}
}
}
handleInterfacesRemoved(removedInterfaces);
} catch (SocketException e) {
logger.log(Level.WARNING, "IP check failed. Could not get " +
"network interfaces", e);
}
}

private void handleInterfacesRemoved(
Collection removedInterfaces)
{
for (InterfaceInfo interfaceInfo : removedInterfaces) {
interfaceInfosByName.remove(interfaceInfo.networkInterface.getName());
fireIpChanged(new IpChangeEvent(IpChangeEventType.INTERFACE_REMOVED,
interfaceInfo.networkInterface, interfaceInfo.lastAddresses,
null));
}
}

private HashSet getInetAddresses(
NetworkInterface networkInterface) {
return Sets.newHashSet(Collections.list(
networkInterface.getInetAddresses()));
}

private void fireIpChanged(IpChangeEvent event) {
for (Listener listener : listeners) {
listener.handleIpChange(event);
}
}

public static void main(String[] args) {
IpChangeNotifier ipChangeNotifier = new IpChangeNotifier(1000);
ipChangeNotifier.addListener(new Listener() {
@Override
public void handleIpChange(IpChangeEvent event) {
System.out.println("ip changed "+event);
}
});
ipChangeNotifier.start();
System.out.println("kevin");
}

public void setBaseline() {
check(false);
}
}

package ca.digitalrapids.net;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import ca.digitalrapids.net.IpChangeNotifier.IpChangeEvent;
import ca.digitalrapids.net.IpChangeNotifier.IpChangeEventType;
import ca.digitalrapids.net.IpChangeNotifier.Listener;
import ca.digitalrapids.net.IpChangeNotifier.NetworkInterfacesFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

public class IpChangeNotifierTest {

private IpChangeNotifier subject;
@Mock
private NetworkInterfacesFactory interfacesFactory;
@Mock
private Listener listener;
@Mock
private InterfaceAddress interfaceAddress;

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
subject = new IpChangeNotifier(500);
subject.setNetworkInterfacesFactory(interfacesFactory);
subject.addListener(listener);
}

@Test
public void testGeneral() throws Exception {
List interfaces = Lists.newArrayList();
when(interfacesFactory.get()).thenReturn(interfaces);

/* NetworkInterface is final, so we can't mock it. Need to think of
* another way to test this class. */
NetworkInterface networkInterface0 =
NetworkInterface.getNetworkInterfaces().nextElement();

subject.check();
verifyZeroInteractions(listener);
interfaces.add(networkInterface0);
subject.check();

ArgumentCaptor captor =
ArgumentCaptor.forClass(IpChangeEvent.class);
verify(listener).handleIpChange(captor.capture());
IpChangeEvent resultEvent = captor.getValue();
assertEquals(IpChangeEventType.INTERFACE_ADDED, resultEvent.getType());
assertEquals(networkInterface0, resultEvent.getNetworkInterface());
assertNull(resultEvent.getOldAddresses());
assertEquals(Sets.newHashSet(Collections.list(
networkInterface0.getInetAddresses())),
resultEvent.getNewAddresses());

subject.check();
verifyZeroInteractions(listener);
}

@Test
public void testSetBaseline() throws Exception {
List interfaces = Lists.newArrayList();
when(interfacesFactory.get()).thenReturn(interfaces);

/* NetworkInterface is final, so we can't mock it. Need to think of
* another way to test this class. */
NetworkInterface networkInterface0 =
NetworkInterface.getNetworkInterfaces().nextElement();

interfaces.add(networkInterface0);
subject.setBaseline();
subject.check();
verifyZeroInteractions(listener);
}

@Test
public void testInterfaceRemoved() throws Exception {
List interfaces = Lists.newArrayList();
when(interfacesFactory.get()).thenReturn(interfaces);

/* NetworkInterface is final, so we can't mock it. Need to think of
* another way to test this class. */
NetworkInterface networkInterface0 =
NetworkInterface.getNetworkInterfaces().nextElement();

interfaces.add(networkInterface0);
subject.setBaseline();
interfaces.clear();
subject.check();

ArgumentCaptor captor =
ArgumentCaptor.forClass(IpChangeEvent.class);
verify(listener).handleIpChange(captor.capture());
IpChangeEvent resultEvent = captor.getValue();
assertEquals(IpChangeEventType.INTERFACE_REMOVED, resultEvent.getType());
assertEquals(networkInterface0, resultEvent.getNetworkInterface());
assertNull(resultEvent.getNewAddresses());
assertEquals(Sets.newHashSet(Collections.list(
networkInterface0.getInetAddresses())),
resultEvent.getOldAddresses());

subject.check();
verifyZeroInteractions(listener);
}

}