/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.isis.core.wrapper.handlers; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.datanucleus.enhancement.Persistable; import org.apache.isis.applib.annotation.Where; import org.apache.isis.applib.events.CollectionAccessEvent; import org.apache.isis.applib.events.InteractionEvent; import org.apache.isis.applib.events.ObjectTitleEvent; import org.apache.isis.applib.events.PropertyAccessEvent; import org.apache.isis.applib.events.UsabilityEvent; import org.apache.isis.applib.events.ValidityEvent; import org.apache.isis.applib.events.VisibilityEvent; import org.apache.isis.applib.services.wrapper.DisabledException; import org.apache.isis.applib.services.wrapper.HiddenException; import org.apache.isis.applib.services.wrapper.InteractionException; import org.apache.isis.applib.services.wrapper.InvalidException; import org.apache.isis.applib.services.wrapper.WrapperFactory.ExecutionMode; import org.apache.isis.applib.services.wrapper.WrapperObject; import org.apache.isis.applib.services.wrapper.WrappingObject; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.consent.Consent; import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; import org.apache.isis.core.metamodel.consent.InteractionResult; import org.apache.isis.core.metamodel.facets.ImperativeFacet; import org.apache.isis.core.metamodel.facets.ImperativeFacet.Intent; import org.apache.isis.core.metamodel.facets.object.mixin.MixinFacet; import org.apache.isis.core.metamodel.interactions.ObjectTitleContext; import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternal; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.Contributed; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; import org.apache.isis.core.metamodel.spec.feature.ObjectMember; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.core.metamodel.specloader.specimpl.ContributeeMember; import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionContributee; import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionMixedIn; import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault; import org.apache.isis.core.runtime.system.session.IsisSessionFactory; public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandlerDefault<T> { private final AuthenticationSessionProvider authenticationSessionProvider; private final SpecificationLoader specificationLoader; private final PersistenceSessionServiceInternal persistenceSessionServiceInternal; private final ProxyContextHandler proxy; private final ExecutionMode executionMode; private final IsisSessionFactory isisSessionFactory; /** * The <tt>title()</tt> method; may be <tt>null</tt>. */ protected Method titleMethod; /** * The <tt>__isis_save()</tt> method from {@link WrapperObject#__isis_save()}. */ protected Method __isis_saveMethod; /** * The <tt>save()</tt> method from {@link WrapperObject#save()}. */ @Deprecated protected Method saveMethod; /** * The <tt>__isis_wrapped()</tt> method from {@link WrapperObject#__isis_wrapped()}. */ protected Method __isis_wrappedMethod; /** * The <tt>wrapped()</tt> method from {@link WrapperObject#wrapped()}. */ @Deprecated protected Method wrappedMethod; /** * The <tt>__isis_executionMode()</tt> method from {@link WrapperObject#__isis_executionMode()}. */ protected Method __isis_executionMode; protected final Set<String> dnPersistableMethods = Sets.newHashSet(); public DomainObjectInvocationHandler( final T delegate, final ExecutionMode mode, final ProxyContextHandler proxy, final IsisSessionFactory isisSessionFactory) { super(delegate, mode, isisSessionFactory); this.proxy = proxy; this.executionMode = mode; this.isisSessionFactory = isisSessionFactory; final ServicesInjector servicesInjector = isisSessionFactory.getServicesInjector(); this.authenticationSessionProvider = servicesInjector.getAuthenticationSessionProvider(); this.specificationLoader = servicesInjector.getSpecificationLoader(); this.persistenceSessionServiceInternal = servicesInjector.getPersistenceSessionServiceInternal(); try { titleMethod = delegate.getClass().getMethod("title", new Class[]{}); } catch (final NoSuchMethodException e) { // ignore } try { __isis_saveMethod = WrapperObject.class.getMethod("__isis_save", new Class[]{}); __isis_wrappedMethod = WrapperObject.class.getMethod("__isis_wrapped", new Class[]{}); __isis_executionMode = WrapperObject.class.getMethod("__isis_executionMode", new Class[]{}); saveMethod = WrapperObject.class.getMethod("save", new Class[] {}); wrappedMethod = WrapperObject.class.getMethod("wrapped", new Class[] {}); dnPersistableMethods.addAll( Lists.newArrayList( Iterables.transform( Arrays.asList(Persistable.class.getDeclaredMethods()), new Function<Method, String>() { @Override public String apply(final Method input) { return input.getName(); } }))); } catch (final NoSuchMethodException nsme) { throw new IllegalStateException( "Could not locate reserved declared methods in the WrappingObject and WrappedObject interfaces", nsme); } } @Override public Object invoke(final Object proxyObject, final Method method, final Object[] args) throws Throwable { if (isObjectMethod(method)) { return delegate(method, args); } if(isJdoMethod(method)) { return delegate(method, args); } if(isInjectMethod(method)) { return delegate(method, args); } final ObjectAdapter targetAdapter = adapterFor(getDelegate()); if (isTitleMethod(method)) { return handleTitleMethod(targetAdapter); } final ObjectSpecification targetNoSpec = targetAdapter.getSpecification(); // save method, through the proxy if (isSaveMethod(method)) { return handleSaveMethod(targetAdapter, targetNoSpec); } if (isWrappedMethod(method)) { return getDelegate(); } if (isExecutionModeMethod(method)) { return executionMode; } final ObjectMember objectMember = locateAndCheckMember(method); final ContributeeMember contributeeMember = determineIfContributed(args, objectMember); final String memberName = objectMember.getName(); final Intent intent = ImperativeFacet.Util.getIntent(objectMember, method); if(intent == Intent.CHECK_IF_HIDDEN || intent == Intent.CHECK_IF_DISABLED) { throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'", memberName)); } if (intent == Intent.DEFAULTS || intent == Intent.CHOICES_OR_AUTOCOMPLETE) { return method.invoke(getDelegate(), args); } if (objectMember.isOneToOneAssociation()) { if (intent == Intent.CHECK_IF_VALID || intent == Intent.MODIFY_PROPERTY_SUPPORTING) { throw new UnsupportedOperationException(String.format("Cannot invoke supporting method for '%s'; use only property accessor/mutator", memberName)); } final OneToOneAssociation otoa = (OneToOneAssociation) objectMember; if (intent == Intent.ACCESSOR) { return handleGetterMethodOnProperty(targetAdapter, args, otoa); } if (intent == Intent.MODIFY_PROPERTY || intent == Intent.INITIALIZATION) { return handleSetterMethodOnProperty(targetAdapter, args, otoa); } } if (objectMember.isOneToManyAssociation()) { if (intent == Intent.CHECK_IF_VALID) { throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only collection accessor/mutator", memberName)); } final OneToManyAssociation otma = (OneToManyAssociation) objectMember; if (intent == Intent.ACCESSOR) { return handleGetterMethodOnCollection(targetAdapter, args, otma, method, memberName); } if (intent == Intent.MODIFY_COLLECTION_ADD) { return handleCollectionAddToMethod(targetAdapter, args, otma); } if (intent == Intent.MODIFY_COLLECTION_REMOVE) { return handleCollectionRemoveFromMethod(targetAdapter, args, otma); } } if (objectMember instanceof ObjectAction) { if (intent == Intent.CHECK_IF_VALID) { throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only the 'invoke' method", memberName)); } final ObjectAction objectAction = (ObjectAction) objectMember; ObjectAction actualObjectAction; ObjectAdapter actualTargetAdapter; final MixinFacet mixinFacet = targetAdapter.getSpecification().getFacet(MixinFacet.class); if(mixinFacet != null) { // rather than invoke on a (transient) mixin, instead try to // figure out the corresponding ObjectActionMixedIn actualTargetAdapter = mixinFacet.mixedIn(targetAdapter, MixinFacet.Policy.IGNORE_FAILURES); actualObjectAction = determineMixinAction(actualTargetAdapter, objectAction); if(actualTargetAdapter == null || actualObjectAction == null) { // revert to original behaviour actualTargetAdapter = targetAdapter; actualObjectAction = objectAction; } } else { actualTargetAdapter = targetAdapter; actualObjectAction = objectAction; } return handleActionMethod(actualTargetAdapter, args, actualObjectAction, contributeeMember); } throw new UnsupportedOperationException(String.format("Unknown member type '%s'", objectMember)); } private static ObjectAction determineMixinAction( final ObjectAdapter domainObjectAdapter, final ObjectAction objectAction) { if(domainObjectAdapter == null) { return null; } final ObjectSpecification specification = domainObjectAdapter.getSpecification(); final List<ObjectAction> objectActions = specification.getObjectActions(Contributed.INCLUDED); for (final ObjectAction action : objectActions) { if(action instanceof ObjectActionMixedIn) { ObjectActionMixedIn mixedInAction = (ObjectActionMixedIn) action; if(mixedInAction.hasMixinAction(objectAction)) { return mixedInAction; } } } // throw new RuntimeException("Unable to find the mixed-in action corresponding to " + objectAction.getIdentifier().toFullIdentityString()); return null; } public InteractionInitiatedBy getInteractionInitiatedBy() { return getExecutionMode().shouldEnforceRules()? InteractionInitiatedBy.USER: InteractionInitiatedBy.FRAMEWORK; } // see if this is a contributed property/collection/action private ContributeeMember determineIfContributed( final Object[] args, final ObjectMember objectMember) { if (!(objectMember instanceof ObjectAction)) { return null; } final ObjectAction objectAction = (ObjectAction) objectMember; for (final Object arg : args) { if (arg == null) { continue; } final ObjectSpecificationDefault objectSpec = getJavaSpecification(arg.getClass()); if (args.length == 1) { // is this a contributed property/collection? final List<ObjectAssociation> associations = objectSpec.getAssociations(Contributed.INCLUDED); for (final ObjectAssociation association : associations) { if (association instanceof ContributeeMember) { final ContributeeMember contributeeMember = (ContributeeMember) association; if (contributeeMember.isContributedBy(objectAction)) { return contributeeMember; } } } } // is this a contributed action? final List<ObjectAction> actions = objectSpec.getObjectActions(Contributed.INCLUDED); for (final ObjectAction action : actions) { if (action instanceof ContributeeMember) { final ContributeeMember contributeeMember = (ContributeeMember) action; if (contributeeMember.isContributedBy(objectAction)) { return contributeeMember; } } } } return null; } private boolean isJdoMethod(final Method method) { return methodStartsWith(method, "jdo") || dnPersistableMethods.contains(method.getName()); } private static boolean isInjectMethod(final Method method) { return methodStartsWith(method, "inject"); } private static boolean methodStartsWith(final Method method, final String prefix) { return method.getName().startsWith(prefix); } // ///////////////////////////////////////////////////////////////// // title // ///////////////////////////////////////////////////////////////// private Object handleTitleMethod(final ObjectAdapter targetAdapter) throws IllegalAccessException, InvocationTargetException { resolveIfRequired(targetAdapter); final ObjectSpecification targetNoSpec = targetAdapter.getSpecification(); final ObjectTitleContext titleContext = targetNoSpec.createTitleInteractionContext(getAuthenticationSession(), InteractionInitiatedBy.FRAMEWORK, targetAdapter); final ObjectTitleEvent titleEvent = titleContext.createInteractionEvent(); notifyListeners(titleEvent); return titleEvent.getTitle(); } // ///////////////////////////////////////////////////////////////// // save // ///////////////////////////////////////////////////////////////// private Object handleSaveMethod( final ObjectAdapter targetAdapter, final ObjectSpecification targetNoSpec) { if(getExecutionMode().shouldEnforceRules()) { final InteractionResult interactionResult = targetNoSpec.isValidResult(targetAdapter, getInteractionInitiatedBy()); notifyListenersAndVetoIfRequired(interactionResult); } if (getExecutionMode().shouldExecute()) { if (targetAdapter.isTransient()) { getPersistenceSessionService().makePersistent(targetAdapter); } } return null; } // ///////////////////////////////////////////////////////////////// // property - access // ///////////////////////////////////////////////////////////////// private Object handleGetterMethodOnProperty( final ObjectAdapter targetAdapter, final Object[] args, final OneToOneAssociation property) { if (args.length != 0) { throw new IllegalArgumentException("Invoking a 'get' should have no arguments"); } if(getExecutionMode().shouldEnforceRules()) { checkVisibility(targetAdapter, property); } resolveIfRequired(targetAdapter); final InteractionInitiatedBy interactionInitiatedBy = getInteractionInitiatedBy(); final ObjectAdapter currentReferencedAdapter = property.get(targetAdapter, interactionInitiatedBy); final Object currentReferencedObj = ObjectAdapter.Util.unwrap(currentReferencedAdapter); final PropertyAccessEvent ev = new PropertyAccessEvent(getDelegate(), property.getIdentifier(), currentReferencedObj); notifyListeners(ev); return currentReferencedObj; } // ///////////////////////////////////////////////////////////////// // property - modify // ///////////////////////////////////////////////////////////////// private Object handleSetterMethodOnProperty( final ObjectAdapter targetAdapter, final Object[] args, final OneToOneAssociation property) { if (args.length != 1) { throw new IllegalArgumentException("Invoking a setter should only have a single argument"); } final Object argumentObj = underlying(args[0]); if(getExecutionMode().shouldEnforceRules()) { checkVisibility(targetAdapter, property); checkUsability(targetAdapter, property); } final ObjectAdapter argumentAdapter = argumentObj != null ? adapterFor(argumentObj) : null; resolveIfRequired(targetAdapter); if(getExecutionMode().shouldEnforceRules()) { final InteractionResult interactionResult = property.isAssociationValid(targetAdapter, argumentAdapter, getInteractionInitiatedBy()).getInteractionResult(); notifyListenersAndVetoIfRequired(interactionResult); } if (getExecutionMode().shouldExecute()) { property.set(targetAdapter, argumentAdapter, getInteractionInitiatedBy()); } return null; } // ///////////////////////////////////////////////////////////////// // collection - access // ///////////////////////////////////////////////////////////////// private Object handleGetterMethodOnCollection( final ObjectAdapter targetAdapter, final Object[] args, final OneToManyAssociation collection, final Method method, final String memberName) { if (args.length != 0) { throw new IllegalArgumentException("Invoking a 'get' should have no arguments"); } if(getExecutionMode().shouldEnforceRules()) { checkVisibility(targetAdapter, collection); } resolveIfRequired(targetAdapter); final InteractionInitiatedBy interactionInitiatedBy = getInteractionInitiatedBy(); final ObjectAdapter currentReferencedAdapter = collection.get(targetAdapter, interactionInitiatedBy); final Object currentReferencedObj = ObjectAdapter.Util.unwrap(currentReferencedAdapter); final CollectionAccessEvent ev = new CollectionAccessEvent(getDelegate(), collection.getIdentifier()); if (currentReferencedObj instanceof Collection) { final Collection<?> collectionViewObject = lookupWrappingObject(method, memberName, (Collection<?>) currentReferencedObj, collection); notifyListeners(ev); return collectionViewObject; } else if (currentReferencedObj instanceof Map) { final Map<?, ?> mapViewObject = lookupWrappingObject(memberName, (Map<?, ?>) currentReferencedObj, collection); notifyListeners(ev); return mapViewObject; } throw new IllegalArgumentException(String.format("Collection type '%s' not supported by framework", currentReferencedObj.getClass().getName())); } private Collection<?> lookupWrappingObject( final Method method, final String memberName, final Collection<?> collectionToLookup, final OneToManyAssociation otma) { return collectionToLookup instanceof WrappingObject ? collectionToLookup : proxy.proxy(collectionToLookup, memberName, this, otma); } private Map<?, ?> lookupWrappingObject( final String memberName, final Map<?, ?> mapToLookup, final OneToManyAssociation otma) { return mapToLookup instanceof WrappingObject ? mapToLookup : proxy.proxy(mapToLookup, memberName, this, otma); } // ///////////////////////////////////////////////////////////////// // collection - add to // ///////////////////////////////////////////////////////////////// private Object handleCollectionAddToMethod( final ObjectAdapter targetAdapter, final Object[] args, final OneToManyAssociation otma) { if (args.length != 1) { throw new IllegalArgumentException("Invoking a addTo should only have a single argument"); } if(getExecutionMode().shouldEnforceRules()) { checkVisibility(targetAdapter, otma); checkUsability(targetAdapter, otma); } resolveIfRequired(targetAdapter); final Object argumentObj = underlying(args[0]); if (argumentObj == null) { throw new IllegalArgumentException("Must provide a non-null object to add"); } final ObjectAdapter argumentNO = adapterFor(argumentObj); if(getExecutionMode().shouldEnforceRules()) { final InteractionResult interactionResult = otma.isValidToAdd(targetAdapter, argumentNO, getInteractionInitiatedBy()).getInteractionResult(); notifyListenersAndVetoIfRequired(interactionResult); } if (getExecutionMode().shouldExecute()) { otma.addElement(targetAdapter, argumentNO, getInteractionInitiatedBy()); } return null; } // ///////////////////////////////////////////////////////////////// // collection - remove from // ///////////////////////////////////////////////////////////////// private Object handleCollectionRemoveFromMethod( final ObjectAdapter targetAdapter, final Object[] args, final OneToManyAssociation collection) { if (args.length != 1) { throw new IllegalArgumentException("Invoking a removeFrom should only have a single argument"); } if(getExecutionMode().shouldEnforceRules()) { checkVisibility(targetAdapter, collection); checkUsability(targetAdapter, collection); } resolveIfRequired(targetAdapter); final Object argumentObj = underlying(args[0]); if (argumentObj == null) { throw new IllegalArgumentException("Must provide a non-null object to remove"); } final ObjectAdapter argumentAdapter = adapterFor(argumentObj); if(getExecutionMode().shouldEnforceRules()) { final InteractionResult interactionResult = collection.isValidToRemove(targetAdapter, argumentAdapter, getInteractionInitiatedBy()).getInteractionResult(); notifyListenersAndVetoIfRequired(interactionResult); } if (getExecutionMode().shouldExecute()) { collection.removeElement(targetAdapter, argumentAdapter, getInteractionInitiatedBy()); } return null; } // ///////////////////////////////////////////////////////////////// // action // ///////////////////////////////////////////////////////////////// private Object handleActionMethod( final ObjectAdapter targetAdapter, final Object[] args, final ObjectAction objectAction, final ContributeeMember contributeeMember) { final ObjectAdapter contributeeAdapter; final Object[] contributeeArgs; if(contributeeMember != null) { final int contributeeParamPosition = contributeeMember.getContributeeParamPosition(); final Object contributee = args[contributeeParamPosition]; contributeeAdapter = adapterFor(contributee); final List<Object> argCopy = Lists.newArrayList(args); argCopy.remove(contributeeParamPosition); contributeeArgs = argCopy.toArray(); } else { contributeeAdapter = null; contributeeArgs = null; } if(getExecutionMode().shouldEnforceRules()) { if(contributeeMember != null) { checkVisibility(contributeeAdapter, contributeeMember); checkUsability(contributeeAdapter, contributeeMember); } else { checkVisibility(targetAdapter, objectAction); checkUsability(targetAdapter, objectAction); } } final ObjectAdapter[] argAdapters = asObjectAdaptersUnderlying(args); if(getExecutionMode().shouldEnforceRules()) { if(contributeeMember != null) { if(contributeeMember instanceof ObjectActionContributee) { final ObjectActionContributee objectActionContributee = (ObjectActionContributee) contributeeMember; final ObjectAdapter[] contributeeArgAdapters = asObjectAdaptersUnderlying(contributeeArgs); checkValidity(contributeeAdapter, objectActionContributee, contributeeArgAdapters); } // nothing to do for contributed properties or collections } else { checkValidity(targetAdapter, objectAction, argAdapters); } } if (getExecutionMode().shouldExecute()) { final InteractionInitiatedBy interactionInitiatedBy = getInteractionInitiatedBy(); final ObjectAdapter mixedInAdapter = null; // if a mixin action, then it will automatically fill in. final ObjectAdapter returnedAdapter = objectAction.execute( targetAdapter, mixedInAdapter, argAdapters, interactionInitiatedBy); return ObjectAdapter.Util.unwrap(returnedAdapter); } return null; } private void checkValidity(final ObjectAdapter targetAdapter, final ObjectAction objectAction, final ObjectAdapter[] argAdapters) { final InteractionResult interactionResult = objectAction.isProposedArgumentSetValid(targetAdapter, argAdapters, getInteractionInitiatedBy()).getInteractionResult(); notifyListenersAndVetoIfRequired(interactionResult); } private ObjectAdapter[] asObjectAdaptersUnderlying(final Object[] args) { final ObjectAdapter[] argAdapters = new ObjectAdapter[args.length]; int i = 0; for (final Object arg : args) { argAdapters[i++] = adapterFor(underlying(arg)); } return argAdapters; } private ObjectAdapter adapterFor(final Object obj) { return obj != null ? getPersistenceSessionService().adapterFor(obj) : null; } private Object underlying(final Object arg) { if (arg instanceof WrappingObject) { final WrappingObject argViewObject = (WrappingObject) arg; return argViewObject.__isis_wrapped(); } else { return arg; } } // ///////////////////////////////////////////////////////////////// // visibility and usability checks (common to all members) // ///////////////////////////////////////////////////////////////// /** * REVIEW: ideally should provide some way to allow to caller to indicate the 'where' context. Having * a hard-coded value like this is an approximation. */ private final Where where = Where.ANYWHERE; private void checkVisibility( final ObjectAdapter targetObjectAdapter, final ObjectMember objectMember) { final Consent visibleConsent = objectMember.isVisible(targetObjectAdapter, getInteractionInitiatedBy(), where); final InteractionResult interactionResult = visibleConsent.getInteractionResult(); notifyListenersAndVetoIfRequired(interactionResult); } private void checkUsability( final ObjectAdapter targetObjectAdapter, final ObjectMember objectMember) { final InteractionResult interactionResult = objectMember.isUsable(targetObjectAdapter, getInteractionInitiatedBy(), where ).getInteractionResult(); notifyListenersAndVetoIfRequired(interactionResult); } // ///////////////////////////////////////////////////////////////// // notify listeners // ///////////////////////////////////////////////////////////////// private void notifyListenersAndVetoIfRequired(final InteractionResult interactionResult) { final InteractionEvent interactionEvent = interactionResult.getInteractionEvent(); notifyListeners(interactionEvent); if (interactionEvent.isVeto()) { throw toException(interactionEvent); } } /** * Wraps a {@link InteractionEvent#isVeto() vetoing} * {@link InteractionEvent} in a corresponding {@link InteractionException}, * and returns it. */ private InteractionException toException(final InteractionEvent interactionEvent) { if (!interactionEvent.isVeto()) { throw new IllegalArgumentException("Provided interactionEvent must be a veto"); } if (interactionEvent instanceof ValidityEvent) { final ValidityEvent validityEvent = (ValidityEvent) interactionEvent; return new InvalidException(validityEvent); } if (interactionEvent instanceof VisibilityEvent) { final VisibilityEvent visibilityEvent = (VisibilityEvent) interactionEvent; return new HiddenException(visibilityEvent); } if (interactionEvent instanceof UsabilityEvent) { final UsabilityEvent usabilityEvent = (UsabilityEvent) interactionEvent; return new DisabledException(usabilityEvent); } throw new IllegalArgumentException("Provided interactionEvent must be a VisibilityEvent, UsabilityEvent or a ValidityEvent"); } // ///////////////////////////////////////////////////////////////// // switching // ///////////////////////////////////////////////////////////////// private ObjectMember locateAndCheckMember(final Method method) { final ObjectSpecificationDefault objectSpecificationDefault = getJavaSpecificationOfOwningClass(method); final ObjectMember member = objectSpecificationDefault.getMember(method); if (member == null) { final String methodName = method.getName(); throw new UnsupportedOperationException("Method '" + methodName + "' being invoked does not correspond to any of the object's fields or actions."); } return member; } protected boolean isTitleMethod(final Method method) { return method.equals(titleMethod); } protected boolean isSaveMethod(final Method method) { return method.equals(saveMethod) || method.equals(__isis_saveMethod); } protected boolean isWrappedMethod(final Method method) { return method.equals(wrappedMethod) || method.equals(__isis_wrappedMethod); } protected boolean isExecutionModeMethod(final Method method) { return method.equals(__isis_executionMode); } // ///////////////////////////////////////////////////////////////// // Specification lookup // ///////////////////////////////////////////////////////////////// private ObjectSpecificationDefault getJavaSpecificationOfOwningClass(final Method method) { return getJavaSpecification(method.getDeclaringClass()); } private ObjectSpecificationDefault getJavaSpecification(final Class<?> clazz) { final ObjectSpecification objectSpec = getSpecification(clazz); if (!(objectSpec instanceof ObjectSpecificationDefault)) { throw new UnsupportedOperationException("Only Java is supported (specification is '" + objectSpec.getClass().getCanonicalName() + "')"); } return (ObjectSpecificationDefault) objectSpec; } private ObjectSpecification getSpecification(final Class<?> type) { return getSpecificationLoader().loadSpecification(type); } // ///////////////////////////////////////////////////////////////// // Dependencies // ///////////////////////////////////////////////////////////////// protected SpecificationLoader getSpecificationLoader() { return isisSessionFactory.getSpecificationLoader(); } public AuthenticationSessionProvider getAuthenticationSessionProvider() { return authenticationSessionProvider; } protected AuthenticationSession getAuthenticationSession() { return getAuthenticationSessionProvider().getAuthenticationSession(); } protected PersistenceSessionServiceInternal getPersistenceSessionService() { return persistenceSessionServiceInternal; } public IsisSessionFactory getIsisSessionFactory() { return isisSessionFactory; } }