1

I'm having trouble creating a sub-application using ColdSpring to instantiate my objects.

In my parent application, I have a ColdSpring config file which is instantiating a bunch of components with no problems. One of them is a 'Categories' (CategoryGateway) component, which has another component for the table (NestedSetTreeTable) injected into it. The injected component contains a package method, 'GetDatasource' which is called by the CategoryGateway component.

This works with no problems in my parent app, however when I try to instantiate the component in my sub-app, it seems that the package method is inaccessible to the CategoryGateway component.

Now, I'm very new to ColdSpring - and also to the concept of creating a sub-application from a parent app - so I'm not sure I'm doing things correctly.

Here's my set up:

Parent app contains a mapped version of ColdSpring and its own coldspring.xml. The coldspring.xml contains the following to instantiate the categories component:

<!-- Categories CFC -->
<bean id="categories" parent="config" class="com.categories.categoryGateway">
    <constructor-arg name="sCategoriesTable">
        <value>${sCategoriesTable}</value>
    </constructor-arg>
    <constructor-arg name="sNSTTable">
        <value>${sNSTTable}</value>
    </constructor-arg>      
    <constructor-arg name="NST">
        <ref bean="categoriesNST" />
    </constructor-arg>      
</bean>

<!-- Categories NST Table CFC -->
<bean id="categoriesNST" parent="config" class="com.categories.NestedSetTreeTable">
    <constructor-arg name="datasourceName">
        <value>${sDSN}</value>
    </constructor-arg>
    <constructor-arg name="table">
        <value>${sNSTTable}</value>
    </constructor-arg>
</bean> 

In my parent's Application.cfc, I instantiate the component thus:

Application.com.categories = beanFactory.getBean('categories');

All works correctly. Now, in my sub Application, I have another mapped version of ColdSpring, and a separate coldspring.xml so that I can use it to instantiate components specific to the sub app. I've checked that the correct coldspring.xml is being run.

I'm not instantiating the base components in my sub-app's Application.cfc, but am calling Super.onApplicationStart to get the parent's method to run. The parent's contains the instantiating code, however it's using the child's Coldspring.xml to find the components - therefore I've created a mapping to the parent app, and adjusted the child's Coldspring.xml accordingly - so this reads:

<!-- Categories CFC -->
<bean id="categories" parent="config" class="core.com.categories.categoryGateway">
    <constructor-arg name="sCategoriesTable">
        <value>${sCategoriesTable}</value>
    </constructor-arg>
    <constructor-arg name="sNSTTable">
        <value>${sNSTTable}</value>
    </constructor-arg>      
    <constructor-arg name="NST">
        <ref bean="categoriesNST" />
    </constructor-arg>      
</bean>

<!-- Categories NST Table CFC -->
<bean id="categoriesNST" parent="config" class="core.com.categories.NestedSetTreeTable">
    <constructor-arg name="datasourceName">
        <value>${sDSN}</value>
    </constructor-arg>
    <constructor-arg name="table">
        <value>${sNSTTable}</value>
    </constructor-arg>
</bean> 

There are several other core components that are loaded in this way before the categories one throws an error - so the technique appears to be working and I have access to my core components without explicitly instantiating them in the child's Application.cfc. However, when it gets to the categories component, I recieve the following error:

Bean creation exception during init() of core.com.categories.categoryGateway

component [core.com.categories.NestedSetTreeTable] has no function with name [getDatasource]:

If I make the getDatasource method public, it seems to work - however this doesn't seem right, since it works as is in the parent app.

Add to the list of things that I'm new to, OOP and inheritance... so if I'm going about this totally the wrong way then please let me know. What I'm attempting is the separation of core functionality (user management, error handling, validation and a bunch of stuff I tend to reuse in every app) from specific app functionality (an e-commerce site may have a cart component not required by a CMS, for instance) in such a way that as I develop a new feature, I can decide to put it in the core and make it available to all my apps, or in the specific app. So in this example, I'm attempting to load core functionality in the parent's Application.cfc and have the child automatically be able to use it. Ideally the child shouldn't have to specify the core functionality in the Coldspring.xml, but I've not found a way of running both the parent's and the child's.

Any help, much appreciated!

Community
  • 1
  • 1
Gary Stanton
  • 1,435
  • 12
  • 28
  • I recommend you break this out into two questions (because they *are* two questions). 1) the issue with the `getDatasource()` method; 2) how to configure your parent/sub app with Coldspring. And possibly a third questions - although not a good fit for S/O - about the architecting of your application. These are all mostly unrelated questions, and overly complicate what you're asking here. – Adam Cameron Apr 07 '13 at 21:16
  • You might have more luck asking Coldspring-specific questions on the Coldspring forum (https://groups.google.com/forum/?fromgroups#!forum/coldspring-users). It doesn't look like there's much Coldspring expertise on S/O. – Adam Cameron Apr 07 '13 at 21:28

1 Answers1

1

The injected component contains a private method, 'GetDatasource' which is called by the CategoryGateway component.

You are mistaken here. If GetDatasource is private to NestedSetTreeTable, then CategoryGateway has no access to it. Fullstop.

One can only call private methods from within the component itself, or in a situation where ComponentChild extends ComponentParent, ComponentChild can call private methods from ComponentParent.

It is irrelevant that CategoryGateway takes a NestedSetTreeTable as an argument: that does not confer any special access for CateoryGateway to NestedSetTreeTable's methods.

Coldspring is also irrelevant here: it is just a mechanism for marshalling the instantiation of objects, it does not confer any special powers, nor break the rules of method access.

So this being the case - and I don't doubt you are seeing disparate situations between your environments - you are omitting some key information from your question.

Adam Cameron
  • 29,677
  • 4
  • 37
  • 78
  • Ahh, sorry - I was mistaken... the access is 'Package', not 'Private'. Until very recently I've been using CFCs to encapsulate functions and not really making use of OOP, so all my own CFCs have generally contained public methods... Thus I am easily confused. ;) I'll update the question to reflect this. The Gateway and NestedSetTreeTable CFCs work perfectly in the parent app, (and I didn't write them!) so I'm pretty sure that the issue is going to be with my configuration rather than the CFCs themselves. – Gary Stanton Apr 07 '13 at 21:17
  • Still: you should not see different behaviour. Coldspring will not affect anything along these lines. Try factoring out Coldspring whilst troubleshooting: just use createObject() calls to instantiate the CFCs. This might give a clearer picture of what's going on. – Adam Cameron Apr 07 '13 at 21:24
  • Does your subapp have any different CF mappings that could be confusing matters so that CF thinks those two CFCs aren't in the same package? This is very doubtful (and I cannot see hot it could be the case), but it's the only thing I can think of. – Adam Cameron Apr 07 '13 at 21:26
  • Right you are... I've taken Coldspring out of the equation and I get the same issue instantiating manually. Doesn't make any sense to me. I have a couple of other mappings, but nothing that should come close to this instantiation. I'm going to have a tinker and see if I can get more info. Clearly this question doesn't have anything to do with Coldspring, but I'll hold back on updating it until I've figured out what I should update it /with/. – Gary Stanton Apr 07 '13 at 22:00
  • Can you pls post the following things to pastebin or something: 1) your Application.cfc files (main and sub app). We only need the stuff in the pseudo-constructor, not the actual methods; 2) a representation of the directory structure of both apps, indicating both the HTTP doc root and the CF root, and where the Application.cfc and CFC files are; 3) the instantiation code for the CFCs (which might be onApplicationStart(), I guess?); 4) the relevant bits of the CFC code needed to replicate this. So a cut down init() method, plus the method not being found. Anything else should be irrelevant. – Adam Cameron Apr 07 '13 at 22:38
  • I appreciate your willingness to wade through my code! It seems however, somewhat embarrassingly, that this is now working after a server reboot... I'm wondering if this was caused by Railo caching something in the mapping maybe? With regard to your comment about the architecture of my app, I've been working on it for a while now and found it very difficult to find examples, tutorials or discussions on the *right* way to do this. My app/sub-app seems to be feasible, but doesn't entirely feel right. Do you have an opinion on a methodology I could research? – Gary Stanton Apr 07 '13 at 23:18
  • Heh. Oops. Um, from what you describe, it sounds to me like yer on the right track. Possibly google "application.cfc parent child" and/or "application.cfc inheritance" and wade through the first few pages of results to see what people are saying. That'll give you good info and more ideas for rfined search string. – Adam Cameron Apr 07 '13 at 23:47