Showing posts with label Lucee 5. Show all posts
Showing posts with label Lucee 5. Show all posts

Thursday 10 November 2016

PHP & CFML: I get thrown by how static variables work

G'day:
I like it when I demonstrate to myself I'm a bit thick. Today was one of those days. I've been working with PHP for a coupla years now, and pretty much have ported my OO knowledge over to it. Coming from a CFML background one thing I'm not so familiar with is static variables and methods. I "get" it, that's cool, but clearly I'm still mid-way up the learning curve, and some idiosyncrasies still elude me. I found one today.

I was doing some testing, and the situation I was in was that my test classes needed some subclassing... don't ask... it was legit but I cannot go into details and it'd bore you rigid anyhow. In my base class I had a static variable which I needed to have a different value for in the subclass (it was actually a sub-sub-class. Dumb tests). So I set it to a new value in my test method, then the test method called a method from the base class, and the test went splat cos it was still using the base class value. This confused the shit out of me at first. Here's a simplified example:

class GP {

    public static $test = "GP";
    
    public function fromGP(){
        return self::$test;
    }
    
    public function get(){
        return self::$test;
    }

}

class P extends GP {

    public static $test = "P";
    
    public function fromP(){
        return self::$test;
    }

}

class C extends P {

    public static $test = "C";
    
    public function fromC(){
        return self::$test;
    }

}

$c = new C();

echo "fromC: " . $c->fromC() . PHP_EOL;
echo "fromP: " . $c->fromP() . PHP_EOL;
echo "fromGP: " . $c->fromGP() . PHP_EOL;
echo "get: " . $c->get() . PHP_EOL;

So:

  • I've got a grandparent class (GP) which sets a static variable, and has two methods to get its value.
  • And a Parent class (P) that extends GP, with its own value for the static variable, and another different method to get its value.
  • And a Child class (C) which does the same thing, but to the Parent.
  • In my test rig I create a C instance, and call each of the methods.
Now cos I'm thick, I expected this output:

fromC: C
fromP: C
fromGP: C
get: C

Because C overrides the value for the static test variable, so the inherited methods should all use that value.

But what I did get is this:

fromC: C
fromP: P
fromGP: GP
get: GP

It took me a while to click. I changed my variable to be not static, and got the results I expected. Changed it back: back to the "wrong" (where "wrong" is "not what I wanted") answer. Then it dawned on me I guess. Static variables are bound to the class. I already knew this, but the ramifications didn't hit me initially. $c might be an object that inherits from P and GP, and obviously object variables will also follow inheritance rules, but static variables relate to the class, not the object. So in the class GP, in which the method fromGP is defined... self::$test refers to the static $test variable in that class. So: the value is GP. and on from there for fromP and down to fromC too (and get).

It made sense once I engaged my brain.

When I got home from work I decided to "ask" Java what it thought about this, and knocked together an analogous solution. Please pardon my shit Java skills.

package me.adamcameron.test;

public class GP {

    public static String test = "GP";
    
    public String fromGP(){
        return test;
    }
    
    public String get(){
        return test;
    }

}

package me.adamcameron.test;

public class P extends GP {

    public static String test = "P";
    
    public String fromP(){
        return test;
    }

}

package me.adamcameron.test;

public class C extends P {

    public static String test = "C";
    
    public String fromC(){
        return test;
    }

}

package me.adamcameron.test;

public class Test {

    public static void main(String[] args){
        C c = new C();
        
        System.out.println("fromC: " + c.fromC());
        System.out.println("fromP: " + c.fromP());
        System.out.println("fromGP: " + c.fromGP());
        System.out.println("get: " + c.get());
        
    }

}

And that - predictably - confirmed PHP's behaviour (not that I doubted PHP. Much):

>java me.adamcameron.test.Test
fromC: C
fromP: P
fromGP: GP
get: GP

Coolio. I'm also pleased I managed to write 20-odd lines of Java and get it right firstsecond time (it was a typo! ;-)

Last but not least, I remember that Lucee's flavour of CFML introduced static variables in Lucee 5, so I decided to see what it did:

component {

    static {
        test = "GP";
    }
    
    public function fromGP(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}

component extends=GP {

    static {
        test = "P";
    }
    
    public function fromP(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}

component extends=P {

    static {
        test = "C";
    }
    
    public function fromC(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}


<cfscript>
o = new C();

writeOutput("fromC: #o.fromC()#<br>");
writeOutput("fromP: #o.fromP()#<br>");
writeOutput("fromGP: #o.fromGP()#<br>");
writeOutput("get: #o.get()#<br>");
</cfscript>

When I ran this...

fromC: C
fromP: C
fromGP: C
get: C


Hrm. So Lucee does what I originally wanted, but I now reckon this is not what it should be doing. I best go ask someone from LAS what the story is there.

Anyway that was that. Just a small lesson learned for me today.

Oh, btw... the solution was to just make the variable not static. Then me code worked fine. There really was no need for it to be static in the first place, on reflection.

Update:
It's important to read Kalle's comment (below) regarding static:: in PHP.

Righto.

--
Adam

Sunday 24 January 2016

Lucee 5: Platform modularity in CFML

G'day:
I had a long (and often protracted) conversation with Denny - who seems to do some work on the Lucee project, but I really don't quite get where he fits into things - and later in the piece Geoff, who we all know as Daemon's representative on the LAS "board", currently holding the role of President. The topic was "WTF is OSGi, what does it mean to Lucee, why the hell is causing such delays in Lucee 5's release and why do I - as a CFML developer - care?". It wasn't a formal topic, but it just happened to be what we were yapping about.

It was a very frustrating conversation at times because Denny - nice bloke that he is - doesn't seem to be able to empathise with other people: everything he says is always positioned from his own perspective, and he struggles to "get" that other people have different perspectives which are not the same as his. So the conversation always crept back to how it would benefit him, or someone identical to him. Irrelevant.

Geoff came along later in the piece and made a lot more sense.

Sean came in at the very end and had his own commentary to make, most of which was over night for my perspective so I've just caught up with it now.

The challenge here is it's easy to "get" what OSGi is all about... and as one researches I think even more questions about it - especially in the context of Lucee - arise, but... yeah... bottom line it compartmentalises packages so they can have different dependencies, which has been a bit of a blight in the Java world in the past. OSGi is one of the ways to work around that. I am taking a very high level position on this cos I ain't a Java dev, don't want to be one, and dependency management is all a bit yawn-inducing. It's an important consideration, sure, but not terribly interesting. But we all get it, yeah? Say Lucee's using one module that requires SomeDependency v1.0.0; for my code, I need to use SomeDependency v1.2.0 because it introduces a new feature I want to leverage. This is currently a bit of a challenge given the way the JVM loads its libraries: one can have version v1.0.0 of SomeDependency, or v1.2.0, but not both. And what if something in 1.2.0 happens to break something Lucee needs? Splat. There are ways around all this, but they're not streamlined. Oh... and the way OSGi manages modules means they can be (un)loaded on the fly. Yep... lovely... impacts almost no-one in the course of their day-to-day work.

Lucee 5 is being rewritten to follow / implement the OSGi specification, which will solve all this. Lovely. It's not - on the face of it - hugely exciting for most CFML developers. Most CFML developers don't use Java modules, and even ones like me who do occasionally... this version clash situation has simply never been much of a problem. Obviously it'll be more of a problem for people who do this stuff more often, but that is not most Lucee users. So I've always been asking "why do I care about this in the context of Lucee? Who's the target audience? How big is the target audience? Is it really worth all these bloody delays with Lucee 5 to accommodate a very niche audience?" Up until last night I never got a satisfactory explanation, and it really just seemed like something Micha decided to do because Micha felt like doing it. Or maybe an self-indulgence so that it can be claimed Lucee is OSGi compliant, even if it's mostly an unhelpful exercise to almost all users of Lucee. None of the messaging from LAS justified the effort to me. They don't need to justify themselves to me, sure, but I'm part of the community, and I'm sure I'm not the only person wondering.

And then Geoff explained it: modularisation of the platform.

You know how for years we've been asking Adobe to modularise ColdFusion? Obviously the same applies to Lucee, except in a smaller way. ColdFusion's got a huge footprint, most of which one doesn't need most of the time, so why should one need to install it? Lucee's footprint is much smaller, but the same still applies.

That's interesting-ish in itself, but it has a very compelling knock-on effect. If everything is a module, then enhancements and bugfixes become a lot easier to deal with. One can mess around within the scope of the module's codebase, and provided one doesn't do anything daft, it won't impact the outside world (it needs to maintain its touchpoints to the outside world, obviously: its API). This means the testing and footprint for work on a given module is much smaller. And it makes the job just... easier. And whre things are easier, things progress faster, and more smoothly.

It will also mean that organisations that like to stay up to date and can implement changes expediently can do so. And it means that organisations which need to move more slowly with such things can probably round out their checks and balances a lot faster. It's less bureaucratic overhead to say "we're updating Lucee's PDF module" than it is to say "we're updating our Lucee installation". This in turn means that there doesn't need to be as much hesitation on the part of LAS if they do want to make breaking changes to modules... organisations who aren't ready for the breaking changes don't need to implement them. This means there can be a more dynamic attitude to moving things forward, as the "no person left behind" idea isn't so entrenched in whatever work is done. This goes back to Adobe's glacial approach to doing anything: the whole system is implemented and released as one huge blob, and everything has backwards compatibility issues, and even the smallest backwards compat challenge in one distant corner of the ColdFusion system will prevent the rest of it moving forward at a pace appropriate to 2016.

Update:

Another good example where this sort of thing pays off is detailed in Mary Jo's comment below: she needed to run an up-to-date version of Solr cos CF's one is so woefully out of date, but it wasn't really doable on CF due to its monolithic approach to packaging, and dependency spaghetti. This sort of thing will be much easier with Lucee 5 (not that they'd be so slack at keeping their libs up to date anyhow, but you know what I mean).


This is bloody great news for Lucee. I'm quite sure why they're not messaging it this way. Here's what they say about the OSGI work on their website (this is the page promoting it):

OSGi

Lucee 5 is completely OSGi based, OSGi is the defacto standard in most Java enterprise environments, to manage bundles (jar libraries) used by the environment.

This means all libraries used are managed by Lucee itself as OSGi bundles, but this does not end with 3rd party libraries. Lucee handles it's own core as an OSGi bundle, Lucee archives (.lar files) and even our Java based extensions are all OSGi based.

So Lucee 5 is an OSGi engine that manages all libraries used.

What is the benefit?

OSGi is a framework used in most major enterprise environments, it is well tested and stable.

OSGi allows for the running of different versions of the same library at the same time. Lucee for example is not using internally the latest version of the HSQLDB data source, but this does not stop you from using whichever version of this data source you like. OSGi allows you to use whichever version of a library you like, independent of what version of the same library Lucee itself is using or any other applications in the same environment.

We can update every library Lucee is using at any time without issue and without the need to restart the JVM. This means that Lucee patches can include updated libraries, making Lucee a lot more flexible than it was in the past.

Can I benefit from OSGi somehow?

Yes you can, Lucee 5.0 also comes with some new build in functions to use OSGi features directly.

Today you use createObject('java',"my.class.Path") to create Java Objects, but with the power of OSGi under the hood we can do more!

Lucee 5 introduces the function javaProxy() to load Java objects, this functions interface is optimized to OSGi, so you can do something like this:

dtf=javaProxy("org.joda.time.format.DateTimeFormat", "org-joda-time", "2.1.0");

The function syntax is:
javaProxy(string className [, string bundleName, string bundleVersion]);

Only the class name is required all other arguments are optional. Lucee will also download the bundles from the update provider automatically if they are not available locally.

The Lucee update provider already provides a wide range of bundles in various versions and this will be added to continuously overtime.

You can therefore load a certain class in a specific version, even, for example, if the Lucee core is using the same class with a different version.

OSGi is the biggest change in the Lucee 5.0 release that brings Lucee to a completely new level. Whilst it maybe not a dazzling new feature, it is a necessary one for an engine to be used in the enterprise segment.


Zzzzzzzzz. Oh sorry, you got to the end of that did you? Well done. I mean one can perhaps infer the bigger picture there if one takes the time, but - even as a reasonable adept technical person - I read that, my eyes glaze over, and I'm going "so the f*** what?". Sorry, but I am.

But if one frames it in the context of real world CFML devs/admins, dispense with the buzzwords and start it with "we've listened to the community and Lucee 5's platform is now completely modular [etc]", then you would have kept my interest all along.

I think the issue is that these docs are written by people so far up the Jefferies Tubes of Lucee they have lost sight of the fact they're not the core audience of Lucee. This was where I was struggling with Denny last night, and where I struggle in general with a bunch of the messaging from Lucee. In short, I guess, the technical people shouldn't be writing it or presenting it.

Now Geoff didsay he pointed out all this at the keynote of Dev.Objective(), and - yeah - he's the sort of person to understand how to message these things, but a presentation at Dev.Objective() is no use to most people, because most people weren't there. And to message didn't seem to trickle back out anyhow, so I wonder if it hit home anyway?

I really think this is a potentially excellent feature of Lucee, but not in the way they are currently positioning it. The message should not be about OSGi, it should be about modularity of the Lucee platform, and what immediate benefit it brings to Lucee's CFML community. Not to the few engineers behind the scenes who care about the implementation (we shouldn't need to care about the implementation for one thing), but how it's going to impact my life, as a Lucee CFML developer. It's not that OSGi has anything to do with it. It's that it modularises the platform.

I think there's a better story for LAS to be telling here.

Also in closing: thanks Denny, Geoff and Sean for the interesting discussion last night.

Righto. Time for breakfast and - more importantly - coffee.

--
Adam