Saturday, October 31, 2009

CFThread Must Die (Kill Bill) - Part 1

I saw an interesting question on stackoverflow.com last week asking how to monitor cfthreads when using the CF Standard edition. The Enterprise edition comes with a very slick Server Monitor. However, that is off limits if you are running the Standard Edition. As I am the ever curious type, I decided to do some investigating. After a lot of digging I eventually came up with some (highly experimental) code that seems to work. As the experiment revealed a few interesting things about the internals of cfthreads, I decided to post it for the curious masses.


FX - It's Just an Illusion
Let me say up front this was a learning exercise. The code is highly experimental. I am not gutsy enough to use home-spun code to manage threads on a production system, over a commercial monitoring product.

Commercial products have several advantages over most home-spun code. First, most monitors use listeners to hook into the server. So they are capable of intercepting events at an early stage and routing them directly to an appropriate handler. That is far more elegant than trying to handle events after-the-fact in custom code. Second, commercial products are likely to be more robust and typically more thoroughly tested. That is especially important when it comes to threads. Threads can be a magical thing indeed. But if you have ever spent hours chasing down obscure bugs caused by a race condition in a single line of code, then you have a tiny inkling of the extremely bad things that can happen when threads are mishandled.

O-k-a-y! Now that I have completely burst your bubble (and hopefully weeded out the reckless in the process) on to the example ..

The Java Connection
Since cfthread is just a wrapper around java.lang.Thread, my first inclination was to approach the problem from the java angle. So I hit the old java trail and did some reading to brush up on my threads.

Now given that you can identify the Thread of the current page with a simple bit of java code, I imagined you could use something similar to locate other threads running on the server.

  <cfscript>
CurrentThread = createObject("java", "java.lang.Thread").currentThread();
</cfscript>

Now threads can belong to ThreadGroups. ThreadGroups are a bit like directories. They have a hierarchical structure and groups can contain both Threads and other ThreadGroups (ie files and subdirectories). So in theory you could work your way up the chain to identify all active threads.

A handy snippet from the slightly dated javaalmanac.com site provided an example of how to traverse ThreadGroups and display all active threads. With a small modification I was able to output the names of all my active threads. (The image below is a much condensed version)


That is when I noticed something curious. All of the threads I created started with the name "cfthread". (You have to love development teams that adhere to naming conventions!). The thread status, or more accurately the Thread.State, is similar to cfthread statuses. But with slightly different names: NEW, RUNNABLE, etcetera.

So I decided to try a brute force approach and stop() any cfthreads in a RUNNABLE state. Now, it is worth noting that Thread.stop() was deprecated because it can be unsafe under certain situations. But as this was just an experiment, I decided to give it a whirl.

In the Event of a Zombie Attack...
It seemed to work. But in one or two of my tests, the threads seemed to die. Yet did not seem to be removed from CF's list of running threads. Since the Standard Edition is limited to ten (10) threads, when I tried to fire up new threads they never executed. They just kept piling up in the queue. I am not sure if it was an anomaly or a flaw in my testing. But that, combined with the warnings about Thread.stop() convinced me to try a different approach.

Dumpster Diving
After much digging and copious usage of cfdump, I found a promising method in the FusionContext class called getUserThreadTask(). Following the trail of this method, I discovered that cfthread runs as a type of scheduled task, under a worker thread. Considering the set-it-and-forget-it nature of threads, the fact that they are scheduled was not that surprising.



The Task class looked extremely promising. It seemed to contain all of the methods I would need for tracking and stopping threads. But it took a bit of work to figure how and when to use the getUserThreadTask() method. As I will explain in Part 2.






----------------
* On a totally unrelated note, in case you were wondering, no I did not see the zombie movie referred to in this entry .. thank goodness.

It is late. Time to pack it in before I turn into a pumpkin. Happy Halloween ;)

...Read More

Tuesday, October 20, 2009

ColdFusion 8 Downloads (They're baaaaack)

Well, apparently the ColdFusion 8 Downloads are back on Adobe.com. While I cannot say I understand why they ever went away in the first place ... it is good to see they are back. But what with Halloween coming up, you may want to download a copy of CF8 now. Just in case they disappear again ;)

