apache-cayenne

Apache Cayenne: Merging fixed with dynamically supplied DataMaps into one ServerRuntime


My Application design contains fixed and dynamically DataMaps to connect to different (Oracle) DB instances/users. For simplicity i did split them into two different Cayenne Projects.

  1. Create ServerRuntimeBuilder with fixed DataMap, build and query my customers table.
  2. For every customer, load the dynamic DataMap with a second ServerRuntimeBuilder with the necessary modifications (change name and defaultschema):

    ...
    List<Customer> allCustomers = Customer.allCustomers(this.cayenneRuntime1.newContext());
    for (Customer customer : allCustomers) {
        final String dbPrefix = customer.getDbprefix();
    
        ServerRuntimeBuilder builder = ServerRuntime.builder();
        // load and modify DataMap
        builder.addConfig("ApacheCayenne/Data/cayenne-dynamicDomain.xml");
        builder.addModule(binder -> {
            binder.bind(DataMapLoader.class).to(MyDataMapLoader.class);
            ServerModule.contributeProperties(binder).put("CUSTOMER_PREFIX", dbPrefix);
        });
        ServerRuntime cayenneRuntime2 = builder.build();
        LOG.info("Initialized customer model " + customer + " prefix: " + customer.getDbprefix());
        // TODO Merge with cayenneRuntime1?
    }
    
    
    public class MyDataMapLoader extends XMLDataMapLoader {  
        @Inject
        private RuntimeProperties properties; 
        @Override
        public DataMap load(Resource configurationResource) throws CayenneRuntimeException {
            DataMap map = super.load(configurationResource);
            // Dynamically set name and default schema to map
            String dbPrefix = properties.get("CUSTOMER_PREFIX");
            map.setName(customer1.dbPrefix + "...")
            map.setDefaultSchema(customer1.dbPrefix + "...")
            return map;
        }
    }
    

And additionally an own ConfiguredDataSource implementation, that takes care about the JDBC connection URL and credentials. That part works fine so far.

My Problem: with this design i end up having 1 + n ServerRuntimes for every customer. But in the end i would want to have a singe ServerRuntime.

a) Is it possible merge these ServerRuntimes on the fly with Cayenne 4.0RC1 functions and future releases?

b) Do i need to read the dynamic maps with the help of cayenne-project library as described here. Modify and store every dynamic Project to the filesystem. After processing all my customers, create a final ServerRuntime with all the Cayenne Projects using the described way with an ExecutingMergerContext or with this Merging Multiple Projects?

ServerRuntime runtime = ServerRuntime.builder()
    .addConfig("com/example/cayenne-fixed-project.xml")
    .addConfig("org/foo/cayenne-dynamic-Customer1.xml")
    .addConfig("org/foo/cayenne-dynamic-Customer2.xml")
    .build();

Thanks in advance for helping me out.


Solution

  • I'd start runtime1 to get the config data, and then create runtime2 that contains both fixed and dynamic maps, and also a custom DataMap loader to dynamically rewrite name and schema:

    ServerRuntime runtime2 = ServerRuntime.builder()
      .addConfig("com/example/cayenne-fixed-project.xml")
      .addConfig("org/foo/cayenne-dynamic-Customer1.xml")
      .addConfig("org/foo/cayenne-dynamic-Customer2.xml")
      .addModule(..)
      .build();
    

    Just don't forget to shut down runtime1 when you are done with it, so you'll only have a single runtime at the end.