A Sunny Commune - Cheng's Blog

« Previous page | Main | Next page »

http://blogs.sun.com/chengfang/date/20071225 Tuesday December 25, 2007

Merry Christmas but sorry, no webkinz

Important statement about the content of this post:

I've been informed that Ganz is in fact taking every step possible to ensure they provide their community with an infrastructure that will support the heaviest loads. Sun is in fact working with Ganz to provision this infrastructure, but due to a number of complex reasons the teams were not able to complete the migration prior to the Christmas rush. 

This post is strictly my personal views and does not represent that of my employer or coworkers.  I had underestimated the complexity of managing such a large site.  Some conclusions in this post are premature based on incorrect facts.  My sincere apologies for any hard feelings and hindrance it has caused to webkinz/panther team during this holiday season.  Thank you very much for bringing us this fascinating gift and see you soon at webkinz world. 

Sincerely,

Cheng Fang
 

So today is the Christmas day, a moment kids have been waiting all year long.  After getting a Chihuahua webkinz from Santa this morning, my daughter wasted no time trying to log onto webkinz  to play.  But like millions of kids around the world, she was greeted with this gloomy message on webkinz homepage:

webkinz down

Out of curiosity, I wanted to know what kind of server webkinz is on.  This is what netcraft has to say about this site: 

Netblock OwnerIP addressOSWeb ServerLast changed
Panther Express195.12.231.201LinuxPWS/1.1.2920-Dec-2007
Pather Express213.244.185.11LinuxPWS/1.1.3418-Dec-2007
Panther Express195.12.231.220unknownPWS/1.1.2916-Dec-2007
Panther Express195.12.231.210LinuxPWS/1.1.2924-Nov-2007
Panther Express195.12.231.220unknownPWS/1.1.2922-Nov-2007
Panther Express195.12.231.209LinuxPWS/1.1.2922-Nov-2007
Panther Express195.12.231.205LinuxPWS/1.1.25 2-Nov-2007
Panther Express195.12.231.217unknownPWS/1.1.2231-Oct-2007
Panther Express195.12.231.199LinuxPWS/1.1.2229-Oct-2007
Panther Express195.12.231.205unknownPWS/1.1.2019-Oct-2007

 Apparently, webkinz team has been working very hard to accommodate the potential load, upgrading web server a couple of  times within the last 2 months.  Their hosts are running various version of PWS.

 What kind of web server is PWS?  I've never heard of it.  After some search, I found out this definition on webopedia:

(2) When capitalized as Personal Web Server, the name of Microsoft’s Web server program for individuals hosting Web page files from a personal computer. Personal Web Server is a smaller-scale version of Microsoft’s IIS technology and is therefore limited in its capabilities. It is designed to support Web sites that obtain limited traffic and/or to be used as a staging server for building pages that will be transferred to a server that can handle large amounts of traffic.

 Bold font are by me for emphasis.  What a surprise and what a disappointment!  A crippled version of IIS.  (Note: This is in fact incorrect.  Please see Jason's comments below about webkinz's network infrastructure.)  That's what is powering the site that attracts millions of kids and parents around the globe.  For millions of webkinzs Santa gave out last night, they can't really be played online.  Then they are just a plain plush toy that is not even worth half the price.  Sorry, Santa, I know it's not your fault, but can I exchange it for a walking doodle?

Seriously, the infrastructure of webkinz site is underdeveloped no matter how hard you upgrade or patch it.  Here is a message to webkinz team, on behalf of my daughter, get some real hardware, now.  Sun servers are quite affordable these days.  For less than the price of 80 webkinzs (80*14.99=$1199.2), you can get a Sun Fire X2100 M2 server, two-way x64, high performance, and energy efficient, and free 60-day trial.

In addition, get the real OS, free.  Get the real application/web server, be it Apache, Sun web server, or Glassfish.

Some of these may already on your wish list, and hopefully Santa already delivered some last night.  But if Santa leaves a PWS/IIS under the Christmas tree, keep it for personal use.

http://blogs.sun.com/chengfang/date/20070329 Thursday March 29, 2007

A generic BeanFactory class for custom JNDI resource

