/* * 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.viewer.wicket.ui.components.collection.bulk; import java.util.List; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.wicket.markup.html.link.AbstractLink; import org.apache.wicket.markup.html.link.Link; import org.apache.isis.applib.RecoverableException; import org.apache.isis.applib.annotation.Bulk; import org.apache.isis.applib.annotation.InvokedOn; import org.apache.isis.applib.services.actinvoc.ActionInvocationContext; import org.apache.isis.applib.services.command.Command; import org.apache.isis.applib.services.command.Command.Executor; import org.apache.isis.applib.services.command.CommandContext; import org.apache.isis.applib.services.xactn.TransactionService; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.authentication.MessageBroker; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.core.runtime.system.persistence.PersistenceSession; import org.apache.isis.core.runtime.system.session.IsisSessionFactory; import org.apache.isis.viewer.wicket.model.links.LinkAndLabel; import org.apache.isis.viewer.wicket.model.mementos.ActionMemento; import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento; import org.apache.isis.viewer.wicket.model.models.ActionModel; import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel; import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponse; import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponseType; import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.ObjectAdapterToggleboxColumn; import org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel.ActionLinkFactory; import org.apache.isis.viewer.wicket.ui.errors.JGrowlBehaviour; public final class BulkActionsLinkFactory implements ActionLinkFactory { private static final long serialVersionUID = 1L; private final EntityCollectionModel model; private final ObjectAdapterToggleboxColumn toggleboxColumn; public BulkActionsLinkFactory( final EntityCollectionModel model, final ObjectAdapterToggleboxColumn toggleboxColumn) { this.model = model; this.toggleboxColumn = toggleboxColumn; } /** * @param objectAction * @param linkId */ @Override public LinkAndLabel newLink( final ObjectAction objectAction, final String linkId) { final ActionMemento actionMemento = new ActionMemento(objectAction); final AbstractLink link = new Link<Object>(linkId) { private static final long serialVersionUID = 1L; @Override public void onClick() { final ObjectAction objectAction = actionMemento.getAction(getSpecificationLoader()); final ConcurrencyChecking concurrencyChecking = ConcurrencyChecking.concurrencyCheckingFor(objectAction.getSemantics()); try { final List<ObjectAdapterMemento> toggleMementosList = model.getToggleMementosList(); final List<ObjectAdapter> toggledAdapters = FluentIterable.from(toggleMementosList) .transform( ObjectAdapterMemento.Functions.fromMemento( concurrencyChecking, getPersistenceSession(), getSpecificationLoader())) .toList(); final List<Object> domainObjects = Lists.newArrayList(Iterables.transform(toggledAdapters, ObjectAdapter.Functions.getObject())); final ActionInvocationContext actionInvocationContext = getServicesInjector().lookupService(ActionInvocationContext.class); if (actionInvocationContext != null) { actionInvocationContext.setInvokedOn(InvokedOn.COLLECTION); actionInvocationContext.setDomainObjects(domainObjects); } final Bulk.InteractionContext bulkInteractionContext = getServicesInjector().lookupService(Bulk.InteractionContext.class); if (bulkInteractionContext != null) { bulkInteractionContext.setInvokedAs(Bulk.InteractionContext.InvokedAs.BULK); bulkInteractionContext.setDomainObjects(domainObjects); } ObjectAdapter lastReturnedAdapter = null; int i=0; for(final ObjectAdapter adapter : toggledAdapters) { final CommandContext commandContext = getServicesInjector().lookupService(CommandContext.class); final Command command; if (commandContext != null) { command = commandContext.getCommand(); command.setExecutor(Executor.USER); } int numParameters = objectAction.getParameterCount(); if(numParameters != 0) { return; } if (bulkInteractionContext != null) { bulkInteractionContext.setIndex(i++); } final ObjectAdapter mixedInAdapter = null; final ObjectAdapter[] arguments = {}; lastReturnedAdapter = objectAction.executeWithRuleChecking( adapter, mixedInAdapter, arguments, InteractionInitiatedBy.USER, ActionModel.WHERE_FOR_ACTION_INVOCATION ); TransactionService transactionService = getServicesInjector().lookupService(TransactionService.class); transactionService.nextTransaction(); } model.clearToggleMementosList(); toggleboxColumn.clearToggles(); final ActionModel actionModel = model.getActionModelHint(); if(actionModel != null && actionModel.getActionMemento().getAction(getSpecificationLoader()).getSemantics().isIdempotentInNature()) { actionModel.detach(); // force reload ObjectAdapter resultAdapter = actionModel.getObject(); model.setObjectList(resultAdapter); } else { model.setObject(persistentAdaptersWithin(model.getObject())); } if(lastReturnedAdapter != null) { final ActionResultResponse resultResponse = ActionResultResponseType.determineAndInterpretResult(actionModel, null, lastReturnedAdapter); resultResponse.getHandlingStrategy().handleResults(resultResponse, model.getIsisSessionFactory()); } } catch(final ConcurrencyException ex) { recover(); // display a warning to the user so that they know that the action wasn't performed getMessageBroker().addWarning(ex.getMessage()); return; } catch(final RuntimeException ex) { final RecoverableException appEx = RecoverableException.Util.getRecoverableExceptionIfAny(ex); if (appEx != null) { recover(); getMessageBroker().setApplicationError(appEx.getMessage()); // there's no need to abort the transaction, it will have already been done // (in IsisTransactionManager#executeWithinTransaction(...)). return; } throw ex; } } private void recover() { // resync with the objectstore final List<ObjectAdapterMemento> toggleMementosList = Lists.newArrayList(model.getToggleMementosList()); for (ObjectAdapterMemento oam : toggleMementosList) { // just requesting the adapter will sync the OAM's version with the objectstore oam.getObjectAdapter(ConcurrencyChecking.NO_CHECK, getPersistenceSession(), getSpecificationLoader()); } // discard any adapters that might have been deleted model.setObject(persistentAdaptersWithin(model.getObject())); // attempt to preserve the toggled adapters final List<ObjectAdapter> adapters = model.getObject(); model.clearToggleMementosList(); for (ObjectAdapterMemento oam : toggleMementosList) { final ObjectAdapter objectAdapter = oam.getObjectAdapter(ConcurrencyChecking.NO_CHECK, getPersistenceSession(), getSpecificationLoader()); if(adapters.contains(objectAdapter)) { // in case it has been deleted... model.toggleSelectionOn(objectAdapter); } } } private List<ObjectAdapter> persistentAdaptersWithin(List<ObjectAdapter> adapters) { return Lists.newArrayList(Iterables.filter(adapters, new Predicate<ObjectAdapter>() { @Override public boolean apply(ObjectAdapter input) { return !input.isTransient() && !input.isDestroyed(); } })); } }; link.add(new JGrowlBehaviour()); final String disabledReasonIfAny = null; final boolean blobOrClob = false; final ObjectAdapter objectAdapter = null; return LinkAndLabel.newLinkAndLabel(objectAdapter, objectAction, link, disabledReasonIfAny, blobOrClob); } /////////////////////////////////////////////////////// // Dependencies (from context) /////////////////////////////////////////////////////// SpecificationLoader getSpecificationLoader() { return getIsisSessionFactory().getSpecificationLoader(); } PersistenceSession getPersistenceSession() { return getIsisSessionFactory().getCurrentSession().getPersistenceSession(); } public AuthenticationSession getAuthenticationSession() { return getIsisSessionFactory().getCurrentSession().getAuthenticationSession(); } protected MessageBroker getMessageBroker() { return getAuthenticationSession().getMessageBroker(); } protected ServicesInjector getServicesInjector() { return getIsisSessionFactory().getServicesInjector(); } private IsisSessionFactory getIsisSessionFactory() { return model.getIsisSessionFactory(); } }