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
>
>