Newbie question on service initialization

103 views
Skip to first unread message

ndintenfass

unread,
May 27, 2010, 4:57:26 PM5/27/10
to framework-one
I have read a couple of the threads from earlier this year about
passing initialization arguments to a service -- seems the answer
always comes back to some version of "use ColdSpring [or insert
beanFactory of choice]". Forgive me for bringing this back up, but
I'm wading into FW/1 for the first time.

I'd like to find a way to not have to use all that extra machinery
just to do the very simple thing of giving my service some arguments
it needs to do its job. Here's the current scenario:

I have a CFC that acts as a client to a set of web services. That CFC
is designed such that normally you'd instantiate it in your
onApplicationStart() and pass into the init() method a couple simple
parameters (which version of the API to use, an API key). To use this
in a "FW/1 native way", am I correct to assume that putting it into
the services folder is the best approach? If so, is the answer to
passing it init arguments really that I need to set up a beanFactory
and maintain that factories XML file separately? I'm hoping I'm just
missing that obvious "duh" step and you kind folks can push me in the
right direction...

Thanks,

- Nathan Dintenfass

Sean Corfield

unread,
May 27, 2010, 5:43:08 PM5/27/10
to framew...@googlegroups.com
On Thu, May 27, 2010 at 1:57 PM, ndintenfass <nat...@dintenfass.com> wrote:
> I have a CFC that acts as a client to a set of web services.  That CFC
> is designed such that normally you'd instantiate it in your
> onApplicationStart() and pass into the init() method a couple simple
> parameters (which version of the API to use, an API key).  To use this
> in a "FW/1 native way", am I correct to assume that putting it into
> the services folder is the best approach?

If you're instantiating the CFC and managing it yourself, I'd probably
keep it out of the services/ folder so that it can't "accidentally"
get instantiated / executed by FW/1.

I generally have a model/ folder that contains the domain model CFCs
and my services/ folder only contains stuff that is managed by FW/1
itself.

So you'd declare setupApplication() in your Application.cfc to
instantiate your CFC and if you need access to it cleanly in the
controllers, you could add a getter (in Application.cfc) that returns
application.webServiceClient (or whatever it's called).

Then, if your controllers have an init() that takes the fw instance as
an argument, you could do this:

function init( fw ) {
variables.fw = fw;
variables.ws = fw.getWebServiceClient();
}

then you can just use variables.ws wherever you need access to the service.

That's probably the cleanest solution. Make sense?
--
Sean A Corfield -- (904) 302-SEAN
Railo Technologies, Inc. -- http://getrailo.com/
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

Nathan Dintenfass

unread,
May 27, 2010, 6:43:12 PM5/27/10
to framew...@googlegroups.com
Makes a lot of sense, thanks Sean.

I do wonder, though, if the point of "services" is for CFCs that DON'T
know about the framework, yet they are NOT for "external" CFCs that
aren't part of your "frameworked" world, they seem highly specialized.

Given how common it is to want to pull in such code, I wonder if some
kind of method in the framework to "register" my own singletons would
make sense -- that would create a "native" way in my controllers to talk
to such components yet give me total control over how to instantiate
them -- the register would be a key/value where the value is a component
(or really any arbitrary value), and I would then be able to get at it
without "hacking" in my own getters.

Seems the culture here is to actually implement and then submit such
enhancements rather than just talk about them, though ;)

Are most people just accepting that they'll use ColdSpring (etc) and
therefore don't face this concern?

Sean Corfield

unread,
May 27, 2010, 7:18:38 PM5/27/10
to framew...@googlegroups.com
On Thu, May 27, 2010 at 3:43 PM, Nathan Dintenfass
<nat...@dintenfass.com> wrote:
> I do wonder, though, if the point of "services" is for CFCs that DON'T know
> about the framework, yet they are NOT for "external" CFCs that aren't part
> of your "frameworked" world, they seem highly specialized.

Services has always been the part of FW/1 I've liked least. However,
the convention-based way to locate, instantiate (& cache), and call
methods on them seems to fit with the FW/1 ethos and many people seem
to like them.

> Given how common it is to want to pull in such code, I wonder if some kind
> of method in the framework to "register" my own singletons would make sense
> -- that would create a "native" way in my controllers to talk to such
> components yet give me total control over how to instantiate them -- the
> register would be a key/value where the value is a component (or really any
> arbitrary value), and I would then be able to get at it without "hacking" in
> my own getters.

To be honest, that's pretty close to a bean factory. Wrap it up in a
CFC that implements the following two methods and FW/1 could use it as
a bean factory:
* boolean function containsBean( name )
* any function getBean( name )

Here's how that would work:

// MyFactory.cfc:
component {

function init() {
variables.cache = {
fooService = new model.service.MySvc1( 42, "foo" ),
barService = new model.service.MySvc2( "bar" ),
otherService = new model.service.MySvc3()
};
return this;
}

function containsBean( name ) {
return structKeyExists( variables.cache, name );
}

function getBean( name ) {
return variables.cache[ name ];
}
}

Then in Application.cfc:

function setupApplication() {
var bf = new model.factory.MyFactory();
setBeanFactory( bf );
}

At this point you're all set. FW/1 will be able to lookup services and
wire them into controllers etc.

In your controllers, you can just add a setter for any services you
need and they'll magically appear - or FW/1 can map section.item to
method calls on those services:

// user.cfc controller:
function setFooService( fooService ) {
variables.fooService = fooService;
}
...
var info = variables.fooService.getInfo( 123 );
...
service( 'bar.calculateTax', 'tax' ); // queues up: rc.tax =
barService.calculateTax()

> Are most people just accepting that they'll use ColdSpring (etc) and
> therefore don't face this concern?

For any sizable model, I always use ColdSpring yes.

I noticed there's a project on RIAForge called Framefactory One which
essentially extends (an older version of) FW/1 by adding a
convention-based bean factory.

Nathan Dintenfass

unread,
May 27, 2010, 9:09:54 PM5/27/10
to framew...@googlegroups.com
That seems as elegant a solution as any I've seen elsewhere.

But, I'm struggling to make it work for some reason.

In my main controller method I can do:

rc.sessionFromFactory =
instance.fw.getBeanFactory().getBean("session").getSession();

But, I cannot do:

instance.fw.service("session.getsession","sessionFromFW1Service");

I thought that the first argument of the service is equivalent to
name.method and that the beanfactory should take over.

Sorry for the basic nature of my question... (is there a FW1 IRC?)

- Nathan

Sean Corfield

unread,
May 27, 2010, 10:09:50 PM5/27/10
to framew...@googlegroups.com
On Thu, May 27, 2010 at 6:09 PM, Nathan Dintenfass
<nat...@dintenfass.com> wrote:
> rc.sessionFromFactory =
> instance.fw.getBeanFactory().getBean("session").getSession();
>
> But, I cannot do:
>
> instance.fw.service("session.getsession","sessionFromFW1Service");

The convention is that the service bean name is xxxService -
sessionService in your case. Controller beans are called
yyyController. This ensures (in theory) that they don't clash with
other beans in your factory.

> I thought that the first argument of the service is equivalent to
> name.method and that the beanfactory should take over.

Call it sessionService in your factory and it will work (although
you'll need to say getBean('sessionService') in that first example. In
my example, my beans were called fooService, barService and
otherService. The setters (for injection) used the full bean name - as
would any call to getBean() - but the service() API in FW/1 used the
bean name without the Service suffix.

> Sorry for the basic nature of my question... (is there a FW1 IRC?)

Not yet but I probably should get back on IRC and monitor one. Most of
the channels I used to monitor were on freenode I think but I know the
most popular #coldfusion channel is on dal.net - do folks have a
preference? I'm guessing dal.net since that's where most IRC-savvy
CFers will already be hanging out? I've since found dal.net policies
on channel founders and registered nicknames to be a bit of a pain...

Nathan Dintenfass

unread,
May 27, 2010, 11:26:49 PM5/27/10
to framew...@googlegroups.com
So, the way "in" to my factory I need to call it fooService, but in the
fw.service() method I need to refer to it as "foo" -- I will admit that
feels a bit wobbly to me, but I guess I'll have to live with it ;)

I am both relieved and slightly worried to hear that you (Sean) are not
wholly satisfied with the way the "services" are working -- seems they
live in a limbo state between "not knowing" about the framework but
needing to be structured in such a way that the framework can use them
in a very specific way.

I'm still feeling my way through where I would put things like an
existing CFC that is a client to some web services or that does fancy
things with PDFs or etc. Feels almost like I would put them in a
"model" folder (which seems to be both a convention in FW/1 yet not part
of the actual framework in any way) but then have a service that exposes
specific methods to utilize it inside my controllers in a clean fashion.

While on the topic of using services: I thought I had read in the docs
that because getServiceKey defaults to data calls to service() need not
pass a second argument, but in practice that didn't work. I have
methods in my service that don't return any data at all but need to be
called inside my controllers, so I found myself just putting in a dummy
value for the key. Once past that, I found myself wondering if my
services could have method names custom-tailored to play nice with FW1,
why not let a service define its own getServiceKey (or
getRequestContextKey() perhaps) and call that when setting the key?

FWIW, I personally have found both of those IRC servers to be fine, but
I agree DAL.net is a bit cumbersome for op.

Oh, and THANK YOU SO MUCH for being so generous with your time, both in
creating the framework, then helping the uninitiated!

Sean Corfield

unread,
May 28, 2010, 12:45:40 AM5/28/10
to framew...@googlegroups.com
On Thu, May 27, 2010 at 8:26 PM, Nathan Dintenfass
<nat...@dintenfass.com> wrote:
> So, the way "in" to my factory I need to call it fooService, but in the
> fw.service() method I need to refer to it as "foo" -- I will admit that
> feels a bit wobbly to me, but I guess I'll have to live with it ;)

Yeah, the conventions come about because you often have a 'user'
controller and a 'user' service but there has to be a way to tell them
apart in the bean factory.

> I am both relieved and slightly worried to hear that you (Sean) are not
> wholly satisfied with the way the "services" are working -- seems they live
> in a limbo state between "not knowing" about the framework but needing to be
> structured in such a way that the framework can use them in a very specific
> way.

The only reasons they exist in their current form is:
a) to encourage folks not to write fat controllers (logic can easily
live in a convention-based service)
b) to remove the need for controllers that simply delegate to services
(the controller can be omitted if it's a simple delegate)

The services know nothing about FW/1. Conventions dictate their
location - or the name of the bean that represents them.

> Feels almost like I would put them in a "model" folder (which
> seems to be both a convention in FW/1 yet not part of the actual framework
> in any way) but then have a service that exposes specific methods to utilize
> it inside my controllers in a clean fashion.

Right, that keeps the core model out of the path of the conventions,
whilst establishing a convention for their location.

When I get time to write the Designing Applications guide, it'll cover
all of this. I've also started a Migrating Applications guide (to help
folks coming to FW/1 from other frameworks and from no-framework
code).

> While on the topic of using services: I thought I had read in the docs that
> because getServiceKey defaults to data calls to service() need not pass a
> second argument, but in practice that didn't work.

getServiceKey() lets you override the default key for the
convention-based call that FW/1 makes. Explicit service() calls always
require the action and the key. If you've got suggestions for
improving the docs to make that clearer, let me know!

> I have methods in my
> service that don't return any data at all but need to be called inside my
> controllers, so I found myself just putting in a dummy value for the key.

If you use an empty string for the key, FW/1 ignores the result. That
doesn't appear to be mentioned in the docs, now I look... sorry, I've
opened an issue to fix that.

> Once past that, I found myself wondering if my services could have method
> names custom-tailored to play nice with FW1, why not let a service define
> its own getServiceKey (or getRequestContextKey() perhaps) and call that when
> setting the key?

Because then the service would need to know about the framework which
is an absolute no-no as far as I'm concerned.

> Oh, and THANK YOU SO MUCH for being so generous with your time, both in
> creating the framework, then helping the uninitiated!

The community is great - makes me want to share!

Nathan Dintenfass

unread,
May 28, 2010, 9:00:01 PM5/28/10
to framew...@googlegroups.com
Just in case anyone cared, thought I'd share my solution to the service
issue. Again, my issue was trying to avoid using ColdSpring and having
to maintain configuration information for just a couple of simple
services, and to deal with the fact that my services were existing CFCs
built with zero knowledge of FW/1 (and I want to keep them that way) --
yet, both of these CFCs needed to be instantiated with some specific
configuration information, something FW/1 really only supports
"natively" by using a beanFactory implementation.

First, I created a very lightweight implementation of the beanFactory:

http://coldfusion.pastebin.com/0ktrxsDb

Then, I went ahead and just created my own instances, passing my
initialization arguments as I would in a non-framework CF app. I
register my instances with the beanFactory, and then FW/1 does the rest.

Here's the relevant bit of code in my Application.cfc:

http://coldfusion.pastebin.com/yDBiPeeh

Now, in my controllers I can use the native service() method, or if I
need in my init() in a controller I can just use getService() to give a
"raw" reference to the underlying service.

I still think the issue with services, as they exist today, is that they
are both supposed to NOT know anything about FW/1, yet FW/1 will make
assumptions about their structure (attempting to automatically call
certain methods on certain services based on your section/item) - my
solution allows me to both use the elegant and encapsulated FW/1 native
services functionality while easily being able to just drop the code in
without needing to create a new dependency on ColdSpring (or etc).

Would love any feedback on ways to make this cleaner, better, more
FW1ish, etc. (or any gotchas I'm not anticipating doing it this way).

- Nathan


Sean Corfield

unread,
May 28, 2010, 9:20:40 PM5/28/10
to framew...@googlegroups.com
On Fri, May 28, 2010 at 6:00 PM, Nathan Dintenfass
<nat...@dintenfass.com> wrote:
> Now, in my controllers I can use the native service() method, or if I need
> in my init() in a controller I can just use getService() to give a "raw"
> reference to the underlying service.

I hope you mean calling getService() on your bean factory?
fw.getBeanFactory().getService('foo')

FW/1's own getService() method is not intended to be called (per the
Reference Manual).

> Would love any feedback on ways to make this cleaner, better, more FW1ish,
> etc. (or any gotchas I'm not anticipating doing it this way).

I'd actually recommend putting your managed service CFCs in the model/
folder instead and only creating CFCs in the services/ folder where
you want to rely on FW/1's conventions (now that you have a bean
factory, FW/1 can autowire your independent services into the
FW/1-managed service CFCs).

I was surprised to see / in your component names in createObject() -
component paths are meant to be dot-separated, not slash-separated. It
works but it's odd-looking :)

Marco Betschart

unread,
May 29, 2010, 4:49:54 PM5/29/10
to framework-one
This may be a bit off-topic, but ran into a similar problem and have
also written my own "bean Factory". The clue is, that this bean
factory instantiates all my cfc's in a models respectively beans
folder for me.

The "bean Factory" injects one new function to every controller:
createBean(), so i have access to all my beans in all my controllers
and let them do for instance the validation of formular data.

There is a function injection into my services cfc's too. The "bean
Factory" injects the function createModel() into every service. This
allows me to run the same data manipulation code regardless in which
fw/1 section i am.

You may wan't to check the code out at RiaForge: http://ff1.riaforge.org

There is no sufficient documentation for Framefactory One yet - so if
you have questions, just email me.


On May 29, 3:20 am, Sean Corfield <seancorfi...@gmail.com> wrote:
> On Fri, May 28, 2010 at 6:00 PM, Nathan Dintenfass
>
> <nat...@dintenfass.com> wrote:
> > Now, in my controllers I can use the native service() method, or if I need
> > in my init() in a controller I can just use getService() to give a "raw"
> > reference to the underlying service.
>
> I hope you mean calling getService() on your bean factory?
> fw.getBeanFactory().getService('foo')
>
> FW/1's own getService() method is not intended to be called (per the
> Reference Manual).
>
> > Would love any feedback on ways to make this cleaner, better, more FW1ish,
> > etc. (or any gotchas I'm not anticipating doing it this way).
>
> I'd actually recommend putting your managed service CFCs in the model/
> folder instead and only creating CFCs in the services/ folder where
> you want to rely on FW/1's conventions (now that you have a bean
> factory, FW/1 can autowire your independent services into the
> FW/1-managed service CFCs).
>
> I was surprised to see / in your component names in createObject() -
> component paths are meant to be dot-separated, not slash-separated. It
> works but it's odd-looking :)
> --
> Sean A Corfield -- (904) 302-SEAN
> Railo Technologies, Inc. --http://getrailo.com/
> An Architect's View --http://corfield.org/

AJ Mercer

unread,
May 29, 2010, 7:57:59 PM5/29/10
to framew...@googlegroups.com
Does every controller really need every Bean, and every method of each bean?

Have you thought about creating an API CFC?
This is one component that exposes every public method required by the application.

So the API could have saveCustomer()
which passes the call onto customer.save()
which internally may route to customer.insert() or customer.update() and call validate() (private methods)

Your application would only then need to instantiate the API, and pass that to the controller.


The other neat thing about this approach is that you can refactor you model with out affecting you application
(given you don't change parameters or return types)


Also, you can crete multiple APIs, eg publicAPI - general web site usage, internalAPI - administration side of the application
That way, controllers that don't do any admin functions can have a smaller API;
in terms of weight (file size), but more importantly the number of methods (so developers will not get overwhelming by stuff not relevant to them). You can there apply security to the internalAPI but not the publicAPI which makes things simpler. Also, you can also create superAPI which is an API for the other two (or more).





--

AJ Mercer
http://webonix.net
http://twitter.com/webonix

Nathan Dintenfass

unread,
Jun 1, 2010, 3:07:30 PM6/1/10
to framew...@googlegroups.com

On 5/28/10 6:20 PM, Sean Corfield wrote:
> On Fri, May 28, 2010 at 6:00 PM, Nathan Dintenfass
> <nat...@dintenfass.com> wrote:
>> Now, in my controllers I can use the native service() method, or if I need
>> in my init() in a controller I can just use getService() to give a "raw"
>> reference to the underlying service.
>
> I hope you mean calling getService() on your bean factory?
> fw.getBeanFactory().getService('foo')
>
> FW/1's own getService() method is not intended to be called (per the
> Reference Manual).


Ah, I see -- I wonder if it would be a "Good Thing" to prefix the
private methods you don't intend people to use, calling them something
like fw1GetService() or the ever-controversial underscore for private
methods like _getService(). With such enticing, simple names it's hard
to resist ;)

>
> I was surprised to see / in your component names in createObject() -
> component paths are meant to be dot-separated, not slash-separated. It
> works but it's odd-looking :)

It's from years of deploying into shared hosting environments into
arbitrarily nested directory structures where mappings were not an option.

Sean Corfield

unread,
Jun 1, 2010, 5:25:14 PM6/1/10
to framew...@googlegroups.com
On Tue, Jun 1, 2010 at 12:07 PM, Nathan Dintenfass
<nat...@dintenfass.com> wrote:
> Ah, I see -- I wonder if it would be a "Good Thing" to prefix the private
> methods you don't intend people to use, calling them something like
> fw1GetService() or the ever-controversial underscore for private methods
> like _getService().  With such enticing, simple names it's hard to resist ;)

Maybe I should randomly rename them in every new build? :)

Yeah, I'll probably do something for 2.0 to make sure the utility
methods are uncallable somehow...

> It's from years of deploying into shared hosting environments into
> arbitrarily nested directory structures where mappings were not an option.

Old habits eh? Even tho' you could do per-application mappings now...

Reply all
Reply to author
Forward
0 new messages