Repository: cayenne Updated Branches: refs/heads/STABLE-4.0 27923edf8 -> e4eb10f03
Update documentation - match latest API - remove empty ROP sections - remove mentions of deprecated code - add cross-links and styling (cherry picked from commit 4517505) Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/e4eb10f0 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/e4eb10f0 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/e4eb10f0 Branch: refs/heads/STABLE-4.0 Commit: e4eb10f03589a5dc3c718ef487a57843efb9c9af Parents: 27923ed Author: Nikita Timofeev <stari...@gmail.com> Authored: Tue Jan 30 15:51:01 2018 +0300 Committer: Nikita Timofeev <stari...@gmail.com> Committed: Tue Jan 30 16:03:26 2018 +0300 ---------------------------------------------------------------------- .../_cayenne-guide/part2/customize.adoc | 77 ++++++++---- .../_cayenne-guide/part2/lifecycle.adoc | 30 +++-- .../_cayenne-guide/part2/objectContext.adoc | 123 ++++++++++++++----- .../asciidoc/_cayenne-guide/part2/tuning.adoc | 64 ++-------- .../src/docs/asciidoc/_cayenne-guide/part3.adoc | 13 +- .../_cayenne-guide/part3/ropDeployment.adoc | 18 ++- .../_cayenne-guide/part5/cacheInvalidation.adoc | 2 +- 7 files changed, 188 insertions(+), 139 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/customize.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/customize.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/customize.adoc index aa78d0d..fffa96c 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/customize.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/customize.adoc @@ -153,9 +153,14 @@ Another types of confiuguration that can be bound in the container are lists and ===== Service Lifecycle -An important feature of the Cayenne DI container is instance scope. The default scope (implicitly used in all examples above) is "singleton", meaning that a binding would result in creation of only one service instance, that will be repeatedly returned from `Injector.getInstance(..)`, as well as injected into classes that declare it as a dependency. +An important feature of the Cayenne DI container is instance scope. The default scope (implicitly used in all examples above) +is "singleton", meaning that a binding would result in creation of only one service instance, that will be repeatedly returned from `Injector.getInstance(..)`, +as well as injected into classes that declare it as a dependency. -Singleton scope dispatches a "BeforeScopeEnd" event to interested services. This event occurs before the scope is shutdown, i.e. when `Injector.shutdown()` is called. Note that the built-in Cayenne injector is shutdown behind the scenes when `ServerRuntime.shutdown()` is invoked. Services may register as listeners for this event by annotating a no-argument method with `@BeforeScopeEnd` annotation. Such method should be implemented if a service needs to clean up some resources, stop threads, etc. +Singleton scope dispatches a "BeforeScopeEnd" event to interested services. This event occurs before the scope is shutdown, +i.e. when `Injector.shutdown()` is called. Note that the built-in Cayenne injector is shutdown behind the scenes when `ServerRuntime.shutdown()` is invoked. +Services may register as listeners for this event by annotating a no-argument method with `@BeforeScopeEnd` annotation. +Such method should be implemented if a service needs to clean up some resources, stop threads, etc. Another useful scope is "no scope", meaning that every time a container is asked to provide a service instance for a given key, a new instance will be created and returned: @@ -164,15 +169,26 @@ Another useful scope is "no scope", meaning that every time a container is asked binder.bind(Service2.class).to(Service2Impl.class).withoutScope(); ---- -Users can also create their own scopes, e.g. a web application request scope or a session scope. Most often than not custom scopes can be created as instances of `org.apache.cayenne.di.spi.DefaultScope` with startup and shutdown managed by the application (e.g. singleton scope is a DefaultScope managed by the Injector) . +Users can also create their own scopes, e.g. a web application request scope or a session scope. Most often than not custom scopes +can be created as instances of `org.apache.cayenne.di.spi.DefaultScope` with startup and shutdown managed by the application +(e.g. singleton scope is a DefaultScope managed by the Injector) . ===== Overriding Services -Cayenne DI allows to override services already definied in the current module, or more commonly - some other module in the the same container. Actually there's no special API to override a service, you'd just bind the service key again with a new implementation or provider. The last binding for a key takes precedence. This means that the order of modules is important when configuring a container. The built-in Cayenne injector ensures that Cayenne standard modules are loaded first, followed by optional user extension modules. This way the application can override the standard services in Cayenne. +Cayenne DI allows to override services already definied in the current module, or more commonly - some other module +in the the same container. Actually there's no special API to override a service, you'd just bind the service key again +with a new implementation or provider. The last binding for a key takes precedence. This means that the order of modules is important when configuring a container. +The built-in Cayenne injector ensures that Cayenne standard modules are loaded first, followed by optional user extension modules. +This way the application can override the standard services in Cayenne. ==== Customization Strategies -The previous section discussed how Cayenne DI works in general terms. Since Cayenne users will mostly be dealing with an existing Injector provided by ServerRuntime, it is important to understand how to build custom extensions to a preconfigured container. As shown in "Starting and Stopping ServerRuntime" chapter, custom extensions are done by writing an aplication DI module (or multiple modules) that configures service overrides. This section shows all the configuration possibilities in detail, including changing properties of the existing services, contributing services to standard service lists and maps, and overriding service implementations. All the code examples later in this section are assumed to be placed in an application module "configure" method: +The previous section discussed how Cayenne DI works in general terms. Since Cayenne users will mostly be dealing with +an existing Injector provided by ServerRuntime, it is important to understand how to build custom extensions to a preconfigured container. +As shown in "Starting and Stopping ServerRuntime" chapter, custom extensions are done by writing an application +DI module (or multiple modules) that configures service overrides. This section shows all the configuration possibilities in detail, +including changing properties of the existing services, contributing services to standard service lists and maps, and overriding service implementations. +All the code examples later in this section are assumed to be placed in an application module "configure" method: [source, Java] ---- @@ -194,7 +210,9 @@ ServerRuntime runtime = ServerRuntime.builder() ===== Changing Properties of Existing Services -Many built-in Cayenne services change their behavior based on a value of some environment property. A user may change Cayenne behavior without even knowing which services are responsible for it, but setting a specific value of a known property. Supported property names are listed in "Appendix A". +Many built-in Cayenne services change their behavior based on a value of some environment property. +A user may change Cayenne behavior without even knowing which services are responsible for it, but setting a specific value of a known property. +Supported property names are listed in "Appendix A". There are two ways to set service properties. The most obvious one is to pass it to the JVM with -D flag on startup. E.g. @@ -202,15 +220,21 @@ There are two ways to set service properties. The most obvious one is to pass it $ java -Dcayenne.server.contexts_sync_strategy=false ... ---- -A second one is to contribute a property to `org.apache.cayenne.configuration.DefaultRuntimeProperties.properties` map (see the next section on how to do that). This map contains the default property values and can accept application-specific values, overrding the defaults. +A second one is to contribute a property to `o.a.c.configuration.DefaultRuntimeProperties.properties` map (see the next section on how to do that). +This map contains the default property values and can accept application-specific values, overrding the defaults. -Note that if a property value is a name of a Java class, when this Java class is instantiated by Cayenne, the container performs injection of instance variables. So even the dynamically specified Java classes can use @Inject annotation to get a hold of other Cayenne services. +Note that if a property value is a name of a Java class, when this Java class is instantiated by Cayenne, +the container performs injection of instance variables. So even the dynamically specified Java classes can use @Inject annotation to get a hold of other Cayenne services. -If the same property is specified both in the command line and in the properties map, the command-line value takes precedence. The map value will be ignored. This way Cayenne runtime can be reconfigured during deployment. +If the same property is specified both in the command line and in the properties map, the command-line value takes precedence. +The map value will be ignored. This way Cayenne runtime can be reconfigured during deployment. ===== Contributing to Service Collections -Cayenne can be extended by adding custom objects to named maps or lists bound in DI. We are calling these lists/maps "service collections". A service collection allows things like appending a custom strategy to a list of built-in strategies. E.g. an application that needs to install a custom DbAdapter for some database type may contribute an instance of custom DbAdapterDetector to a `org.apache.cayenne.configuration.server.DefaultDbAdapterFactory.detectors` list: +Cayenne can be extended by adding custom objects to named maps or lists bound in DI. We are calling these lists/maps "service collections". +A service collection allows things like appending a custom strategy to a list of built-in strategies. +E.g. an application that needs to install a custom DbAdapter for some database type may contribute an instance of custom +DbAdapterDetector to a `o.a.c.configuration.server.DefaultDbAdapterFactory.detectors` list: [source, Java] ---- @@ -224,25 +248,29 @@ public class MyDbAdapterDetector implements DbAdapterDetector { [source, Java] ---- -// since build-in list for this key is a singleton, repeated -// calls to 'bindList' will return the same instance -binder.bindList(DefaultDbAdapterFactory.DETECTORS_LIST) - .add(MyDbAdapterDetector.class); +ServerModule.contributeAdapterDetectors(binder) + .add(MyDbAdapterDetector.class); ---- -Maps are customized using a similar `"bindMap"` method. - The names of built-in collections are listed in "Appendix B". ===== Alternative Service Implementations -As mentioned above, custom modules are loaded by ServerRuntime after the built-in modules. So it is easy to redefine a built-in service in Cayenne by rebinding desired implementations or providers. To do that, first we need to know what those services to redefine are. While we describe some of them in the following sections, the best way to get a full list is to check the source code of the Cayenne version you are using and namely look in `org.apache.cayenne.configuration.server.ServerModule` - the main built-in module in Cayenne. +As mentioned above, custom modules are loaded by ServerRuntime after the built-in modules. +So it is easy to redefine a built-in service in Cayenne by rebinding desired implementations or providers. +To do that, first we need to know what those services to redefine are. +While we describe some of them in the following sections, the best way to get a full list +is to check the source code of the Cayenne version you are using and +namely look in `org.apache.cayenne.configuration.server.ServerModule` - the main built-in module in Cayenne. -Now an example of overriding `QueryCache` service. The default implementation of this service is provided by `MapQueryCacheProvider`. But if we want to use `EhCacheQueryCache` (a Cayenne wrapper for the EhCache framework), we can define it like this: +Now an example of overriding `JdbcEventLogger` service. The default implementation of this service +is provided by `Slf4jJdbcEventLogger`. But if we want to use `FormattedSlf4jJdbcEventLogger` +(a logger with basic SQL formatting), we can define it like this: [source, Java] ---- -binder.bind(QueryCache.class).to(EhCacheQueryCache.class); +binder.bind(JdbcEventLogger.class) + .to(FormattedSlf4jJdbcEventLogger.class); ---- ==== Using custom data types @@ -309,11 +337,14 @@ Last step is to register this new type in `ServerRuntime`: ---- ServerRuntime runtime = ServerRuntime.builder() .addConfig("cayenne-project.xml") - .addModule(binder -> ServerModule.contributeValueObjectTypes(binder).add(MoneyValueObjectType.class)) + .addModule(binder -> + ServerModule.contributeValueObjectTypes(binder) + .add(MoneyValueObjectType.class)) .build(); ---- -More examples of implementation you can find in https://github.com/apache/cayenne/tree/master/cayenne-joda[cayenne-joda module]. +More examples of implementation you can find in +https://github.com/apache/cayenne/blob/master/cayenne-server/src/main/java/org/apache/cayenne/access/types/LocalDateValueType.java[cayenne-server]. ===== Extended Types @@ -416,7 +447,9 @@ For Java8 // add DoubleArrayType to list of user types ServerRuntime runtime = ServerRuntime.builder() .addConfig("cayenne-project.xml") - .addModule(binder -> ServerModule.contributeUserTypes(binder).add(new DoubleArrayType())) + .addModule(binder -> + ServerModule.contributeUserTypes(binder) + .add(new DoubleArrayType())) .build(); ---- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/lifecycle.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/lifecycle.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/lifecycle.adoc index 9305723..4457ef3 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/lifecycle.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/lifecycle.adoc @@ -130,10 +130,15 @@ Ignore the annotations for a minute. The important point here is that the listen [source, Java] ---- -ServerRuntime runtime = ... - -runtime.getDataDomain().addListener(new Listener1()); -runtime.getDataDomain().addListener(new Listener2()); +ServerRuntime runtime = ServerRuntime.builder() + // .. + .addModule(binder -> + ServerModule.contributeDomainListeners() + .add(new Listener1()) + .add(new Listener2()) + ) + // .. + .build(); ---- @@ -253,14 +258,15 @@ Now since this is both a filter and a listener, it needs to be registered as suc [source, Java] ---- -CommittedObjectCounter counter = new CommittedObjectCounter(); - -ServerRuntime runtime = ... -DataDomain domain = runtime.getDataDomain(); - -// register filter -// this will also add it as a listener (since 3.2) -domain.addFilter(counter); +// this will also add filter as a listener +ServerRuntime runtime = ServerRuntime.builder() + // .. + .addModule(b -> + ServerModule.contributeDomainFilters(b) + .add(CommittedObjectCounter.class) + ) + // .. + .build(); ---- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc index 888be47..117c263 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc @@ -66,17 +66,21 @@ Each persistent object belongs to a single ObjectContext, and can be in one of t ==== ObjectContext Persistence API -One of the first things users usually want to do with an ObjectContext is to select some objects from a database. This is done by calling "performQuery" method: +One of the first things users usually want to do with an `ObjectContext` is to select some objects from a database: [source, java] ---- -SelectQuery query = new SelectQuery(Artist.class); -List<Artist> artists = context.performQuery(query); +List<Artist> artists = ObjectSelect.query(Artist.class) + .select(context); ---- -We'll discuss queries in some detail in the following chapters. The example above is self-explanatory - we create a SelectQuery that matches all Artist objects present in the database, and then call "performQuery", getting a list of Artist objects. +We'll discuss queries in some detail in the <<queries,Queries>> chapter. The example above is self-explanatory - +we create a `ObjectSelect` that matches all `Artist` objects present in the database, and then use `select` +to get the result. -Some queries can be quite complex, returning multiple result sets or even updating the database. For such queries ObjectContext provides "performGenericQuery"method. While not nearly as commonly-used as "performQuery", it is nevertheless important in some situations. E.g.: +Some queries can be quite complex, returning multiple result sets or even updating the database. +For such queries ObjectContext provides `performGenericQuery()` method. +While not commonly-used, it is nevertheless important in some situations. E.g.: [source, java] ---- @@ -95,23 +99,27 @@ Artist selectedArtist = artists.get(0); selectedArtist.setName("Dali"); ---- -The first time the object property is changed, the object's state is automatically set to "MODIFIED" by Cayenne. Cayenne tracks all in-memory changes until a user calls "commitChanges": +The first time the object property is changed, the object's state is automatically set to *MODIFIED* by Cayenne. +Cayenne tracks all in-memory changes until a user calls `commitChanges`: [source, java] ---- context.commitChanges(); ---- -At this point all in-memory changes are analyzed and a minimal set of SQL statements is issued in a single transaction to synchronize the database with the in-memory state. In our example "commitChanges" commits just one object, but generally it can be any number of objects. +At this point all in-memory changes are analyzed and a minimal set of SQL statements is issued in a single transaction +to synchronize the database with the in-memory state. In our example `commitChanges` commits just one object, +but generally it can be any number of objects. -If instead of commit, we wanted to reset all changed objects to the previously committed state, we'd call `rollbackChanges` instead: +If instead of commit, we wanted to reset all changed objects to the previously committed state, +we'd call `rollbackChanges` instead: [source, java] ---- context.rollbackChanges(); ---- -"newObject" method call creates a persistent object and sets its state to "NEW": +`newObject` method call creates a persistent object and sets its state to *NEW*: [source, java] ---- @@ -119,9 +127,11 @@ Artist newArtist = context.newObject(Artist.class); newArtist.setName("Picasso"); ---- -It will only exist in memory until "commitChanges" is issued. On commit Cayenne might generate a new primary key (unless a user set it explicitly, or a PK was inferred from a relationship) and issue an INSERT SQL statement to permanently store the object. +It will only exist in memory until `commitChanges` is issued. On commit Cayenne might generate a new primary key +(unless a user set it explicitly, or a PK was inferred from a relationship) and issue an +`INSERT` SQL statement to permanently store the object. -`deleteObjects` method takes one or more Persistent objects and marks them as "DELETED": +`deleteObjects` method takes one or more Persistent objects and marks them as *DELETED*: [source, java] ---- @@ -129,11 +139,16 @@ context.deleteObjects(artist1); context.deleteObjects(artist2, artist3, artist4); ---- -Additionally "deleteObjects" processes all delete rules modeled for the affected objects. This may result in implicitly deleting or modifying extra related objects. Same as insert and update, delete operations are sent to the database only when "commitChanges" is called. Similarly "rollbackChanges" will undo the effect of "newObject" and "deleteObjects". +Additionally `deleteObjects` processes all delete rules modeled for the affected objects. +This may result in implicitly deleting or modifying extra related objects. +Same as insert and update, delete operations are sent to the database only when `commitChanges` is called. +Similarly `rollbackChanges` will undo the effect of `newObject` and `deleteObjects`. -`localObject` returns a copy of a given persistent object that is "local" to a given ObjectContext: +`localObject` returns a copy of a given persistent object that is _local_ to a given ObjectContext: -Since an application often works with more than one context, "localObject" is a rather common operation. E.g. to improve performance a user might utilize a single shared context to select and cache data, and then occasionally transfer some selected objects to another context to modify and commit them: +Since an application often works with more than one context, `localObject` is a rather common operation. +E.g. to improve performance a user might utilize a single shared context to select and cache data, +and then occasionally transfer some selected objects to another context to modify and commit them: [source, java] @@ -142,18 +157,23 @@ ObjectContext editingContext = runtime.newContext(); Artist localArtist = editingContext.localObject(artist); ---- -Often an appliction needs to inspect mapping metadata. This information is stored in the EntityResolver object, accessible via the ObjectContext: +Often an application needs to inspect mapping metadata. This information is stored in the `EntityResolver` object, +accessible via the `ObjectContext`: [source, java] ---- EntityResolver resolver = objectContext.getEntityResolver(); ---- -Here we discussed the most commonly used subset of the ObjectContext API. There are other useful methods, e.g. those allowing to inspect registered objects state in bulk, etc. Check the latest JavaDocs for details. +Here we discussed the most commonly used subset of the ObjectContext API. +There are other useful methods, e.g. those allowing to inspect registered objects state in bulk, etc. +Check the latest JavaDocs for details. ==== Cayenne Helper Class -There is a useful helper class called "Cayenne" (fully-qualified name `"org.apache.cayenne.Cayenne"`) that builds on ObjectContext API to provide a number of very common operations. E.g. get a primary key (most entities do not model PK as an object property) : +There is a useful helper class called `Cayenne` (fully-qualified name `org.apache.cayenne.Cayenne`) +that builds on ObjectContext API to provide a number of very common operations. +E.g. get a primary key (most entities do not model PK as an object property) : [source, java] ---- @@ -177,23 +197,36 @@ Artist artist = (Artist) Cayenne.objectForQuery(context, new SelectQuery(Artist. Feel free to explore Cayenne class API for other useful methods. ==== ObjectContext Nesting -In all the examples shown so far an ObjectContext would directly connect to a database to select data or synchronize its state (either via commit or rollback). However another context can be used in all these scenarios instead of a database. This concept is called ObjectContext "nesting". Nesting is a parent/child relationship between two contexts, where child is a nested context and selects or commits its objects via a parent. +In all the examples shown so far an ObjectContext would directly connect to a database to select data +or synchronize its state (either via commit or rollback). However another context can be used in all these scenarios +instead of a database. This concept is called ObjectContext "nesting". +Nesting is a parent/child relationship between two contexts, where child is a nested context and selects +or commits its objects via a parent. -Nesting is useful to create isolated object editing areas (child contexts) that need to all be committed to an intermediate in-memory store (parent context), or rolled back without affecting changes already recorded in the parent. Think cascading GUI dialogs, or parallel AJAX requests coming to the same session. +Nesting is useful to create isolated object editing areas (child contexts) that need to +all be committed to an intermediate in-memory store (parent context), +or rolled back without affecting changes already recorded in the parent. +Think cascading GUI dialogs, or parallel AJAX requests coming to the same session. -In theory Cayenne supports any number of nesting levels, however applications should generally stay with one or two, as deep hierarchies will most certainly degrade the performance of the deeply nested child contexts. This is due to the fact that each context in a nesting chain has to update its own objects during most operations. +In theory Cayenne supports any number of nesting levels, however applications should generally stay with one or two, +as deep hierarchies will most certainly degrade the performance of the deeply nested child contexts. +This is due to the fact that each context in a nesting chain has to update its own objects during most operations. -Cayenne ROP is an extreme case of nesting when a child context is located in a separate JVM and communicates with its parent via a web service. ROP is discussed in details in the following chapters. Here we concentrate on the same-VM nesting. +Cayenne ROP is an extreme case of nesting when a child context is located in a separate JVM and communicates +with its parent via a web service. ROP is discussed in details in the following chapters. +Here we concentrate on the same-VM nesting. To create a nested context, use an instance of ServerRuntime, passing it the desired parent: [source, java] ---- ObjectContext parent = runtime.newContext(); -ObjectContext nested = runtime.newContext((DataChannel) parent); +ObjectContext nested = runtime.newContext(parent); ---- -From here a nested context operates just like a regular context (you can perform queries, create and delete objects, etc.). The only difference is that commit and rollback operations can either be limited to synchronization with the parent, or cascade all the way to the database: +From here a nested context operates just like a regular context (you can perform queries, +create and delete objects, etc.). The only difference is that commit and rollback operations +can either be limited to synchronization with the parent, or cascade all the way to the database: [source, java] ---- @@ -217,11 +250,16 @@ nested.rollbackChanges(); ==== Generic Persistent Objects -As described in the CayenneModeler chapter, Cayenne supports mapping of completely generic classes to specific entities. Although for conveniece most applications should stick with entity-specific class mappings, the generic feature offers some interesting possibilities, such as creating mappings completely on the fly in a running application, etc. +As described in the CayenneModeler chapter, Cayenne supports mapping of completely generic classes +to specific entities. Although for conveniece most applications should stick with entity-specific class mappings, +the generic feature offers some interesting possibilities, such as creating mappings completely +on the fly in a running application, etc. -Generic objects are first class citizens in Cayenne, and all common persistent operations apply to them as well. There are some pecularities however, described below. +Generic objects are first class citizens in Cayenne, and all common persistent operations apply to them as well. +There are some pecularities however, described below. -When creating a new generic object, either cast your ObjectContext to DataContext (that provides "newObject(String)" API), or provide your object with an explicit ObjectId: +When creating a new generic object, either cast your ObjectContext to DataContext +(that provides `newObject(String)` API), or provide your object with an explicit ObjectId: [source, java] ---- @@ -260,13 +298,29 @@ String entityName = generic.getObjectId().getEntityName(); ==== Transactions -Considering how much attention is given to managing transactions in most other ORMs, transactions have been conspicuously absent from the ObjectContext discussion till now. The reason is that transactions are seamless in Cayenne in all but a few special cases. ObjectContext is an in-memory container of objects that is disconnected from the database, except when it needs to run an operation. So it does not care about any surrounding transaction scope. Sure enough all database operations are transactional, so when an application does a commit, all SQL execution is wrapped in a database transaction. But this is done behind the scenes and is rarely a concern to the application code. - -Two cases where transactions need to be taken into consideration are container-managed and application-managed transactions. - -If you are using an EJB container (or some other JTA environment), you'll likely need to switch Cayenne runtime into "external transactions mode". This is done by setting DI configuration property defined in `Constants.SERVER_EXTERNAL_TX_PROPERTY` (see Appendix A). If this property is set to "true", Cayenne assumes that JDBC Connections obtained by runtime whenever that might happen are all coming from a transactional DataSource managed by the container. In this case Cayenne does not attempt to commit or rollback the connections, leaving it up to the container to do that when appropriate. - -In the second scenario, an application might need to define its own transaction scope that spans more than one Cayenne operation. E.g. two sequential commits that need to be rolled back together in case of failure. This can be done via `ServerRuntime.performInTransaction` method: +Considering how much attention is given to managing transactions in most other ORMs, +transactions have been conspicuously absent from the ObjectContext discussion till now. +The reason is that transactions are seamless in Cayenne in all but a few special cases. +ObjectContext is an in-memory container of objects that is disconnected from the database, +except when it needs to run an operation. So it does not care about any surrounding transaction scope. +Sure enough all database operations are transactional, so when an application does a commit, +all SQL execution is wrapped in a database transaction. But this is done behind the scenes +and is rarely a concern to the application code. + +Two cases where transactions need to be taken into consideration are container-managed and +application-managed transactions. + +If you are using an EJB container (or some other JTA environment), +you'll likely need to switch Cayenne runtime into "external transactions mode". +This is done by setting DI configuration property defined in `Constants.SERVER_EXTERNAL_TX_PROPERTY` (see Appendix A). +If this property is set to "true", Cayenne assumes that JDBC Connections obtained by runtime +whenever that might happen are all coming from a transactional DataSource managed by the container. +In this case Cayenne does not attempt to commit or rollback the connections, leaving it up to the container +to do that when appropriate. + +In the second scenario, an application might need to define its own transaction scope that spans more +than one Cayenne operation. E.g. two sequential commits that need to be rolled back together in case of failure. +This can be done via `ServerRuntime.performInTransaction` method: [source, java] ---- @@ -284,7 +338,8 @@ Integer result = runtime.performInTransaction(() -> { }); ---- -When inside the transaction, current thread Transaction object can be accessed via a static method. E.g. here is an example that initializes transaction JDBC connection with a custom connection object : +When inside the transaction, current thread Transaction object can be accessed via a static method. +E.g. here is an example that initializes transaction JDBC connection with a custom connection object : [source, java] ---- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/tuning.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/tuning.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/tuning.adoc index fb5bb3f..ea0fea2 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/tuning.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/tuning.adoc @@ -290,39 +290,9 @@ Those are arbitrary names that allow to categorize queries for the purpose of se The above API is enough for the caching to work, but by default your cache is an unmanaged LRU map. You can't control its size, expiration policies, etc. For the managed cache, you will need to explicitly use one of the more advanced cache providers. -One such provider available in Cayenne is a provider for http://www.ehcache.org[EhCache]. -It can be enabled on ServerRuntime startup in a custom Module: +Use can use <<ext-cayenne-jcache,JCache integration module>> to enable any of JCache API compatible caching providers. -[source, Java] ----- -ServerRuntimeBuilder - .builder() - .addModule((binder) -> - binder.bind(QueryCache.class).to(EhCacheQueryCache.class) - ) - .build(); ----- - -By default EhCache reads a file called "ehcache.xml" located on classpath. You can put your cache configuration in that file. E.g.: - -[source, XML] ----- -<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" - monitoring="off" dynamicConfig="false"> - - <defaultCache maxEntriesLocalHeap="1000" eternal="false" - overflowToDisk="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600"> - </defaultCache> - - <cache name="artists" timeToLiveSeconds="20" maxEntriesLocalHeap="100" /> -</ehcache> ----- - -The example shows how to configure default cache settings ("defaultCache") as well as settings for a named cache group ("artists"). -For many other things you can put in "ehcache.xml" refer to EhCache docs. - -Often "passive" cache expiration policies similar to shown above are not sufficient, and the users want real-time cache invalidation when the data changes. +Often "passive" cache expiration policies used by caching providers are not sufficient, and the users want real-time cache invalidation when the data changes. So in addition to those policies, the app can invalidate individual cache groups explicitly with `RefreshQuery`: [source, Java] @@ -331,30 +301,9 @@ RefreshQuery refresh = new RefreshQuery("artist"); context.performGenericQuery(refresh); ---- -The above can be used e.g. to build UI for manual cache invalidation. It is also possible to automate cache refresh when certain entities are committed. -This requires including `cayenne-lifecycle.jar` deoendency. From that library you will need two things: `@CacheGroups` annotation -to mark entities that generate cache invalidation events and `CacheInvalidationFilter` that catches the updates to the annotated objects and generates appropriate invalidation events: - -[source, Java] ----- -// configure filter on startup -ServerRuntimeBuilder - .builder() - .addModule((binder) -> - binder.bindList(Constants.SERVER_DOMAIN_FILTERS_LIST).add(CacheInvalidationFilter.class) - ) - .build(); ----- - - -Now you can associate entities with cache groups, so that commits to those entities would atomatically invalidate the groups: - -[source, Java] ----- -@CacheGroups("artists") -public class Artist extends _Artist { -} ----- +The above can be used e.g. to build UI for manual cache invalidation. +It is also possible to automate cache refresh when certain entities are committed. +This can be done with the help of <<ext-cache-invalidation,Cache invalidation extension>>. Finally you may cluster cache group events. They are very small and can be efficiently sent over the wire to other JVMs running Cayenne. An example of Cayenne setup with event clustering is https://github.com/andrus/wowodc13/tree/master/services/src/main/java/demo/services/cayenne[available on GitHub]. @@ -395,7 +344,8 @@ public class MyModule implements Module { @Override public void configure(Binder binder) { - binder.bindMap(Constants.PROPERTIES_MAP).put(Constants.SERVER_CONTEXTS_SYNC_PROPERTY, "false"); + ServerModule.contributeProperties(binder) + .put(Constants.SERVER_CONTEXTS_SYNC_PROPERTY, "false"); } } ---- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3.adoc index 5e211d1..0bf6e6e 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3.adoc @@ -15,14 +15,11 @@ == Cayenne Framework - Remote Object Persistence include::part3/rop.adoc[] - -include::part3/ropSetup.adoc[] - -include::part3/serverImpl.adoc[] - -include::part3/clientImpl.adoc[] - include::part3/ropDeployment.adoc[] -include::part3/limitations.adoc[] +// TODO: add content to these files: +//include::part3/ropSetup.adoc[] +//include::part3/serverImpl.adoc[] +//include::part3/clientImpl.adoc[] +//include::part3/limitations.adoc[] http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3/ropDeployment.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3/ropDeployment.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3/ropDeployment.adoc index 6aa4388..230aef8 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3/ropDeployment.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part3/ropDeployment.adoc @@ -14,9 +14,17 @@ === ROP Deployment -==== Deploying ROP Server +==== Server Security Note -NOTE: Recent versions of Tomcat and Jetty containers (e.g. Tomcat 6 and 7, Jetty 8) contain code addressing a security concern related to "session fixation problem" by resetting the existing session ID of any request that requires BASIC authentcaition. If ROP service is protected with declarative security (see the ROP tutorial and the following chapters on security), this feature prevents the ROP client from attaching to its session, resulting in MissingSessionExceptions. To solve that you will need to either switch to an alternative security mechanism, or disable "session fixation problem" protections of the container. E.g. the later can be achieved in Tomcat 7 by adding the following context.xml file to the webapp's META-INF/ directory: +Recent versions of Tomcat and Jetty containers (e.g. Tomcat 6 and 7, Jetty 8) contain code +addressing a security concern related to "session fixation problem" by resetting the existing session ID of any request +that requires BASIC authentication. If ROP service is protected with declarative security +(see the ROP tutorial and the following chapters on security), this feature prevents the ROP client +from attaching to its session, resulting in `MissingSessionExceptions`. + +To solve that you will need to either switch to an alternative security mechanism, +or disable "session fixation problem" protections of the container. +E.g. the later can be achieved in Tomcat 7 by adding the following `context.xml` file to the webapp's `META-INF/` directory: [source, XML] ---- @@ -26,9 +34,9 @@ NOTE: Recent versions of Tomcat and Jetty containers (e.g. Tomcat 6 and 7, Jetty </Context> ---- -(The <Valve> tag can also be placed within the <Context> in any other locations used by Tomcat to load context configurations) +(The `<Valve>` tag can also be placed within the `<Context>` in any other locations used by Tomcat to load context configurations) -==== Deploying ROP Client +//==== Deploying ROP Client -==== Security +//==== Security http://git-wip-us.apache.org/repos/asf/cayenne/blob/e4eb10f0/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part5/cacheInvalidation.adoc ---------------------------------------------------------------------- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part5/cacheInvalidation.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part5/cacheInvalidation.adoc index 246162f..43a69c5 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part5/cacheInvalidation.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part5/cacheInvalidation.adoc @@ -79,7 +79,7 @@ Now we'll set up it's usage by `ServerRuntime`: ---- ServerRuntime.builder() .addModule(CacheInvalidationModule.extend() - // this will disable default handler based on @CacheGroups, and this is optional + // optionally you can disable @CacheGroups annotation processing .noCacheGroupsHandler() .addHandler(CustomInvalidationHandler.class) .module())