In my previous 3 posts(post 1, post 2, and post 3, I wrote about how to create, inject, look up, and configure a custom JNDI resource in glassfish application server. Usually a custom JNDI resource type conform to JavaBeans conventions, with property fields, getters and setters. Therefore, we should be able to write a generic bean factory class that works with any such custom JNDI resource types.

Just to recap, to create a custom JNDI resource, we need at least 2 classes: (1) the resource class to represent your resource, and (2) the factory class that knows how to create instances of your resource. The factory class must implement interface javax.naming.spi.ObjectFactory, which has a single method:


public Object getObjectInstance(Object obj,
                         Name name,
                         Context nameCtx,
                         Hashtable<?,?> environment)
                         throws Exception

Actually, there is already such a bean factory class org/apache/naming/factory/BeanFactory.class in glassfish lib/appserv-rt.jar. Its source code can be viewed here. But I found I have to modify 1 lines to make it work, otherwise the lookup or injection result is always null.


diff -w -u -b -r1.3 BeanFactory.java
--- BeanFactory.java    6 Nov 2006 21:14:08 -0000       1.3
+++ BeanFactory.java    9 Mar 2007 18:09:56 -0000
@@ -121,7 +121,7 @@
                                     Hashtable environment)
         throws NamingException {

-        if (obj instanceof ResourceRef) {
+        if (obj instanceof Reference) {
For your convenience, the modified generic object factory class is copied below:
package customjndi;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import javax.naming.Reference;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.spi.ObjectFactory;

public class GenericObjectFactory implements ObjectFactory {
    private static final Logger logger = Logger.getLogger(GenericObjectFactory.class.getName());

    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
	    Hashtable environment)
	    throws NamingException {
	logger.fine("obj=" + obj);
	logger.fine("name=" + name);
	logger.fine("nameCtx=" + nameCtx);
	if (obj instanceof Reference) {
	    try {
		Reference ref = (Reference) obj;
		String beanClassName = ref.getClassName();
		Class beanClass = null;
		ClassLoader tcl =
			Thread.currentThread().getContextClassLoader();
		if (tcl != null) {
		    try {
			beanClass = tcl.loadClass(beanClassName);
		    } catch (ClassNotFoundException e) {
		    }
		} else {
		    try {
			beanClass = Class.forName(beanClassName);
		    } catch (ClassNotFoundException e) {
			e.printStackTrace();
		    }
		}
		if (beanClass == null) {
		    throw new NamingException("Class not found: " + beanClassName);
		}

		BeanInfo bi = Introspector.getBeanInfo(beanClass);
		PropertyDescriptor[] pda = bi.getPropertyDescriptors();

		Object bean = beanClass.newInstance();

		Enumeration e = ref.getAll();
		while (e.hasMoreElements()) {

		    RefAddr ra = (RefAddr) e.nextElement();
		    String propName = ra.getType();

		    String value = (String) ra.getContent();

		    Object[] valueArray = new Object[1];

		    int i = 0;
		    for (i = 0; i < pda.length; i++) {

			if (pda[i].getName().equals(propName)) {

			    Class propType = pda[i].getPropertyType();

			    if (propType.equals(String.class)) {
				valueArray[0] = value;
			    } else if (propType.equals(Character.class) || propType.equals(char.class)) {
				valueArray[0] = Character.valueOf(value.charAt(0));
			    } else if (propType.equals(Byte.class) || propType.equals(byte.class)) {
				valueArray[0] = Byte.valueOf(value);
			    } else if (propType.equals(Short.class) || propType.equals(short.class)) {
				valueArray[0] = Short.valueOf(value);
			    } else if (propType.equals(Integer.class) || propType.equals(int.class)) {
				valueArray[0] = Integer.valueOf(value);
			    } else if (propType.equals(Long.class) || propType.equals(long.class)) {
				valueArray[0] = Long.valueOf(value);
			    } else if (propType.equals(Float.class) || propType.equals(float.class)) {
				valueArray[0] = Float.valueOf(value);
			    } else if (propType.equals(Double.class) || propType.equals(double.class)) {
				valueArray[0] = Double.valueOf(value);
			    } else {
				throw new NamingException("String conversion for property type '" + propType.getName() + "' not available");
			    }

			    Method setProp = pda[i].getWriteMethod();
			    if (setProp != null) {
				setProp.invoke(bean, valueArray);
			    } else {
				throw new NamingException("Write not allowed for property: " + propName);
			    }

			    break;

			}

		    }

		    if (i == pda.length) {
			throw new NamingException("No set method found for property: " + propName);
		    }

		}

		return bean;

	    } catch (java.beans.IntrospectionException ie) {
		throw new NamingException(ie.getMessage());
	    } catch (java.lang.IllegalAccessException iae) {
		throw new NamingException(iae.getMessage());
	    } catch (java.lang.InstantiationException ie2) {
		throw new NamingException(ie2.getMessage());
	    } catch (java.lang.reflect.InvocationTargetException ite) {
		throw new NamingException(ite.getMessage());
	    }

	} else {
	    return null;
	}

    }
}

For more details, see glassfish dev email alias discuss on Factory class for custom JNDI resource

Tags: , , , ,

http://blogs.sun.com/chengfang/date/20070309 Friday March 09, 2007

How to parameterize and configure custom resources

In my previous two posts (post1 and post2), I demonstrated how to create and inject custom resources in Glassfish application server. However, the custom resource used there has a hard-coded name attribute and thus cannot be parameterized. In this post, I will modify a few steps and files to make it configurable, so that the same resource type and factory can be applied to multiple resources.

1. Specify additional properties when creating custom resource:


asadmin create-custom-resource --restype foo.Widget 
--factoryclass foo.WidgetFactory
--property name=widget-two
--description "this is widget-two"
custom/widget-two

 

It's also possible to specify multiple key-value pairs in the form of key1=val1:key2=val2:key3=val3.

2. Modify foo.WidgetFactory.getObjectInstance method:


    public Object getObjectInstance(Object obj, 
                                    Name name, 
                                    Context nameCtx, 
                                    Hashtable<?, ?> environment) 
        throws Exception {
        Widget widget = new Widget();
        if(obj instanceof Reference) {
            Reference reference = (Reference) obj;
            Enumeration<RefAddr> attributes = reference.getAll();
            widget.init(attributes);
        }
        return widget;
    }

 

I also added a init method to foo.Widget to initialize fields:


    public void init(Enumeration<RefAddr> attributes) {
        while(attributes.hasMoreElements()) {
            RefAddr refAddr = (RefAddr) attributes.nextElement();
            if("name".equals(refAddr.getType())) {
                setName((String) refAddr.getContent());
            }
        }
    }

 

3. Modify the servlet class to inject multiple custom resources with different values but same type and factory class:


public class FooServlet extends HttpServlet {
    @Resource(name="widget-one", mappedName="custom/widget-one")
    private Widget widget1;

    @Resource(name="widget-two", mappedName="custom/widget-two")
    private Widget widget2;

    @Resource(name="widget-three", mappedName="custom/widget-three")
    private Widget widget3;
    
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Servlet FooServlet</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Servlet FooServlet at " + request.getContextPath() + "</h1>");
        out.println("<p>Injected custom resource: " + widget1 + "<br>");
        out.println("Custom resource from JNDI lookup: " + lookupWidget("widget-one") + "</p>");
        out.println("<p>Injected custom resource: " + widget2 + "<br>");
        out.println("Custom resource from JNDI lookup: " + lookupWidget("widget-two") + "</p>");
        out.println("<p>Injected custom resource: " + widget3 + "<br>");
        out.println("Custom resource from JNDI lookup: " + lookupWidget("widget-three") + "</p>");
        out.println("</body>");
        out.println("</html>");
    }

 

To look up custom resources without using injection, just configure them in web.xml (or ejb-jar.xml, application-client.xml). Note that the you need to use resource-env-ref, not resource-ref elements:


    <resource-env-ref>
        <resource-env-ref-name>widget-one</resource-env-ref-name>
        <resource-env-ref-type>foo.Widget</resource-env-ref-type>
        <mapped-name>custom/widget-one</mapped-name>
    </resource-env-ref>
    <resource-env-ref>
        <resource-env-ref-name>widget-two</resource-env-ref-name>
        <resource-env-ref-type>foo.Widget</resource-env-ref-type>
        <mapped-name>custom/widget-two</mapped-name>
    </resource-env-ref>
    <resource-env-ref>
        <resource-env-ref-name>widget-three</resource-env-ref-name>
        <resource-env-ref-type>foo.Widget</resource-env-ref-type>
        <mapped-name>custom/widget-three</mapped-name>
    </resource-env-ref>

 

4. Build, deploy the war and view the servlet output at http://localhost:8080/WebApplication1/FooServlet

Tags: , , , ,