You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2014/12/22 20:30:20 UTC
svn commit: r1647386 - in /isis/site/trunk/content:
components/objectstores/jdo/using-neo4j.md documentation.md
Author: danhaywood
Date: Mon Dec 22 19:30:19 2014
New Revision: 1647386
URL: http://svn.apache.org/r1647386
Log:
adding page on using neo4j
Added:
isis/site/trunk/content/components/objectstores/jdo/using-neo4j.md
- copied, changed from r1630348, isis/site/trunk/content/components/objectstores/jdo/deploying-on-the-google-app-engine.md
Modified:
isis/site/trunk/content/documentation.md
Copied: isis/site/trunk/content/components/objectstores/jdo/using-neo4j.md (from r1630348, isis/site/trunk/content/components/objectstores/jdo/deploying-on-the-google-app-engine.md)
URL: http://svn.apache.org/viewvc/isis/site/trunk/content/components/objectstores/jdo/using-neo4j.md?p2=isis/site/trunk/content/components/objectstores/jdo/using-neo4j.md&p1=isis/site/trunk/content/components/objectstores/jdo/deploying-on-the-google-app-engine.md&r1=1630348&r2=1647386&rev=1647386&view=diff
==============================================================================
--- isis/site/trunk/content/components/objectstores/jdo/deploying-on-the-google-app-engine.md (original)
+++ isis/site/trunk/content/components/objectstores/jdo/using-neo4j.md Mon Dec 22 19:30:19 2014
@@ -1,475 +1,49 @@
-Title: Deploying on the Google App Engine
+Title: Using Neo4J (1.8.0-SNAPSHOT).
-The Google App Engine (GAE) provides a JDO API, meaning that you can deploy Isis onto GAE using the JDO objectstore.
+As of 1.8.0-SNAPSHOT Apache Isis has experimental support for Neo4J, courtesy of DataNucleus' [Neo4J Datastore](http://www.datanucleus.org/products/datanucleus/datastores/neo4j.html) implementation.
-However, GAE is not an RDBMS, and so there are some limitations that it imposes. This page gathers together various hints, tips and workarounds.
+The [simpleapp](../../../intro/getting-started/simpleapp-archetype.html) and [todoapp](../../../intro/getting-started/simpleapp-archetype.html) archetypes have been updated so that they can be optionally run under Neo4J.
-### Primary Keys and Owned/Unowned Relationships
+The steps below describe the configuration steps required to update an existing app.
-All entities must have a `@PrimaryKey`. Within GAE, the type of this key matters.
-
-For an entity to be an aggregate root, (ie a root of an GAE entity group), its key must be a `Long`, eg:
-
-<pre>
- @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
- @PrimaryKey
- private Long id;
-</pre>
-
-Any collection that holds this entity type (eg `ToDoItem#dependencies` holding a collection of `ToDoItem`s) should then be annotated with `@Unowned` (a GAE annotation).
-
-If on the other hand you want the object to be owned (through a 1:m relationship somewhere) by some other root, then use a String:
-
-<pre>
-@PrimaryKey
-@Persistent(valueStrategy = javax.jdo.annotations.IdGeneratorStrategy.IDENTITY)
-@Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
-private String key;
-</pre>
-
-Note: if you store a relationship with a String key it means that the parent object *owns* the child, any attempt to change the relationship raise and exception.
-
-### Custom Types
-
-Currently Isis' `Blob` and `Clob` types and the JODA types (`LocalDate` et al) are *not* supported in GAE.
-
-Instead, GAE defines a [fixed set of value types](https://developers.google.com/appengine/docs/java/datastore/entities#Properties_and_Value_Types), including `BlobKey`. Members of the Isis community have this working, though I haven't seen the code.
-
-The above notwithstanding, Andy Jefferson at DataNucleus tells us:
-
-> GAE JDO/JPA does support *some* type conversion, because looking at [StoreFieldManager.java](http://code.google.com/p/datanucleus-appengine/source/browse/trunk/src/com/google/appengine/datanucleus/StoreFieldManager.java#349) for any field that is Object-based and not a relation nor Serialized it will call [TypeConverstionUtils.java](http://code.google.com/p/datanucleus-appengine/source/browse/trunk/src/com/google/appengine/datanucleus/TypeConversionUtils.java#736) and that looks for a `TypeConverter` (specify `@Extension` with key of "type-converter-name" against a field and value as the `TypeConverter` class) and it should convert it. Similarly when getting the value from the datastore.
-
-On further investigation, it seems that the GAE implementation performs a type check on a `SUPPORTED_TYPES` Java set, in `com.google.appengine.api.datastore.DataTypeUtils`:
-
-
- 234 if (!supportedTypes.contains(value.getClass())) {
- 235 throw new IllegalArgumentException(prefix + value.getClass().getName() + " is not a supported property type.");
- 236 }
-
-We still need to try out Andy's recipe, above.
-
-The GAE `BlobKey` issue has been solved; we hope to publish further details (possibly in a blog series).
-
-
-----
-###GAE compatible version of `ToDoItem.java`
-
-Is as follows:
-
- @javax.jdo.annotations.Queries( {
- @javax.jdo.annotations.Query(
- name="todo_all", language="JDOQL",
- value="SELECT FROM dom.todo.ToDoItem WHERE ownedBy == :ownedBy"),
- @javax.jdo.annotations.Query(
- name="todo_notYetComplete", language="JDOQL",
- value="SELECT FROM dom.todo.ToDoItem WHERE ownedBy == :ownedBy && complete == false"),
- @javax.jdo.annotations.Query(
- name="todo_complete", language="JDOQL",
- value="SELECT FROM dom.todo.ToDoItem WHERE ownedBy == :ownedBy && complete == true"),
- @javax.jdo.annotations.Query(
- name="todo_similarTo", language="JDOQL",
- value="SELECT FROM dom.todo.ToDoItem WHERE ownedBy == :ownedBy && category == :category"),
- @javax.jdo.annotations.Query(
- name="todo_autoComplete", language="JDOQL",
- value="SELECT FROM dom.todo.ToDoItem WHERE ownedBy == :ownedBy && description.startsWith(:description)")
- })
- @javax.jdo.annotations.Version(strategy= VersionStrategy.VERSION_NUMBER, column="VERSION")
- @ObjectType("TODO")
- @Auditable
- @AutoComplete(repository=ToDoItems.class, action="autoComplete")
- @MemberGroups({"General", "Detail"})
- @javax.jdo.annotations.PersistenceCapable
- public class ToDoItem implements Comparable<ToDoItem> {
- // @PrimaryKey
- // @Persistent(valueStrategy = javax.jdo.annotations.IdGeneratorStrategy.IDENTITY)
- // @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
- // private String key;
- @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
- @PrimaryKey
- private Long id;
-
- private static final long ONE_WEEK_IN_MILLIS = 7 * 24 * 60 * 60 * 1000L;
-
- public static enum Category {
- Professional, Domestic, Other;
- }
-
- // {{ Identification on the UI
- public String title() {
- final TitleBuffer buf = new TitleBuffer();
- buf.append(getDescription());
- if (isComplete()) {
- buf.append(" - Completed!");
- } else {
- if (getDueBy() != null) {
- SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
- buf.append(" due by ", sdf.format(getDueBy()));
- }
- }
- return buf.toString();
- }
-
- // }}
-
- // {{ Description
- private String description;
-
- @RegEx(validation = "\\w[@&:\\-\\,\\.\\+ \\w]*")
- // words, spaces and selected punctuation
- @MemberOrder(sequence = "1")
- @DescribedAs("Enter the description")
- public String getDescription() {
- return description;
- }
-
- public void setDescription(final String description) {
- this.description = description;
- }
- // }}
-
- // {{ DueBy (property)
- private Date dueBy;
-
- @javax.jdo.annotations.Persistent
- @MemberOrder(name="Detail", sequence = "3")
- @Optional
- @DescribedAs("Enter the date")
- public Date getDueBy() {
- return dueBy;
- }
-
- public void setDueBy(final Date dueBy) {
- this.dueBy = dueBy;
- }
- public void clearDueBy() {
- setDueBy(null);
- }
- // proposed new value is validated before setting
- public String validateDueBy(final Date dueBy) {
- if (dueBy == null) {
- return null;
- }
- return isMoreThanOneWeekInPast(dueBy) ? "Due by date cannot be more than one week old" : null;
- }
- // }}
-
- // {{ Category
- private Category category;
-
- @MemberOrder(sequence = "2")
- @DescribedAs("Enter the category")
- public Category getCategory() {
- return category;
- }
-
- public void setCategory(final Category category) {
- this.category = category;
- }
- // }}
-
- // {{ OwnedBy (property)
- private String ownedBy;
-
- @Hidden
- // not shown in the UI
- public String getOwnedBy() {
- return ownedBy;
- }
-
- public void setOwnedBy(final String ownedBy) {
- this.ownedBy = ownedBy;
- }
-
- // }}
-
- // {{ Complete (property)
- private boolean complete;
-
- @Disabled
- // cannot be edited as a property
- @MemberOrder(sequence = "4")
- public boolean isComplete() {
- return complete;
- }
-
- public void setComplete(final boolean complete) {
- this.complete = complete;
- }
-
- // {{ Notes (property)
- private String notes;
-
- @Hidden(where=Where.ALL_TABLES)
- @Optional
- @MultiLine(numberOfLines=4)
- @MemberOrder(name="Detail", sequence = "6")
- public String getNotes() {
- return notes;
- }
-
- public void setNotes(final String notes) {
- this.notes = notes;
- }
- // }}
-
-
-
- // {{ Attachment (property)
- private BlobKey attachment;
-
- @Persistent
- @Optional
- @MemberOrder(name="Detail", sequence = "7")
- public BlobKey getAttachment() {
- return attachment;
- }
-
- public void setAttachment(final BlobKey attachment) {
- this.attachment = attachment;
- }
- // }}
-
-
-
-
- // {{ Version (derived property)
- @Hidden(where=Where.ALL_TABLES)
- @Disabled
- @MemberOrder(name="Detail", sequence = "99")
- @Named("Version")
- public Long getVersionSequence() {
- if(!(this instanceof PersistenceCapable)) {
- return null;
- }
- PersistenceCapable persistenceCapable = (PersistenceCapable) this;
- final Long version = (Long) JDOHelper.getVersion(persistenceCapable);
- return version;
- }
- public boolean hideVersionSequence() {
- return !(this instanceof PersistenceCapable);
- }
- // }}
-
- // {{ completed (action)
- @Bulk
- @MemberOrder(sequence = "1")
- public ToDoItem completed() {
- setComplete(true);
- return this;
- }
-
- // disable action dependent on state of object
- public String disableCompleted() {
- return complete ? "Already completed" : null;
- }
- // }}
-
- // {{ notYetCompleted (action)
- @MemberOrder(sequence = "2")
- public ToDoItem notYetCompleted() {
- setComplete(false);
- return this;
- }
-
-
- // disable action dependent on state of object
- public String disableNotYetCompleted() {
- return !complete ? "Not yet completed" : null;
- }
- // }}
-
-
-
-
- // {{ dependencies (Collection)
- @Unowned
- private List<ToDoItem> dependencies = new ArrayList<ToDoItem>();
-
- @Disabled
- @MemberOrder(sequence = "1")
- @Resolve(Type.EAGERLY)
- public List<ToDoItem> getDependencies() {
- return dependencies;
- }
-
- public void setDependencies(final List<ToDoItem> dependencies) {
- this.dependencies = dependencies;
- }
- // }}
-
- // {{ add (action)
- @MemberOrder(name="dependencies", sequence = "3")
- public ToDoItem add(final ToDoItem toDoItem) {
- getDependencies().add(toDoItem);
- return this;
- }
- public String validateAdd(final ToDoItem toDoItem) {
- if(getDependencies().contains(toDoItem)) {
- return "Already a dependency";
- }
- if(toDoItem == this) {
- return "Can't set up a dependency to self";
- }
- return null;
- }
- public List<ToDoItem> choices0Add() {
- return toDoItems.allToDos();
- }
-
-
- // }}
-
- // {{ remove (action)
- @MemberOrder(name="dependencies", sequence = "4")
- public ToDoItem remove(final ToDoItem toDoItem) {
- getDependencies().remove(toDoItem);
- return this;
- }
- public String disableRemove(final ToDoItem toDoItem) {
- return getDependencies().isEmpty()? "No dependencies to remove": null;
- }
- public String validateRemove(final ToDoItem toDoItem) {
- if(!getDependencies().contains(toDoItem)) {
- return "Not a dependency";
- }
- return null;
- }
- public List<ToDoItem> choices0Remove() {
- return getDependencies();
- }
- // }}
-
-
- // {{ clone (action)
- @Named("Clone")
- // the name of the action in the UI
- @MemberOrder(sequence = "3")
- // nb: method is not called "clone()" is inherited by java.lang.Object and
- // (a) has different semantics and (b) is in any case automatically ignored
- // by the framework
- public ToDoItem duplicate() {
- return toDoItems.newToDo(getDescription() + " - Copy", getCategory(), getDueBy());
- }
- // }}
-
- // {{ isDue (programmatic)
- @Programmatic
- // excluded from the framework's metamodel
- public boolean isDue() {
- if (getDueBy() == null) {
- return false;
- }
- return !isMoreThanOneWeekInPast(getDueBy());
- }
-
- // }}
-
-
- // {{ SimilarItems (derived collection)
- @MemberOrder(sequence = "5")
- @NotPersisted
- @Resolve(Type.EAGERLY)
- public List<ToDoItem> getSimilarItems() {
- return toDoItems.similarTo(this);
- }
-
- // }}
-
-
-
- // {{ compareTo (programmatic)
- /**
- * by complete flag, then due by date, then description
- */
- // exclude from the framework's metamodel
- @Override
- public int compareTo(final ToDoItem other) {
- if (isComplete() && !other.isComplete()) {
- return +1;
- }
- if (!isComplete() && other.isComplete()) {
- return -1;
- }
- if (getDueBy() == null && other.getDueBy() != null) {
- return +1;
- }
- if (getDueBy() != null && other.getDueBy() == null) {
- return -1;
- }
- if (getDueBy() == null && other.getDueBy() == null || getDueBy().equals(this.getDueBy())) {
- return getDescription().compareTo(other.getDescription());
- }
- return getDueBy().compareTo(getDueBy());
- }
- // }}
-
- // {{ helpers
- private static boolean isMoreThanOneWeekInPast(final Date dueBy) {
- return dueBy.getTime() < Clock.getTime() - ONE_WEEK_IN_MILLIS;
- }
-
- // }}
-
- // {{ filters (programmatic)
- @SuppressWarnings("unchecked")
- public static Filter<ToDoItem> thoseDue() {
- return Filters.and(Filters.not(thoseComplete()), new Filter<ToDoItem>() {
- @Override
- public boolean accept(final ToDoItem t) {
- return t.isDue();
- }
- });
- }
-
- public static Filter<ToDoItem> thoseComplete() {
- return new Filter<ToDoItem>() {
- @Override
- public boolean accept(final ToDoItem t) {
- return t.isComplete();
- }
- };
- }
-
- public static Filter<ToDoItem> thoseOwnedBy(final String currentUser) {
- return new Filter<ToDoItem>() {
- @Override
- public boolean accept(final ToDoItem toDoItem) {
- return Objects.equal(toDoItem.getOwnedBy(), currentUser);
- }
-
- };
- }
-
- public static Filter<ToDoItem> thoseSimilarTo(final ToDoItem toDoItem) {
- return new Filter<ToDoItem>() {
- @Override
- public boolean accept(final ToDoItem eachToDoItem) {
- return Objects.equal(toDoItem.getCategory(), eachToDoItem.getCategory()) &&
- Objects.equal(toDoItem.getOwnedBy(), eachToDoItem.getOwnedBy()) &&
- eachToDoItem != toDoItem;
- }
-
- };
- }
- // }}
-
- // {{ injected: DomainObjectContainer
- @SuppressWarnings("unused")
- private DomainObjectContainer container;
-
- public void setDomainObjectContainer(final DomainObjectContainer container) {
- this.container = container;
- }
- // }}
-
- // {{ injected: ToDoItems
- private ToDoItems toDoItems;
-
- public void setToDoItems(final ToDoItems toDoItems) {
- this.toDoItems = toDoItems;
- }
- // }}
-
-
-
- }
+#### ConnectionURL
+
+In `persistor.properties`, update the JDO `ConnectionURL` property, eg:
+
+ isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL=neo4j:neo4j_DB
+
+The other connection properties (`ConnectionDriverName`, `ConnectionUserName` and `ConnectionPassword`) should be commented out.
+
+
+#### Update pom.xml
+
+Add the following dependency to the `webapp` project's `pom.xml`:
+
+ <dependencies>
+ ...
+ <dependency>
+ <groupId>org.datanucleus</groupId>
+ <artifactId>datanucleus-neo4j</artifactId>
+ <version>3.2.3</version>
+ </dependency>
+ ...
+ </dependencies>
+
+In the [simpleapp](../../../intro/getting-started/simpleapp-archetype.html) and [todoapp](../../../intro/getting-started/simpleapp-archetype.html) applications this is defined under the "neo4j" profile so can be activated using `-P neo4j`.
+
+
+#### Try it out!
+
+If you want to quickly try out neo4j for yourself:
+
+* run the [simpleapp](../../../intro/getting-started/simpleapp-archetype.html) archetype (1.8.0-SNAPSHOT)
+
+* build the app:
+
+ mvn clean install -P neo4j
+
+* run the app:
+
+ mvn antrun:run -P self-host,neo4j -o
+
+If you visit the about page you should see the neo4j JAR files are linked in, and a `neo4j_DB` subdirectory within the `webapp` directory.
\ No newline at end of file
Modified: isis/site/trunk/content/documentation.md
URL: http://svn.apache.org/viewvc/isis/site/trunk/content/documentation.md?rev=1647386&r1=1647385&r2=1647386&view=diff
==============================================================================
--- isis/site/trunk/content/documentation.md (original)
+++ isis/site/trunk/content/documentation.md Mon Dec 22 19:30:19 2014
@@ -321,19 +321,25 @@ For both:
* [Externalized Configuration](./reference/externalized-configuration.html)
* [JVM args](./reference/jvm-args.html)
-JDO Objectstore
+#### <a name="more-advanced-topics-jdo-objectstore">JDO Objectstore</a>
-* [Deploying on the Google App Engine](components/objectstores/jdo/deploying-on-the-google-app-engine.html)
* [Using a JNDI Datasource](components/objectstores/jdo/using-jndi-datasource.html)
* [Enabling Logging](components/objectstores/jdo/enabling-logging.html)
+#### <a name="more-advanced-topics-no-sql-support">NoSQL Support</a>
+
+* [Deploying on the Google App Engine](components/objectstores/jdo/deploying-on-the-google-app-engine.html)
+* [Using Neo4J](components/objectstores/jdo/using-neo4j.html) (1.8.0-SNAPSHOT)
+
+<!--
#### <a name="core-bundled-components">Other Core Components</a>
* [Core Runtime](core/runtime.html) [stub]
* [Webserver](core/webserver.html) [stub]
* [In-memory Object Store](core/inmemory-objectstore.html) [stub]
* ['Bypass' Implementation](core/bypass-security.html) [stub]
+-->
}