No, what you are suggesting is reasonable when there's only one variation from the common ServerRuntime, which is currently my use case. What you're saying about caching this unrestricting qualifier runtime makes sense for my situation, and I like to stick to the recommended practices whenever possible.
The problem would be for when there are several. In one instance, I might need to turn off PaymentMethod's qualifiers. In another, I need to turn off PaymentHistory's qualifiers, in a third, turn off both of them, in a fourth turn off a different entities. I don't see that this solution scales, but that's not really a problem for my situation. It just seems like there's no reason not to let people create a copy of a runtime from an existing runtime so they don't have to create a duplicate runtime out there for some obscure use case that doesn't warrant storing it globally. It's a hypothetical situation, so I guess it's not really an issue at present. On Fri, Sep 27, 2013 at 1:48 PM, Andrus Adamchik <[email protected]> wrote: >> I was able to do this in six easily-understandable lines of code with >> a ServerRuntime(Modules) constructor, and I fear that your suggested >> approach requires me to create several separate classes, store >> configuration globally, and create new modules for each qualifier that >> I need to disable or change. > > No, nothing like that :) I assume in both your and John's cases any number of > runtimes created have application scope (runtimes are not cheap, so you won't > be starting them ad-hoc). I noticed you have ServiceLocator, so keep using > that. And just implement 2 methods there, each indicating the purpose and/or > flavor of a given runtime, say: > > - getCommonRuntime() > - getAdminRuntime() > > And inside ServiceLocator in place where you create "common" runtime, create > "admin" one as well from the same set of modules. And use the same 6 lines of > code to customize the later. Those 6 lines you can either place in a subclass > of XMLDataMapLoader (redefining it in a module), or do it after the runtime > is created (like you are doing now). For the purpose of this discussion this > is actually irrelevant, so I take back my earlier remark about pre and post > 3.1 way of doing things. > >> And John's use case is even more straight-forward than my own. > > Same solution. > > I guess I am too much of a dependency injection person, always assuming that > referencing some common object is cheap and easy. So maybe I am overlooking > something? > > Andrus > > > On Sep 27, 2013, at 8:03 PM, Mike Kienenberger <[email protected]> wrote: > >> While I can understand the desire for purity with that pattern, it >> seems to me that it's too expensive to maintain in this use case. >> >> I don't want to have to maintain a factory for building runtimes and >> track all of the configuration needed to do that, just to turn off a >> qualifier. The existing runtime contains everything necessary -- >> there's just no easy way to get at it. >> >> Maybe I am overlooking something. >> >> I know you are busy, but can you provide an example of how I would >> perform the same task for which I provided code (turn off a qualifier >> in an otherwise duplicate ServerRuntime deep inside business logic) >> using modules? >> >> I was able to do this in six easily-understandable lines of code with >> a ServerRuntime(Modules) constructor, and I fear that your suggested >> approach requires me to create several separate classes, store >> configuration globally, and create new modules for each qualifier that >> I need to disable or change. >> >> And John's use case is even more straight-forward than my own. >> >> >> On Fri, Sep 27, 2013 at 12:49 PM, Andrus Adamchik >> <[email protected]> wrote: >>>> So it looks like I can reuse modules to create my own runtime. >>> >>> Yes. >>> >>>> However, the current 3.1 API isn't very friendly toward this approach. >>> >>> >>> Yes - see my other email. A common pattern is to have some kind of external >>> factory that defines the modules. >>> >>> Also I don't perceive 'copy' method as very useful. The point of a second >>> runtime is that it is somehow different from the first, and modules is what >>> makes it different. I think you are doing it in a pre-3.1-DI-way - creating >>> a runtime first, then customizing it. I would usually place customization >>> code inside the modules, which are kind of deferred "closures". >>> >>> Andrus >>> >>> >>> On Sep 27, 2013, at 7:39 PM, Mike Kienenberger <[email protected]> wrote: >>> >>>> So it looks like I can reuse modules to create my own runtime. >>>> However, the current 3.1 API isn't very friendly toward this approach. >>>> I had to clone the ServerRuntime class so that I could create my own >>>> version so I could call CayenneRuntime(Module... modules) in my >>>> constructor. >>>> >>>> Having a cloned ServerRuntime on which to make customizations seems >>>> like a reasonable use case that we should support. Can we add such a >>>> method to ServerRuntime? I think that's a better choice than making >>>> it easier to create a ServerRuntime subclass. >>>> >>>> private ServerRuntime(Module... modules) { >>>> super(modules); >>>> } >>>> >>>> public ServerRuntime copy() { >>>> return ServerRuntime(serverRuntime.getModules()); >>>> } >>>> >>>> It might be reasonable to make the private constructor public so that >>>> someone can create ServerRuntimes where they are not required to pass >>>> configurationLocation data but can provide their own alternative to >>>> ServerModule. This would have also worked in my case, removing the >>>> need to have a copy() method. However, I think the copy() method >>>> provides assurance to the developer that this approach is viable. >>>> >>>> >>>> Here's what my application code ended up looking like (using my cloned >>>> class), and I think it's a good approach (except for the cloned class) >>>> for these cases: >>>> >>>> PaymentMethod paymentMethod = paymentHistory.getPaymentMethod(); >>>> if (null == paymentMethod) { >>>> ServerRuntime currentRuntime = >>>> ServiceLocator.getCayenneRuntime(); >>>> >>>> // Payment method was invalidated at this point -- create >>>> a runtime that can read invalidated payment methods >>>> >>>> CopiedServerRuntime copiedRuntime = new >>>> CopiedServerRuntime(currentRuntime); >>>> DataDomain dataDomain = copiedRuntime.getDataDomain(); >>>> dataDomain.setSharedCacheEnabled(false); >>>> >>>> EntityResolver entityResolver = dataDomain.getEntityResolver(); >>>> ObjEntity paymentMethodObjEntity = >>>> entityResolver.getObjEntity(PaymentMethod.class.getSimpleName()); >>>> paymentMethodObjEntity.setDeclaredQualifier(null); >>>> >>>> ObjectContext unrestrictedObjectContext = >>>> copiedRuntime.getContext(); >>>> PaymentHistory unrestrictedPaymentHistory = >>>> unrestrictedObjectContext.localObject(paymentHistory); >>>> paymentMethod = unrestrictedPaymentHistory.getPaymentMethod(); >>>> } >>>> >>>> >>>> On Fri, Sep 27, 2013 at 11:46 AM, Mike Kienenberger <[email protected]> >>>> wrote: >>>>> The idea of creating a new ServerRuntime is good, but the >>>>> implementation leaves me with a few questions. My ServerRuntime is >>>>> created in the context of the web app. >>>>> >>>>> Is it reasonable to try to create it using the information in the >>>>> existing ServerRuntime? Can I pull the injector and modules out of >>>>> the existing Runtime and reuse them to create a new runtime, or do I >>>>> need to create copies of the existing modules and injector? My guess >>>>> is that each runtime needs unique injector and modules objects, but I >>>>> want to minimize the work I need to do. >>>>> >>>>> It looks like I have to pull my configurationLocation out of the >>>>> ServerModule, which doesn't really provide it. >>>>> >>>>> I'm not seeing an easy way to create a new ServerRuntime from scratch >>>>> without having access to data which was only available back when the >>>>> web application started up, short of storing that information >>>>> somewhere globally and reusing it. >>>>> >>>>> On Tue, Sep 24, 2013 at 2:04 PM, Andrus Adamchik <[email protected]> >>>>> wrote: >>>>>>> The "special DataContext" case where the qualifier should be ignored >>>>>>> can probably be handled by starting a separate ServerRuntime, where you >>>>>>> can strip off the qualifiers. For whatever overhead it creates (ideally >>>>>>> not much), this has an advantage of cleanly separating "spaces" with >>>>>>> different ORM rules. >>>>>> >>>>>> Elaborating on this a bit… The old Configuration allowed multiple >>>>>> DataDomains, each of those requiring its own DataMap(s) saved in the >>>>>> project upfront. Good idea, but hard to use in practice. >>>>>> >>>>>> ServerRuntime (with single DD each) is more user-friendly. By starting >>>>>> multiple independent runtimes you to easily reuse a single mapping >>>>>> project, tweaking each in-memory copy local to each Runtime (as well as >>>>>> tweaking other parameters like cache). 2 Runtimes can reuse a single >>>>>> DataSource (JNDI, or otherwise), etc. >>>>>> >>>>>> Andrus >>>>>> >>>> >>> >> >
