Marc,

  thanks for that detailed update. I think you have come very far in
terms of solving the real problems! After I sent that (slightly
pessimistic) mail yesterday I started digging into the code once more
and now think there are two reasonable possiblities to get tenant config
separated - just for the goal of being able to move tenants without
having to touch a lot of shared config files.

a) Fabricate the entityengine.xml from tenant config that resides in
tenant apps. That is a somewhat local change and just a simple extension
to the template mechanism you have.
b) Change those places where OfBiz keeps config in class variables so
that its wrapped by a WeakHashMap<Classloader,T> so that we can separate
config by the thread's context classloader. Somewhat a hack, but it will
show what you need to maintain in a "tenant context" if you would want
to make OfBiz really multi-tenant capable.

However, what you describe below is extremely useful regardless of a) or
b) (in particular making sure that keys don't collide). 

And I haven't even looked into operating the web apps in a multi-tenant
scenario yet.

I guess I need to experiment a little longer to get settled on a plan.

Thanks,
  Henning


Am Mittwoch, den 08.07.2009, 17:11 -0400 schrieb Marc Morin:

> We do indeed have a single copy of the entityengine.xml file, but we have 
> added "templates" to the file, such that we have an entity in our main 
> "provisioning" instance, that lists all the tenants and the coordinates of 
> the database for their delegator.
> 
> The delegator for the tenants are then instantiated using the template and 
> the information from this provisioning database.
> 
> This means, that in a VM, all instances share the same "structure" of 
> delegator and data sources.  Below is a snippet of our entityengine.xml file 
> that illustrates the template delegator and associated datasource.
> 
> The FlexibleStringExpander is used to expand the template items.   Our 
> delegators have names that are numbers, (ie. party_id of our customer in our 
> deployment instance).   Each tenant has a row in the DatabaseDetail entity 
> which has columns databaseName, internalHost, externalHost.  In our case, the 
> username and password are generated (not stored in the database).
> 
> The internalHost is the internal hostname for the database server, while the 
> externalHost is the external hostname for the database server.  Since we 
> deploy on Amazon EC2, each host has both a local and public IP address. 
> 
> If your paying attention to the information below, you can see that the 
> sequence numbers for each tenant are prefixed with the party_id and a "-".  
> Making all surrogate keys non-colliding (an important feature for sharing 
> database between tenants).
> 
> 
>     <delegator name="default" entity-model-reader="main" 
> entity-group-reader="main" entity-eca-reader="main" 
> distributed-cache-clear-enabled="false">
>         <group-map group-name="org.ofbiz" datasource-name="localpostgres"/>
>         <group-map group-name="org.ofbiz.olap" 
> datasource-name="localpostgres"/>
>         <group-map group-name="com.emforium.deploy" 
> datasource-name="emforium"/>
>     </delegator>
>    <delegator name="${instance}"  crypto-minimum-keys="0" 
> entity-model-reader="main" entity-group-reader="main" 
> entity-eca-reader="main" distributed-cache-clear-enabled="false" 
> sequenced-id-prefix="${instance}-">
>         <group-map group-name="org.ofbiz" datasource-name="${instance}"/>
>         <group-map group-name="org.ofbiz.olap" datasource-name="${instance}"/>
>         <group-map group-name="com.emforium.deploy" 
> datasource-name="${instance}"/>
>      </delegator>
> 
>     <datasource name="${instance}"
>             helper-class="org.ofbiz.entity.datasource.GenericHelperDAO"
>             schema-name="public"
>             field-type-name="postgres"
>             check-on-start="true"
>             add-missing-on-start="true"
>             generate-sql="false"
>             use-fk-initially-deferred="false"
>             alias-view-columns="false"
>             join-style="ansi"
>             use-binary-type-for-blob="true">
>             
>         <after-create-table>GRANT SELECT,INSERT,UPDATE,DELETE ON TABLE 
> ${table} TO runtime</after-create-table>
>         <after-create-table>GRANT SELECT ON TABLE ${table} TO 
> readonly</after-create-table>
>         <after-create-table>REVOKE ALL ON TABLE ${table} FROM 
> public</after-create-table>
>         <after-create-table>ALTER TABLE ${table} OWNER TO 
> admin</after-create-table>
>         <after-create-view>GRANT SELECT ON TABLE ${table} TO 
> runtime</after-create-view>
>         <after-create-view>GRANT SELECT ON TABLE ${table} TO 
> readonly</after-create-view>
>         <after-create-view>REVOKE ALL ON TABLE ${table} FROM 
> public</after-create-view>
>         <after-create-view>ALTER TABLE ${table} OWNER TO 
> admin</after-create-view>
> 
>         <inline-jdbc
>                 jdbc-driver="org.postgresql.Driver"
>                 jdbc-uri="jdbc:postgresql://${internalHost}/${databaseName}"
>                 jdbc-username="${username}"
>                 jdbc-password="${password}"
>                 isolation-level="ReadCommitted"
>                 pool-minsize="2"
>                 pool-maxsize="250"/>
>     </datasource>
> 
> 
> ----- Original Message -----
> From: "Henning" <[email protected]>
> To: [email protected]
> Sent: Wednesday, July 8, 2009 6:25:15 AM GMT -05:00 US/Canada Eastern
> Subject: Re: Using OfBiz modular, partially, and in many instances
> 
> After spending some more time on stepping through the OfBiz code and
> trying to get the entity engine running outside of the OfBiz framework I
> found that while you can run the entity engine like that, but it still
> requires a single entityengine.xml from the classpath and will maintain
> that in some static cache. I think I saw the same pattern applied for
> other configurations. 
> 
> In my understanding that means that one instance of the entity engine
> code can be used for exactly one configuration per VM and in particular,
> for the multi-tenant use-case, all data sources for all tenants have to
> be pre-defined in that one configuration file. If I understood Marc
> correctly, that's also what he does.
> 
> So the entity engine can be configured exactly once and it will not
> forget until the VM stops.
> 
> However, it seems that all you need to process a request for a tenant is
> its delegator and subsequently a service dispatcher. Both, it seems,
> could be created based on some tenant specific configuration and still
> work correctly, if it wasn't for the static cache. 
> 
> Also, it seems that the ResourceLoader supports separation per class
> loader, so that you could have different entityengine.xml files in
> different deployable (assuming these use different classloaders at
> runtime) and the one to be the first one to ask for a delegator would
> actually provide the entity engine config. Unfortunately any other code
> would then share that configuration. Plus... GenericDelegator,
> EntityConfigUtil etc. would not know about different classloaders still.
> 
> Currently I have the impression that trying to change all those places
> could be an endless endeavour and I better start looking into a solution
> that - at runtime - has duplicate deployments of OfBiz.
> 
> Any thoughts?
> 
> Thanks,
>   Henning
> 
> 

Reply via email to