...Read More

Thursday, October 1, 2009

ColdFusion 8: Draw a Text Outline on an Image

In one of my older entries, I wrote about how to fill text with an image. A recent poster asked about doing something similar, except they wanted to draw an outline of the text instead of filling it with an image. As far as I know, this is not supported using the basic ColdFusion image functions. But there is a great example of how to do this in one of the older java2D tutorials. While there is not much to the example, it does use a few interesting concepts. So I decided to write up a quick CF translation.

All you need is a new image that supports transparency. Next, grab the underlying graphics object from the image. You will be using it to draw the text outline onto the image.


<cfscript>
transparentImage = ImageNew("", width, height, "argb");
graphics = ImageGetBufferedImage(transparentImage).createGraphics();
</cfscript>


Now here is where you veer into the java code. The first step in the example is to enable antialiasing for smoother text. This is achieved with RenderingHints. If you are not familiar with them, think of them as java's version of ImageSetAntialiasing().


<cfscript>
RenderingHints = createObject("java", "java.awt.RenderingHints");
graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);
graphics.setRenderingHint( RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY
);
</cfscript>


Next the example creates a graphical representation of the text. In other words, it gets the shape of the rendered text. This is done with a neat class called TextLayout. TextLayout handles all the hard work of positioning, spacing, text styles and generating the final glyphs.


<cfscript>
Font = createObject("java", "java.awt.Font");
textFont = Font.init("Arial", Font.PLAIN, width / 5 );
textColor = createObject("java", "java.awt.Color").decode("##80aa1c");
fontContext = graphics.getFontRenderContext();
layout = createObject("java", "java.awt.font.TextLayout").init(text, textFont, fontContext);
</cfscript>


But before the text shape can be drawn onto the image, it must be positioned. This is done with an AffineTransform. In this case it is a fancy way of saying move the x,y drawing coordinates. (I tweaked the code example to center the text).


<cfscript>
transX = (width/2) - (layout.getBounds().getWidth()/2);
transY = (height/2) + layout.getDescent();
transform = createObject("java", "java.awt.geom.AffineTransform").init();
transform.setToTranslation( transX, transY );
</cfscript>


Finally, the example grabs the shape of the text, sets the text color and draws the outline onto the image. That is all there is to it.


<cfscript>
shape = layout.getOutline(transform);
graphics.setColor(textColor);
graphics.draw(shape);
graphics.dispose();
</cfscript>




Full Code

<cfscript>
width = 330;
height = 150;
text = "Crossfade";

// Create a new image that supports transparency
transparentImage = ImageNew("", width, height, "argb");
graphics = ImageGetBufferedImage(transparentImage).createGraphics();

// Add rendering hints to smooth text edges
RenderingHints = createObject("java", "java.awt.RenderingHints");
graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);
graphics.setRenderingHint( RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY
);

// Create a graphical representation of the text
Font = createObject("java", "java.awt.Font");
textFont = Font.init("Arial", Font.PLAIN, width / 5 );
textColor = createObject("java", "java.awt.Color").decode("##80aa1c");
fontContext = graphics.getFontRenderContext();
layout = createObject("java", "java.awt.font.TextLayout").init(text, textFont, fontContext);

// Center text outline
transX = (width/2) - (layout.getBounds().getWidth()/2);
transY = (height/2) + layout.getDescent();
transform = createObject("java", "java.awt.geom.AffineTransform").init();
transform.setToTranslation( transX, transY );

// Draw the text outline onto the image
shape = layout.getOutline(transform);
graphics.setColor(textColor);
graphics.draw(shape);
graphics.dispose();
</cfscript>

<cfoutput>
<!--- display raw image --->
<cfimage source="#transparentImage#" action="writeToBrowser">
<!--- display WITH background --->
<div style="background-color: ##000080; width: #width#; height=#height#;">
<cfimage source="#transparentImage#" action="writeToBrowser">
</div>
</cfoutput>

...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep