singleton that is dependent on a transient

5 views
Skip to first unread message

Carol F

unread,
Nov 4, 2009, 4:41:16 PM11/4/09
to ColdSpring-Users
I have three CFCs:
1. MailAccountManager.cfc - EmailServer Bean, manages email settings:
server info, username/password, etc
2. MailManager.cfc - this is the Email Bean, manages the actual email,
has setters for the email's FROM, TO, SUBJECT, BODY, TYPE (html or
text), etc, and has the sendMail() method which does the actual
<CFMAIL>. This bean is dependent on MailAccountManager.cfc since I
need the email settings to do the <CFMAIL>
3. MailService.cfc - this is the service cfc, and is dependent on
MailManager.cfc. I have two init functions. One does init that
basically does nothing. The second init is "initMail()" and this
initializes the mail parameters in MailManager.cfc. I have the first
init() since CS chokes if I use initMail() as the init(). I don't
know if I did that correctly. I have two methods in MailService.cfc
that sends email: mail() and mailTo(). Mail() will just send email
with whatever previous settings, but the email body is different which
is why it's required. MailTo() needs a to, from, body and subject
each and everytime. Here is the code:

<cfcomponent output="false">
<cffunction name="init" output="false">
<cfargument name="from" required="false" />
<cfargument name="to" required="false" />
<cfargument name="cc" required="false" />
<cfargument name="bcc" required="false" />
<cfargument name="type" required="false" />
<cfargument name="subject" required="false" />

<cfset VARIABLES.instance = {} />
<cfset VARIABLES.instance = ARGUMENTS />
<cfreturn this />
</cffunction>

<cffunction name="initMail" output="false">
<cfargument name="from" required="false" default="" />
<cfargument name="to" required="false" default="" />
<cfargument name="cc" required="false" default="" />
<cfargument name="bcc" required="false" default="" />
<cfargument name="type" required="false" default="" />
<cfargument name="subject" required="false" default="" />

<cfset VARIABLES.instance.from = ARGUMENTS.from />
<cfset VARIABLES.instance.to = ARGUMENTS.to />
<cfset VARIABLES.instance.cc = ARGUMENTS.cc />
<cfset VARIABLES.instance.bcc = ARGUMENTS.bcc />
<cfset VARIABLES.instance.type = ARGUMENTS.type />
<cfset VARIABLES.instance.subject = ARGUMENTS.subject />

<cfset getMailManager().setTo(ARGUMENTS.To) />
<cfset getMailManager().setFrom(ARGUMENTS.From) />
<cfset getMailManager().setCC(ARGUMENTS.CC) />
<cfset getMailManager().setBCC(ARGUMENTS.BCC) />
<cfset getMailManager().setType(ARGUMENTS.Type) />
<cfset getMailManager().setSubject(ARGUMENTS.Subject) />
</cffunction>

<cffunction name="mailTo" output="false">
<cfargument name="from" required="true" />
<cfargument name="to" required="true" />
<cfargument name="cc" default="" />
<cfargument name="bcc" default="" />
<cfargument name="type" default="html"/>
<cfargument name="subject" required="true" />
<cfargument name="body" required="true" />

<cfset getMailManager().mailTo( from="#ARGUMENTS.from#",
to="#ARGUMENTS.to#",
cc="#ARGUMENTS.cc#",
bcc="#ARGUMENTS.bcc#",
type="#ARGUMENTS.type#",
subject="#ARGUMENTS.subject#",
body="#ARGUMENTS.body#") />
</cffunction>

<cffunction name="mail" output="false">
<cfargument name="body" required="true" />

<cfset getMailManager().mail(body="#ARGUMENTS.body#") />
</cffunction>

<cffunction name="setMailManager" output="false">
<cfset VARIABLES.instance.MailManager = ARGUMENTS[1] />
</cffunction>

<cffunction name="getMailManager" output="false">
<cfreturn VARIABLES.instance.MailManager />
</cffunction>

<cffunction name="getVariables" output="false">
<cfreturn VARIABLES.instance />
</cffunction>
</cfcomponent>

1st question: For MailService.cfc, is that optimal, having two inits?
What would you recommend I would have done?
2nd question: I have all three CFCs as singletons in the application
scope. If I need to change the mail settings in
MailAccountManager.cfc, i.e. if I am sending from a different email
account, I do a:
MailService.getMailManager().getMailAccountManager().setUsername
("new_username")
MailService.getMailManager().getMailAccountManager().setMailServer
("new_mailserver")
and so on with whatever settings to be able to send email from that
server. Is this ok? It works, but I was wondering how else this
would have been architected/executed properly?
3rd question: I was thinking that since MailAccountManager.cfc has
the settings I have, then whenever I need to change email settings, I
can just do a createObject() for it, then set it in
MailServer.getMailManager().setMailAccountManager() with this new
object. This would make my two singletons MailServer and MailManager
dependent on a transient object. How would I set that up in my
coldspring.xml? Here is my current coldspring.xml:

<bean id="MailAccountManager"
class="com.prank.mail.MailAccountManager">
<constructor-arg name="server">
<value>${server}</value>
</constructor-arg>
<constructor-arg name="port">
<value>${port}</value>
</constructor-arg>
<constructor-arg name="username">
<value>${username}</value>
</constructor-arg>
<constructor-arg name="password">
<value>${password}</value>
</constructor-arg>
<constructor-arg name="useSSL">
<value>${useSSL}</value>
</constructor-arg>
<constructor-arg name="useTLS">
<value>${useTLS}</value>
</constructor-arg>
</bean>

<bean id="MailManager" class="com.prank.mail.MailManager">
<property name="MailAccountManager">
<ref bean="MailAccountManager" />
</property>
</bean>

<bean id="MailService" class="com.prank.mail.MailService">
<property name="MailManager">
<ref bean="MailManager" />
</property>
</bean>

Dennis Clark

unread,
Nov 4, 2009, 6:24:16 PM11/4/09
to coldspri...@googlegroups.com
On Wed, Nov 4, 2009 at 4:41 PM, Carol F <cfc...@gmail.com> wrote:

1st question: For MailService.cfc, is that optimal, having two inits?
What would you recommend I would have done?
 
Having two init methods is OK. Your real problem is that your second init method (initMail) for MailService is calling setters on another ColdSpring bean (MailManager). You should leave it completely to ColdSpring to set up the MailManager bean properties.

2nd question: I have all three CFCs as singletons in the application
scope.  If I need to change the mail settings in
MailAccountManager.cfc, i.e. if I am sending from a different email
account, I do a:
MailService.getMailManager().getMailAccountManager().setUsername
("new_username")
MailService.getMailManager().getMailAccountManager().setMailServer
("new_mailserver")
and so on with whatever settings to be able to send email from that
server.  Is this ok?  It works, but I was wondering how else this
would have been architected/executed properly?

No, this is not OK, this is in fact VERY BAD BAD BAD BAD BAD. This is terribly thread-unsafe: any requests using the same MailService bean to send a mail at the time you're running this code could easily end up sending email out through the wrong account, or simply fail completely. What do you think would happen if, between the getUsername() and setMailServer() calls in the current request, another completely different request tried to use the same bean send out an email?

In general, you never want to change the configuration properties of a singleton after it is configured. If you need your bean to use different settings for a particular service call, the settings should be passed in as arguments. For example, your MailManager's mail and mailTo methods could have the following:

<cfargument name="username" type="string" required="false" default="#getMailAccountManager().getUsername()#" />

Your methods could then use arguments.username instead of getMailAccountManager().getUsername() for the username to use in the CFMAIL call. The arguments.username variable would have the correct value regardless of whether or not it was passed in by the caller.
 
 
3rd question:  I was thinking that since MailAccountManager.cfc has
the settings I have, then whenever I need to change email settings, I
can just do a createObject() for it, then set it in
MailServer.getMailManager().setMailAccountManager() with this new
object.  This would make my two singletons MailServer and MailManager
dependent on a transient object.  How would I set that up in my
coldspring.xml?

Again, VERY BAD. This is the same issue as before, but at the object level instead of the property level. If you really want to manage all your transient changes to the email settings in a single object, I suggest you write a service method that returns a cloned copy of the "default" MailAccountManager bean, the add the following to your mail and mailTo methods:

<cfargument name="mailAccountManager" type="any" required="false" default="#getMailAccountManager()#" />

Your mail and mailTo methods should then use arguments.mailAccountManager instead of getMailAccountManager() for its MailAccountManager services.

There are even better ways to accomplish the above, and other architectural improvements that could be made, but my suggestions here aim to take you a step in the right direction.

Hope that helps,

Dennis

ch...@team193.com

unread,
Nov 4, 2009, 5:20:41 PM11/4/09
to ColdSpring-Users
Hi Carol,

Either I'm not following what your trying to do here, or something
isn't quite right with your setup.

To me, MailAccountManager and MailManager are not "managers" at all
they are simple entity beans.
It looks like MailAccountManager is actually your smtp server config
(i'll call that smtpServerConfig) and MailManager is a transient bean
that represents an email message (lets call it Email).
I'd have both with get/set methods for their properties, and a single
init method with an argument for each of their properties.

MailService should only be dependant on smtpServerConfig, and act as a
factory for instances of Email.

so here's the two beans i'd have

<bean id="smtpServerConfig" class="com.prank.mail.smtpServerConfig">

<constructor-arg name="server">
<value>${server}</value>
</constructor-arg>
<etc... />
</bean>

<bean id="MailService" class="com.prank.mail.MailService">
<property name="smtpServerConfig">
<ref bean="smtpServerConfig" />
</property>
</bean>

now when i want to send an email i'd have a method in MailService to
create it, looking something like

<cffunction name="createEmail">
<cfargument name="to">
<cfargument name="from">
<cf etc.... />

<cfset var email = createobject("component",
"com.prank.mail.Email").init(argumentCollection=arguments) />

<cfreturn email />
</cffunction>

then another method that accepts an instance of Email and does the
necessary to ship it out

<cffunction name="sendEmail">
<cfargument name="email" type="com.prank.mail.Email" />

<cfmail to="#arguments.email.getTo()#" from="..." >
#arguments.email.getBody()#
</cfmail>
</cffuntion>

I guess there's 1,000,001 ways to do this kind of thing and this is
just what seems right to me

Cheers, Chris

Carol F

unread,
Nov 4, 2009, 7:28:43 PM11/4/09
to ColdSpring-Users
What would you use a "manager" for?

I'm renaming my two CFCs to Email.cfc and smtpServerConfig.cfc. I
just want to know your naming convention. Why are you doing first-
letter-lowercase with smtpServerConfig but uppercase with Email.cfc?
> ...
>
> read more »

Brian Kotek

unread,
Nov 4, 2009, 8:13:19 PM11/4/09
to coldspri...@googlegroups.com
Standard convention for class names is camel case, so you'd probably want SmtpServerConfig or SMTPServerConfig.

Tony Nelson

unread,
Nov 4, 2009, 8:23:56 PM11/4/09
to coldspri...@googlegroups.com
Not to be picky, but I think you mean pascal case, not camel case. :)

Usually classes are written using pascal case and instances of the class are written using camel case. For example, <cfset smtpServerConfig = createObject("component","path.to.SMTPServerConfig") /> or <bean id="smtpServerConfig" class="path.to.SMTPServerConfig" />.

-Tony

Brian Kotek

unread,
Nov 4, 2009, 8:41:08 PM11/4/09
to coldspri...@googlegroups.com
Well, not to be more picky, but technically Camel Case doesn't necessarily denote the initial letter being capitalized or not. But in general, most people I know or have worked with just use "camel case" to denote the first letter being upper (aka "upper camel case" or "bumpy caps"), and "mixed camel case" or "headless camel case" to denote the first letter being lower. :-)

Tony Nelson

unread,
Nov 4, 2009, 8:48:07 PM11/4/09
to coldspri...@googlegroups.com
When in Rome.

-Tony

Gavin Baumanis

unread,
Nov 4, 2009, 11:29:16 PM11/4/09
to coldspri...@googlegroups.com
Hi Carol,

> What would you use a "manager" for?


Generically,
There is a manager and a gateway.

Some people call them different things - depending on (normally)
previous training.

Whenever you hear "Manager or Services Layer" you can think public API.
Whenever you hear gateway / DAO (data access objects) - you think the
"actual" code that does something.

So you might have the following CFCs.

userManager.cfc
userGateway.cfc

userManager might have a function;
userUpdate() - which accepts the userid and the fields you want the
application to be able to update.
(Note your database may in fact have many more columns than your
public API allows you to update.)

and you userUpdate() function will then onwards call a function in the
userGateway.cfc that actually performs the actual SQL for updating the
record as requested.

And the code is userGateway.userUpdate() - could be handwritten SQL,
or transfer or hibernate method calls.

The real benefit isn't seen until such tie as your pointy-haired boss
turns up one day and says I read in "XXX Magazine" today that
Hibernate is the way that everyone is doing things today.
Previously you had hand-written SQL... so now you just need to update
functions/methods in the userGateway object. - the rest of the
application remains untouched.

6 months later he turns up again and says - "You know, since we're an
Australian Company - we should support local developers and their
work.... Let use this other ORM called "Transfer"...
Again the ONLY code you need to change is that which is in the
userGateway.cfc -the rest of your application and the userManager.cfc
do not need to be changed at all - yet you've gone and changed the way
your application speaks with the database easily on three occasions. -
you haven't had to touch previously tested code - you haven't induced
any obscure errors.

Or perhaps you have changed your flavour of database... you've gone
from Oracle to PostgreSQL, perhaps. Again you only need to change the
code in the gateway
(and if you're using an ORM) then you probably haven;t even needed to
change that - just the ORM configuration file(s).


Hope that helps... and there are plenty of people here that can give
you their perspective if I haven't illustrated clearly enough ...

Gavin.
As always, please contact me if I can be of any further assistance.



Gavin "Beau" Baumanis
Senior Application Developer
PalCare P/L

657 Nicholson Street
Carlton North
Victoria, Australia, 3054

E: be...@palcare.com.au
P: +61 -3 9380 3513
M: +61 -438 545 586
W: http://www.palcare.com.au

Sean Corfield

unread,
Nov 5, 2009, 3:02:17 PM11/5/09
to coldspri...@googlegroups.com
I'll agree with Brian, the most commonly used terms are:

CamelCase

headlessCamelCase

I've never heard Pascal case used in the wild (it was one of my first
programming languages back in the 70's tho').

"No camels were harmed in the naming of these variables..."

Tony Nelson

unread,
Nov 5, 2009, 3:07:35 PM11/5/09
to ColdSpring-Users
Yeah I've only heard PascalCase from some of the .NET guys I work
with, although I think it's a little more descriptive than "upper
camel case" and "lower camel case". I guess it must be a Microsoft
thing: http://msdn.microsoft.com/en-us/library/ms229043.aspx

On Nov 5, 2:02 pm, Sean Corfield <seancorfi...@gmail.com> wrote:
> I'll agree with Brian, the most commonly used terms are:
>
> CamelCase
>
> headlessCamelCase
>
> I've never heard Pascal case used in the wild (it was one of my first
> programming languages back in the 70's tho').
>
> "No camels were harmed in the naming of these variables..."
>
Reply all
Reply to author
Forward
0 new messages