org.nakedobjects.remoting.client.persistence.PersistenceSessionProxy Maven / Gradle / Ivy
package org.nakedobjects.remoting.client.persistence;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.adapter.ResolveState;
import org.nakedobjects.metamodel.adapter.oid.Oid;
import org.nakedobjects.metamodel.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.commons.component.ApplicationScopedComponent;
import org.nakedobjects.metamodel.commons.component.SessionScopedComponent;
import org.nakedobjects.metamodel.commons.debug.DebugString;
import org.nakedobjects.metamodel.facets.collections.modify.CollectionFacet;
import org.nakedobjects.metamodel.services.ServicesInjector;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.util.CollectionFacetUtils;
import org.nakedobjects.remoting.shared.ServerFacade;
import org.nakedobjects.remoting.shared.data.Data;
import org.nakedobjects.remoting.shared.encoding.object.ObjectEncoder;
import org.nakedobjects.remoting.shared.encoding.object.data.IdentityData;
import org.nakedobjects.remoting.shared.encoding.object.data.ObjectData;
import org.nakedobjects.remoting.shared.encoding.query.data.PersistenceQueryData;
import org.nakedobjects.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.PersistenceSession;
import org.nakedobjects.runtime.persistence.PersistenceSessionAbstract;
import org.nakedobjects.runtime.persistence.PersistenceSessionFactory;
import org.nakedobjects.runtime.persistence.adapterfactory.AdapterFactory;
import org.nakedobjects.runtime.persistence.adaptermanager.AdapterManagerExtended;
import org.nakedobjects.runtime.persistence.objectfactory.ObjectFactory;
import org.nakedobjects.runtime.persistence.oidgenerator.OidGenerator;
import org.nakedobjects.runtime.persistence.query.PersistenceQuery;
import org.nakedobjects.runtime.persistence.query.PersistenceQueryBuiltIn;
import org.nakedobjects.runtime.transaction.TransactionalClosureAbstract;
import org.nakedobjects.runtime.transaction.TransactionalClosureWithReturnAbstract;
import org.nakedobjects.runtime.transaction.updatenotifier.UpdateNotifier;
public class PersistenceSessionProxy extends PersistenceSessionAbstract {
final static Logger LOG = Logger.getLogger(PersistenceSessionProxy.class);
private ServerFacade connection;
private ObjectEncoder encoder;
private final Hashtable cache = new Hashtable();
private HashMap services = new HashMap();
//////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////
public PersistenceSessionProxy(
final PersistenceSessionFactory persistenceSessionFactory,
final AdapterFactory adapterFactory,
final ObjectFactory objectFactory,
final ServicesInjector containerInjector,
final OidGenerator oidGenerator,
final AdapterManagerExtended identityMap,
final ServerFacade distribution,
final ObjectEncoder encoder) {
super(persistenceSessionFactory, adapterFactory, objectFactory, containerInjector, oidGenerator, identityMap);
setConnection(distribution);
setEncoder(encoder);
}
//////////////////////////////////////////////////////////////////
// init, shutdown, reset, isInitialized
//////////////////////////////////////////////////////////////////
/**
* TODO: mismatch between {@link SessionScopedComponent} (open) and
* {@link ApplicationScopedComponent} (init).
*/
@Override
public void doOpen() {
connection.init();
}
/**
* TODO: mismatch between {@link SessionScopedComponent} (open) and
* {@link ApplicationScopedComponent} (init).
*/
public void doClose() {
connection.shutdown();
}
/**
* No need to install fixtures, rely on server-side to do the right thing.
*/
public boolean isFixturesInstalled() {
return true;
}
//////////////////////////////////////////////////////////////////
// objectChanged
//////////////////////////////////////////////////////////////////
public void objectChanged(final NakedObject adapter) {
if (adapter.isTransient()) {
getUpdateNotifier().addChangedObject(adapter);
}
if (adapter.getResolveState().respondToChangesInPersistentObjects()) {
getClientSideTransactionManager().addObjectChanged(adapter);
}
}
//////////////////////////////////////////////////////////////////
// destroy
//////////////////////////////////////////////////////////////////
public synchronized void destroyObject(final NakedObject object) {
if (LOG.isDebugEnabled()) {
LOG.debug("destroyObject " + object);
}
getClientSideTransactionManager().addDestroyObject(object);
// TODO need to do garbage collection instead
// NakedObjects.getObjectLoader().unloaded(object);
}
//////////////////////////////////////////////////////////////////
// makePersistent
//////////////////////////////////////////////////////////////////
/**
* REVIEW: we should perhaps have a little more symmetry here, and
* have the {@link ServerFacade} callback to the {@link PersistenceSession}
* (the PersistenceSessionPersist API) to handle remapping
* of adapters.
*/
public synchronized void makePersistent(final NakedObject object) {
if (LOG.isDebugEnabled()) {
LOG.debug("makePersistent " + object);
}
getClientSideTransactionManager().addMakePersistent(object);
// the two implementations (proxy vs object store) vary here.
// the object store does not make this call directly, it
// instead delegates to the PersistAlgorithm that makes a
// callback to the PersistenceSessionPersist API,
// which in turn calls remaps the adapters.
//
// the proxy persistor on the other hand does nothing here.
// instead we remap the adapter in distribution code,
// processing the handling of the returned results.
//
// (see REVIEW comment above)
}
//////////////////////////////////////////////////////////////////
// getInstances, hasInstances
//////////////////////////////////////////////////////////////////
@Override
protected NakedObject[] getInstances(final PersistenceQuery persistenceQuery) {
final NakedObjectSpecification noSpec = persistenceQuery.getSpecification();
if (LOG.isDebugEnabled()) {
LOG.debug("getInstances of " + noSpec + " with " + persistenceQuery);
}
// REVIEW: really not sure what this is doing
if (cache.containsKey(noSpec) && persistenceQuery instanceof PersistenceQueryBuiltIn) {
PersistenceQueryBuiltIn builtIn = (PersistenceQueryBuiltIn) persistenceQuery;
final NakedObject collection = (NakedObject) cache.get(noSpec);
if (collection.getSpecification().isCollection()) {
final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
final List instances = new ArrayList();
for (NakedObject instance : facet.iterable(collection)) {
if (builtIn.matches(instance)) {
instances.add(instance);
}
}
return (NakedObject[]) instances.toArray(new NakedObject[instances.size()]);
}
}
return findInstancesFromServer(persistenceQuery);
}
private NakedObject[] findInstancesFromServer(
final PersistenceQuery persistenceQuery) {
final PersistenceQueryData criteriaData = encoder.encodePersistenceQuery(persistenceQuery);
return getTransactionManager().executeWithinTransaction(
new TransactionalClosureWithReturnAbstract(){
public NakedObject[] execute() {
final ObjectData[] instancesAsObjectData = connection.findInstances(getAuthenticationSession(), criteriaData);
final NakedObject[] instances = new NakedObject[instancesAsObjectData.length];
for (int i = 0; i < instancesAsObjectData.length; i++) {
instances[i] = encoder.decode(instancesAsObjectData[i]);
}
return instances;
}
@Override
public void onSuccess() {
clearAllDirty();
}
});
}
public boolean hasInstances(final NakedObjectSpecification specification) {
if (LOG.isDebugEnabled()) {
LOG.debug("hasInstances of " + specification);
}
if (cache.containsKey(specification)) {
final NakedObject collection = (NakedObject) cache.get(specification);
final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
if (facet != null) {
return facet.size(collection) > 0;
}
}
return hasInstancesFromServer(specification);
}
private boolean hasInstancesFromServer(
final NakedObjectSpecification specification) {
return getTransactionManager().executeWithinTransaction(
new TransactionalClosureWithReturnAbstract(){
public Boolean execute() {
return connection.hasInstances(getAuthenticationSession(), specification.getFullName());
}});
}
//////////////////////////////////////////////////////////////////
// loadObject, reload
//////////////////////////////////////////////////////////////////
public synchronized NakedObject loadObject(final Oid oid, final NakedObjectSpecification hint) {
final NakedObject adapter = getAdapterManager().getAdapterFor(oid);
if (adapter != null) {
return adapter;
}
return loadObjectFromServer(oid, hint);
}
private NakedObject loadObjectFromServer(final Oid oid,
final NakedObjectSpecification hint) {
return getTransactionManager().executeWithinTransaction(
new TransactionalClosureWithReturnAbstract(){
public NakedObject execute() {
final ObjectData data = connection.getObject(null, oid, hint.getFullName());
return encoder.decode(data);
}});
}
public void reload(final NakedObject object) {
final IdentityData identityData = encoder.encodeIdentityData(object);
reloadFromServer(identityData);
}
private void reloadFromServer(final IdentityData identityData) {
getTransactionManager().executeWithinTransaction(
new TransactionalClosureAbstract() {
public void execute() {
final ObjectData update = connection.resolveImmediately(getAuthenticationSession(), identityData);
encoder.decode(update);
}});
}
//////////////////////////////////////////////////////////////////
// resolveImmediately, resolveField
//////////////////////////////////////////////////////////////////
public synchronized void resolveImmediately(final NakedObject adapter) {
final ResolveState resolveState = adapter.getResolveState();
if (!resolveState.canChangeTo(ResolveState.RESOLVING)) {
return;
}
final Oid oid = adapter.getOid();
if (LOG.isDebugEnabled()) {
LOG.debug("resolve object (remotely from server)" + oid);
}
resolveImmediatelyFromServer(adapter);
}
private void resolveImmediatelyFromServer(final NakedObject adapter) {
getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract(){
public void execute() {
final ObjectData data = connection.resolveImmediately(
getAuthenticationSession(), encoder.encodeIdentityData(adapter));
encoder.decode(data);
}
});
}
public void resolveField(final NakedObject adapter, final NakedObjectAssociation field) {
if (field.getSpecification().isCollectionOrIsAggregated()) {
return;
}
final NakedObject referenceAdapter = field.get(adapter);
if (referenceAdapter != null && referenceAdapter.getResolveState().isResolved()) {
return;
}
if (referenceAdapter == null || !referenceAdapter.isPersistent()) {
return;
}
if (LOG.isInfoEnabled()) {
LOG.info("resolveField on server: " + adapter + "/" + field.getId());
}
resolveFieldFromServer(adapter, field);
}
private void resolveFieldFromServer(final NakedObject adapter,
final NakedObjectAssociation field) {
getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
public void execute() {
final Data data = connection.resolveField(getAuthenticationSession(), encoder.encodeIdentityData(adapter), field
.getId());
encoder.decode(data);
}});
}
//////////////////////////////////////////////////////////////////
// Services
//////////////////////////////////////////////////////////////////
@Override
public Oid getOidForService(final String name) {
Oid oid = services.get(name);
if (oid == null) {
final IdentityData data = connection.oidForService(getAuthenticationSession(), name);
oid = data.getOid();
registerService(name, oid);
}
return oid;
}
@Override
public void registerService(final String name, final Oid oid) {
services.put(name, oid);
}
//////////////////////////////////////////////////////////////////
// Debugging
//////////////////////////////////////////////////////////////////
@Override
public void debugData(final DebugString debug) {
super.debugData(debug);
debug.appendln("Connection", connection);
}
public String debugTitle() {
return "Proxy Object Manager";
}
//////////////////////////////////////////////////////////////////
// Dependencies (injected)
//////////////////////////////////////////////////////////////////
public void setCacheInstances(final String[] names) {
for (int i = 0; i < names.length; i++) {
final NakedObjectSpecification spec = NakedObjectsContext.getSpecificationLoader().loadSpecification(names[i]);
cache.put(spec, Boolean.TRUE);
}
}
public void setConnection(final ServerFacade connection) {
this.connection = connection;
}
public void setEncoder(final ObjectEncoder factory) {
this.encoder = factory;
}
/**
* Downcasts.
*/
private ClientSideTransactionManager getClientSideTransactionManager() {
return (ClientSideTransactionManager) getTransactionManager();
}
//////////////////////////////////////////////////////////////////
// Dependencies (from context)
//////////////////////////////////////////////////////////////////
private AuthenticationSession getAuthenticationSession() {
return NakedObjectsContext.getAuthenticationSession();
}
private UpdateNotifier getUpdateNotifier() {
return NakedObjectsContext.getUpdateNotifier();
}
}
// Copyright (c) Naked Objects Group Ltd.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy