FusionCharts debug mode doesn't work @ 100% x 100%

by timvasil 2/26/2008 12:21:00 AM

FusionCharts' debugMode doesn't work when you specify dimensions of 100% by 100%.

Generally speaking, 100% x 100% isn't what you want anyway, as the chart will never shrink or grow even if its container does.  You're forced to recreate the chart when you want to change its dimensions as documented here and here.  So you're better off determining the actual width and height of your container and passing those values into the FusionCharts constructor.

Another gotcha:  the value you use for a setDataXML call is different depending on whether or not the chart has already been rendered!  If the chart isn't rendered yet, you have to URL escape the XML by calling a function like encodeURIComponent.  Otherwise, you can send in XML directly.  So, when re-constructing a chart to change its dimension, make sure you're encoding the XML if you go the XML route!

Tags:

FusionCharts

Rounding Dates in MySQL 5

by timvasil 2/20/2008 12:19:00 AM

A while back I blogged about how to round dates in SQL Server 2000+.  Here's the MySQL port:

DROP FUNCTION IF EXISTS `dbname`.`fn_RoundDate` $$
CREATE FUNCTION `dbname`.`fn_RoundDate` (dateArg DATETIME, formatArg CHAR) RETURNS DATETIME
DETERMINISTIC
BEGIN

 IF dateArg IS NULL OR formatArg IS NULL THEN
    RETURN NULL;
  ELSEIF formatArg = 'T' THEN -- Time
    RETURN CAST(dateArg AS TIME);
  ELSE
    -- Round to day
    SET dateArg = CAST(dateArg AS DATE);

   RETURN CASE formatArg
    WHEN 'D' THEN -- Day
     CAST(dateArg AS DATE)
    WHEN 'W' THEN -- Week
        ADDDATE(dateArg, INTERVAL 1 - DAYOFWEEK(dateArg) DAY)
    WHEN 'M' THEN -- Month
        ADDDATE(dateArg, INTERVAL 1 - DAYOFMONTH(dateArg) DAY)
      WHEN 'Q' THEN -- Quarter
        ADDDATE(MAKEDATE(YEAR(dateArg), 1), INTERVAL FLOOR(MONTH(dateArg) / 4) QUARTER)
    WHEN 'Y' THEN -- Month
        ADDDATE(dateArg, INTERVAL 1 - DAYOFYEAR(dateArg) DAY)
    ELSE -- Unknown format
     dateArg
    END;
  END IF;

END $$

DELIMITER ;

Why there isn't a built-in function to do something so useful--in either SQL Server or MySQL--is beyond me.

Tags:

MySQL

NHibernate & Spring.NET with Oracle

by timvasil 2/14/2008 1:50:00 PM
The documentation for using NHibernate with Oracle is a bit spotty.  Based on some internet searches and trial & error, here's what I've found to work:
  1. Download the Oracle client (or "instant client"):
     
    http://www.oracle.com/technology/software/products/database/oracle10g/htdocs/winsoft.html
     
    Without this client, you'll see the following error message when using Microsoft's Oracle driver:  Class Initialization method Imd.Tests.ServerApi.DeploymentTest.Initialize threw exception. NHibernate.HibernateException:  NHibernate.HibernateException: System.Data.OracleClient requires Oracle client software version 8.1.7 or greater. --->  System.Exception: System.Data.OracleClient requires Oracle client software version 8.1.7 or greater.
     
  2. Set the ORACLE_HOME environment variable and restart your IDE.  An example value is C:\Oracle\product\10.1.0\Client_1. If you don't set this variable you get an error message stating:
     
    Unable to load DLL
    'OraOps10.dll': The specified module could not be found
     
     
    If you specify the wrong directory, i.e. specify a directory one level too high or too low, you'll see bizarre exceptions, including NullReferenceExceptions circa Oracle.DataAccess.Client.OracleException.get_Source() or Oracle.DataAccess.Client.OracleException.get_Message().
      
  3. Add a project assembly reference to Oracle.DataAccess.dll.  I found this in C:\Oracle\product\10.1.0\Client_1\BIN.
     
  4. Update your App.config (or Web.config) properties to specify the Oracle connection string, driver, dialect, etc:

        <db:provider provider="OracleClient-2.0"
                     connectionString="Data Source=(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = {hostname})(PORT = 1521)))(CONNECT_DATA = (SERVICE_NAME = {serviceName})));User Id={username};Password={pass};"/>
     
        <object id="ConfigDbSessionFactory"
                type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
            <property name="HibernateProperties">
                <dictionary>
                    <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
                    <entry key="hibernate.dialect" value="NHibernate.Dialect.Oracle9Dialect"/>
                    <entry key="hibernate.connection.driver_class" value="NHibernate.Driver.OracleClientDriver"/>
                </dictionary>
            </property>
           . . .
        </object>

These instructions are for using Microsoft's .NET driver (System.Data.OracleClient / V2.0.0.0).  A few settings would have to change if you want to use Oracle's .NET driver driver instead (System.DataAccess.Client / V2.102.2.20):

  1. Change OracleClient-2.0 to OracleODP-2.0.
  2. Change NHibernate.Driver.OracleClientDriver to NHibernate.Driver.OracleDataClientDriver

According to the NHibernate documentation, "Microsoft's driver does not handle long character strings correctly. An error happens in some circumstances when using a string of length 2000-4000 as a parameter value.  Oracle cannot handle empty strings (""), you should use null instead. An IUserType implementation to perform the conversion is contained in Nullables.NHibernate library (part of NHibernateContrib package)."  I've also seen problems with the Microsoft driver not being able to convert a Guid into a byte[].  So pick your poison :-)

Common Pitfalls:
  • You may recieve a perplexing "invalid username/password" error when your username and password is, in fact, correct.  ODP.NET might be adjusting the case of your password before hashing it and sending it to Oracle; you can get around this by turning off case sensitivity:
    ALTER SYSTEM SET SEC_CASE_SENSITIVE_LOGON = FALSE;
  • You may see NullReferenceExceptions with Oracle.DataAccess.Client.OracleException.get_Source() in the stack trace.  This may be indicative of a mismatch between the Spring.NET provider name and the NHibernate driver, i.e. using an Oracle provider name and a Microsoft driver.  If you specify OracleClient-2.0, use the NHibernate.Driver.OracleDataClientDriver driver; if you specify OracleODP-2.0, use NHibernate.Driver.OracleClientDriver.

Tags:

.NET Framework | Hibernate | Oracle | Spring.NET

Exporting FusionChart images

by timvasil 2/5/2008 11:35:00 AM

FusionCharts is a flash-based animated charting package.  One of its recent features is the ability to export the chart images so end users can save them to disk.  Unfortunately the sample code provided gives only PHP and C# examples of how to do this, which isn't so handy if you're using Java. So, in the public interest, here is a Java of that code:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
    // Extract parameters
    int width = Integer.parseInt(req.getParameter("width")) - 1;
    int height = Integer.parseInt(req.getParameter("height"));
    String bgColorStr = req.getParameter("bgcolor");
    int bgColor = (bgColorStr == null || bgColorStr.length() == 0) ? 0xffffff : Integer.parseInt(bgColorStr, 16);
    String data = req.getParameter("data");
   
    // Build the bitmap image
    BufferedImage image = buildBitmap(width, height, bgColor, data);
   
    // Compress the image as a JPEG
    ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
    ImageWriteParam writerParam = writer.getDefaultWriteParam();
    writerParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    writerParam.setCompressionQuality(0.95f);

    // Stream the image to the user agent
    resp.addHeader("Content-Disposition", "attachment; filename=\"FusionCharts.jpg\"");
    resp.setContentType("image/jpeg");
    ImageOutputStream imageOut = ImageIO.createImageOutputStream(resp.getOutputStream());
    writer.setOutput(imageOut);
    writer.write(null, new IIOImage(image, null, null), writerParam);
    imageOut.flush();
    imageOut.close();
}
   
private BufferedImage buildBitmap(int width, int height, int bgColor, String data)
{
    BufferedImage chart = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    String[] rows = data.split(";");
    int colIdx = 0;
    for (int rowIdx = 0; rowIdx < rows.length; rowIdx++)
    {
        // Split individual pixels
        String[] pixels = rows[rowIdx].split(",");
        colIdx = 0;
        for (int pixelIdx = 0; pixelIdx < pixels.length; pixelIdx++)
        {               
            // Split the color and repeat factor
            String[] clrs = pixels[pixelIdx].split("_");  
            int color = ("".equals(clrs[0])) ? bgColor : Integer.parseInt(clrs[0], 16);
            int repeatFactor = Integer.parseInt(clrs[1]);
           
            // Set the color the specified number of times
            for (int repeatCount = 0; repeatCount < repeatFactor; repeatCount++, colIdx++)
            {                      
                chart.setRGB(colIdx, rowIdx, color);
            }
        }
    }
   
    return chart;
}

Note:  I think there's a bug in the Flash image exporter.  It looks like it's reporting the width of the image to be 1 pixel greater than whan it actually is, hence the expression Integer.parseInt(req.getParameter("width")) - 1

Tags:

FusionCharts | Java

WCF serialization with NHibernate object graphs

by timvasil 2/5/2008 11:05:00 AM

