Sep 24

Wake-on-LAN is an Ethernet computer networking standard that allows a computer to be turned on or woken up by a network message. The message is usually sent by a simple program executed on another computer on the local area network.

BIOS Setup

You first need to enable WOL in your bios. This might be a Wake on LAN setting under power management, or it might be something like “Allow power from S5 by PME”. S5 is a power management state and PME is Power Management Event.

Computer Setup

You need to make sure you allow your network device to wake up your pc. In windows you can do this through the device manager for your network adapter. If you edit the properties for your network card (Control Panel > Device Manager > Network Adapters > Your Ethernet card > Properties > Power Management) – you can check a box to ‘Allow this device to wake up the computer’ and you can force it only to allow WOL magic packets to wake up the pc.

Routers

If your pc is behind a router, then you will need to forward all requests to port 9 to your pc that you want to wake up. Some routers allow you to assign applications to certain devices, and you may see ‘Wake On LAN’ as one of the applications.

Java code to wake up your pc

Below is some java code that you can use to wake up your pc using wake on lan. You will need to know your IP address and the MAC address of your network card.

package com.devedup.net;

import org.apache.log4j.Logger;

import java.io.*;
import java.net.*;

/**
 * Used for waking up computers
 *
 * @author dave
 * @since 9 Apr 2009 08:00:04
 */
public class WakeOnLAN {

    private final static Logger logger = Logger.getLogger(WakeOnLAN.class);

    public static final int PORT = 9;

    public static void main(String[] args) {

        if (args.length != 2) {
            logger.info("Usage: java WakeOnLan <broadcast-ip> <mac-address>");
            logger.info(
                "Example: java WakeOnLan 192.168.0.255 00:0D:61:08:22:4A");
            logger.info(
                "Example: java WakeOnLan 192.168.0.255 00-0D-61-08-22-4A");
            System.exit(1);
        }

        String ipStr = args[0];
        String macStr = args[1];

        try {
            wakeup(ipStr, macStr);
        } catch (Exception e) {
            logger.info("Failed to send Wake-on-LAN packet: + e");
            System.exit(1);
        }

    }

    /**
     * Send a Wake On LAN magic packet to ip and mac address specifed
     *
     * @param ip
     * @param macAddress
     */
    public static void wakeup(String ip, String macAddress) {
        if (ip == null || ip.length() < 1)
            throw new IllegalArgumentException(
                          "ip address cannot be null or blank");
        if (macAddress == null || macAddress.length() < 1)
            throw new IllegalArgumentException(
                          "macAddress cannot be null or blank");

        byte[] macBytes = getMacBytes(macAddress);
        byte[] bytes = new byte[6 + 16 * macBytes.length];
        for (int i = 0; i < 6; i++) {
            bytes[i] = (byte) 0xff;
        }
        for (int i = 6; i < bytes.length; i += macBytes.length) {
            System.arraycopy(macBytes, 0, bytes, i, macBytes.length);
        }

        DatagramSocket socket = null;
        try {
            InetAddress address = InetAddress.getByName(ip);
            DatagramPacket packet =
                new DatagramPacket(bytes, bytes.length, address, PORT);

            if (logger.isInfoEnabled()) {
                logger.info("Sending datagram (UDP) packet to ip["
                                   + address + "] on port[" + PORT + "]");
                logger.info("Message bytes [" + bytes + "]");
            }

            socket = new DatagramSocket();
            socket.send(packet);
            logger.info("Wake-on-LAN packet sent.");
            return;
        } catch (UnknownHostException e) {
            logger.error(e);
        } catch (SocketException e) {
            logger.error(e);
        } catch (IOException e) {
            logger.error(e);
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        }
    }

    private static byte[] getMacBytes(String macStr)
                                     throws IllegalArgumentException {
        byte[] bytes = new byte[6];
        String[] hex = macStr.split("(\\:|\\-)");
        if (hex.length != 6) {
            throw new IllegalArgumentException("Invalid MAC address.");
        }
        try {
            for (int i = 0; i < 6; i++) {
                bytes[i] = (byte) Integer.parseInt(hex[i], 16);
            }
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                                "Invalid hex digit in MAC address.");
        }
        return bytes;
    }

}

written by admin \\ tags: , ,

Jun 11