Say you have a complex object graph with circular references and you need to send it across the wire as XML.  The good ol' XmlSerializer is not going to help you; you need a more powerful too like WCF's DataContractSerializer.  By slapping [DataContract] onto the classes in your object graph and [DataMember] onto the non-transient properties and fields of these classes, you're golden.  WCF serializes cyclical object graphs without complaint.  Well, almost.

If you're using Hibernate's lazy-initialization feature, you'll see a problem:  .NET will compain about unknown types (proxy wrappers) that need to be registered as well-known types.  Unfortunately the types aren't known until run-time.  Sure, you could traverse the object graph at runtime, figure out these proxy types, and pass them into a DataContractSerializer costructor, but that's far from elegant.  Even if you make it over that hurdle, .NET will complain about Hibernate's persistent set implementations too; you'll have to add those types too.  If you're using generics, that could quickly balloon to a lot of types.

Fortunately there's a straightforward solution:  IDataContractSurrogate.  WCF provides this interface specifically to get around sticky situations like this.  You can use it to unwrap and initialize proxies, and to replace Hibernate's persistent collections with built-in well-known collections. Here's how it works:

public class HibernateDataContractSurrogate : IDataContractSurrogate
{
    public HibernateDataContractSurrogate()
    {
    }

    public Type GetDataContractType(Type type)
    {
        // Serialize proxies as the base type
        if (typeof(INHibernateProxy).IsAssignableFrom(type))
        {
            type = type.GetType().BaseType;
        }

        // Serialize persistent collections as the collection interface type
        if (typeof(IPersistentCollection).IsAssignableFrom(type))
        {
            foreach (Type collInterface in type.GetInterfaces())
            {
                if (collInterface.IsGenericType)
                {
                    type = collInterface;
                    break;
                }
                else if (!collInterface.Equals(typeof(IPersistentCollection)))
                {
                    type = collInterface;
                }
            }
        }

        return type;
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        // Serialize proxies as the base type
        if (obj is INHibernateProxy)
        {
            // Getting the implementation of the proxy forces an initialization of the proxied object (if not yet initialized)
            obj = NHibernateProxyHelper.GetLazyInitializer((INHibernateProxy)obj).GetImplementation();
        }

        // Serialize persistent collections as the collection interface type
        if (obj is IPersistentCollection)
        {
            IPersistentCollection persistentCollection = (IPersistentCollection)obj;
            persistentCollection.ForceInitialization();
            obj = persistentCollection.Entries(); // This returns the "wrapped" collection
        }

        return obj;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        return obj;
    }

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
    {
        return null;
    }

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null;
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return null;
    }

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
    {
        return typeDeclaration;
    }
}

To use this surrogate, simply pass in an instance to the DataContractSerializer constructor:

DataContractSerializer serializer = new DataContractSerializer(typeof(rootObj.GetType()), null, int.MaxValue, false, true, new HibernateDataContractSurrogate());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, rootObj);

Notes:

  • With this technique, you avoid having to translate your Hibernate objects into DTOs before serialization.
  • There's an alternative:  you can use NetDataContractSerializer, however that would assume you've got NHibernate and Castyle.DynamicProxy at the other end of the wire.  It also requires a bit more legwork, as described here.
  • WCF doesn't know about Iesi sets, so you're still on the hook for adding attributes such as [WellKnown(typeof(HasedSet<type>))] to classes that use sets.

Tags:

.NET Framework | Hibernate | WCF

ResultSets vs. Hibernate

by timvasil 2/1/2008 8:14:00 PM

When writing Java code requiring object/relational mapping to an underlying relational database, I've always relied on Hibernate.  Recently, though, I tackled a small project dealing with only a few objects and primarily read-only access to the data.  I thought it'd be the perfect opportunity to use JDBC directly--to keep the project simple.  The code ended up looking pretty clean.  Here's a sample data access method to log in a user and provide some information related to the user, namely the Customer object associated with the user and all the "Sites" associated with that customer:

try
{
    final NamedParameterStatement stmt = new NamedParameterStatement(getConnection(),
            "SELECT u.*, c.*, cs.*, us.CustomerSiteId AS UserSiteId FROM tbl_users u" +
            " INNER JOIN tbl_customers c ON u.CustomerId = c.CustomerId" +
            " INNER JOIN tbl_customer_sites cs ON cs.CustomerId = c.CustomerId" +
            " LEFT OUTER JOIN tbl_user_sites us ON us.CustomerSiteId = cs.CustomerSiteId" +
            " WHERE LOWER(Username) = :username AND (:password IS NULL OR u.Password = :password)");
    stmt.setString("username", StringUtils.nonNullify(username).toLowerCase());
    stmt.setString("password", password);
    final ResultSet results = stmt.executeQuery();
    if (!results.first())
    {
        // No user found
        return null;
    }

    // Hydrate customer
    final Customer customer = HydrationUtils.hydrateCustomer(results);
   
    // Hydrate user
    final User user = HydrationUtils.hydrateUser(results);
    user.setCustomer(customer);
   
    // Hydrate customer sites and determine user accessible sites
    results.beforeFirst();
    while (results.next())
    {
        final Site site = HydrationUtils.hydrateSite(results);
        customer.addSite(site);
        if (results.getObject("UserSiteId") != null)
        {
            user.getAccessibleSites().add(site);
        }
    }
   
    if (updateLastLogonTime)
    {
        final NamedParameterStatement updateStmt = new NamedParameterStatement(getConnection(),
                "UPDATE tbl_users SET LastLogon = NOW() WHERE UserId = :userId;");
        updateStmt.setInt("userId", user.getId());
        updateStmt.executeUpdate();
    }
   
    return user;
}
catch (Exception e)
{
    s_log.error(e);
    throw new RuntimeException(e);
}
finally
{
    close();

There are a number of things to notice about this code:

  • Java doesn't support named parameters.  I had to write my own NamedParameterStatement class so I could perform parameter substitution using named parameters.
  • Though you don't see its implementation, my getConnection() method pulls a connection from a c3p0 connection pool.  Without Hibernate, I have to manage connection strings and the connection pool.
  • The "hydration" process (aka relational to object mapping) is handled by the HydrationUtils class.  This is a bit clunky and an annoyance to write, with a bunch of setter calls and data conversions (e.g. from java.sql.Date to java.util.Date).
  • The work to ensure there aren't multiple objects referring to the same row in a table is handled explicitly, i.e. the sites in the user's access list is pulled from the sites in the customer's list.  Hibernate would have handled this for me.
  • The underlying DDL script has to be written by me, and the embedded SQL limits portability.

Yes, there's a bit of work going on here.  Some of it is brittle.  For example, additions to the object model will not cause compile time errors, yet the HydrationUtils methods would need updating.  Such issues would likely not be caught until runtime.

As the application grew more complex and I found myself struggling to manage unique indexes and foreign keys in an ever-growing DDL script, I decided to switch to Hibernate.  Now, take a look at the same user logon code written Hibernate style:

return new TransactionRunner<User>() {
    @Override
    protected User doWork(Session session) throws Exception
    {
        // Prepare the user-fetching query--eagerly fetching associated customer and the customer's associated sites
        final Query userQuery = session.createQuery("from User as u " +
          " inner join fetch u.customer c" +
          " inner join fetch c.sites" +
          " left join fetch u.accessibleSites" +
          " where lower(u.username) = :username and (:password is null or u.password = :password)");
        userQuery.setParameter("username", username);
        userQuery.setParameter("password", password);
       
        // Fetch the user
        final User user = (User)userQuery.uniqueResult();
       
        // Update the user's last logon time (if so desired)
        if (updateLastLogonTime)
        {
            user.setLastLogon(new Date());
            session.flush();
        }
       
        // Replace persistent sets with regular sets for serialization
        user.setAccessibleSites(new HashSet(user.getAccessibleSites()));
        user.getCustomer().setSites(new HashSet(user.getCustomer().getSites()));
        session.setFlushMode(FlushMode.MANUAL);
        return user;
    }
}.run();

See the difference?

  • I wrote HQL instead of SQL to ensure portability.  No more NamedParameterStatement; Hibernate understands named parameters.  It also understands objects, so the query has an object-oriented feel to it.  I'm joining objects, not IDs.
  • There's no hydration code.  Hibernate does this for me.
  • Updating an object is as simply as changing its properties; I didn't have to write any SQL.
  • I didn't have to manage any databsae connection object explicitly.
  • I did write a TransactionRunner class to wrap the code in a transaction, so the begin/commit/rollback could happen at the appropriate time.  Writing data access methods within anonymous classes is a bit clunkly.  The alternative of using annotations with a pointcut-enabling component such as Spring crossed my mind, but I don't really have any need for most of its features at this point so I'll live with the callback.
  • I have to jump through a few hoops near the end of the method so I can get rid of Hibernate's persistent collections in favor of Java's built-in collections.  Even though Hibernate's collections implement the standard Java List, Map, and Set interfaces, they don't play nice with GWT, and I needed to serialize the object graph to a web client.

Overall I feel more comfortable with the Hibernate code.  I think it's cleaner and easier to maintain.  And next time I'm working on a simple little project, I won't be so quick to discount an O/R mapping tool; I believe it does save time, even for the small projects.

Tags:

Java | Hibernate | GWT

Search

Calendar

«  July 2014  »
SuMoTuWeThFrSa
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar

Recent comments

Archive