The ActionMapper is the component that maps a url onto one of your action classes. More information can be found about it here:

http://struts.apache.org/2.1.6/docs/actionmapper.html

In most applications you won’t need to change the default ActionMapper. A project I am currently working on required that I implement an API specified by a 3rd party. The requests that they send us are all POST requests over HTTPS. Nothing fancy. To determine which method/procedure that they want to call, they send a parameter on the request named ‘function’. I decided that I would implement the receiving service using Struts 2 and create a custom ActionMapper. I did a search, but the results didn’t show any examples of creating a custom ActionMapper, so I downloaded the  struts 2 source to see how the default one worked, and then I constructed mine with that as a ‘guide’.

My implementation is rather basic, as it isn’t such a complex requirement.:

import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import org.apache.log4j.Logger;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public class SyncActionMapper implements ActionMapper {

    private final Logger LOG = Logger.getLogger(getClass());

    private static final String UNKNOW_FUNCTION_MAPPING = "unknownFunction";

    @Override
    public ActionMapping getMapping(HttpServletRequest request,
                              ConfigurationManager configurationManager) {
        Map&lt;String, ActionConfig&gt; actions =
           configurationManager.getConfiguration().
              getPackageConfig("sync-facade").getActionConfigs();

        ActionMapping mapping = new ActionMapping();
        mapping.setNamespace("/");

        String actionName = request.getParameter("function");
        if (actionName == null || actionName.length() &lt; 1) {
            LOG.error("function param no found on request " + actionName);
            actionName = UNKNOW_FUNCTION_MAPPING;
        } else if (!actions.containsKey(actionName)) {
            LOG.error("action not found in struts.xml " + actionName);
            actionName = UNKNOW_FUNCTION_MAPPING;
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("function param found, "
                    + " mapping to action with the name: " + actionName);
            }
        }
        mapping.setName(actionName);
        return mapping;
    }

    @Override
    public ActionMapping getMappingFromActionName(String actionName) {
        ActionMapping mapping = new ActionMapping();
        mapping.setName(actionName);
        mapping.setNamespace("/");
        return mapping;
    }

    @Override
    public String getUriFromActionMapping(ActionMapping actionMapping) {
        throw new IllegalStateException("not implemented");
    }
}

I also wanted to map unknown functions to an error action. Checking the struts.xml file and returning a valid ActionMapping is not part of the ActionMapper contract (so the docs say), but I decided to add this here. After getting the function name, I check to see if this function exists in struts.xml using the ConfigurationManager (i may need to optimize this line of code, not sure yet).

To set my custom ActionMapper to be the default I added a reference to it in struts.xml:

<constant name="struts.mapper.class"
    value="com.devedup.sync.dispatcher.mapper.SyncActionMapper"/>

I can now map to actions simply from the url http://localhost/myapp?function=myAction  (or similarly with the function param in the body of the POST request).

written by admin \\ tags: , ,

Jun 07

Why doesn’t the standard Java SDK provide a method that returns a 32 character (128bit) String representation of  an MD5 encrypted string… just like most other languages do (PHP, Ruby)… i don’t know! Below are Ruby and PHP examples and then a Java implementation.

Ruby implementation:

require 'digest/md5'
digest = Digest::MD5.hexdigest("encrypt this text")

PHP implementation:

<?php
   $str = "Hello";
   echo md5($str);
?>

Java implementation (2 methods for clarity):

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException

public class MD5 {

public static String digest(String text) {
      throws NoSuchAlgorithmException, UnsupportedEncodingException {
   MessageDigest md = MessageDigest.getInstance("MD5");
   byte[] md5hash = new byte[32];
   md.update(text.getBytes("iso-8859-1"), 0, text.length());
   md5hash = md.digest();
   return convertToHex(md5hash);
}

private static String convertToHex(byte[] b) {
   StringBuilder result = new StringBuilder(32);
   for (int i = 0; i &lt; b.length; i++) {
      result.append(
         Integer.toString( ( b[i] &amp; 0xff ) + 0x100, 16).substring( 1 ));
   }
   return result.toString();
}
}

This isn’t the fastest MD5 implementation, but that isn’t my goal here. I found quite a few implementations when searching, some of which gave results which were not even correct!

Hope this implementation helps someone else.

written by admin \\ tags: , , ,