001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.progmodel.facets.actions.invoke.event; 021 022import java.lang.reflect.Method; 023 024import org.apache.isis.applib.annotation.PostsActionInvokedEvent; 025import org.apache.isis.applib.annotation.PostsCollectionAddedToEvent; 026import org.apache.isis.applib.services.eventbus.ActionInvokedEvent; 027import org.apache.isis.applib.services.eventbus.CollectionAddedToEvent; 028import org.apache.isis.core.commons.lang.StringExtensions; 029import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 030import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware; 031import org.apache.isis.core.metamodel.facetapi.Facet; 032import org.apache.isis.core.metamodel.facetapi.FacetHolder; 033import org.apache.isis.core.metamodel.facetapi.FacetUtil; 034import org.apache.isis.core.metamodel.facetapi.FeatureType; 035import org.apache.isis.core.metamodel.facets.Annotations; 036import org.apache.isis.core.metamodel.facets.actions.debug.DebugFacet; 037import org.apache.isis.core.metamodel.facets.actions.exploration.ExplorationFacet; 038import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacet; 039import org.apache.isis.core.metamodel.facets.named.NamedFacet; 040import org.apache.isis.core.metamodel.facets.named.NamedFacetInferred; 041import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext; 042import org.apache.isis.core.metamodel.runtimecontext.RuntimeContextAware; 043import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector; 044import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware; 045import org.apache.isis.core.metamodel.spec.ObjectSpecification; 046import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract; 047import org.apache.isis.core.progmodel.facets.actions.invoke.DebugFacetViaNamingConvention; 048import org.apache.isis.core.progmodel.facets.actions.invoke.ExplorationFacetViaNamingConvention; 049 050/** 051 * Sets up {@link ActionInvocationFacet}, along with a number of supporting 052 * facets that are based on the action's name. 053 * 054 * <p> 055 * The supporting methods are: {@link ExecutedFacet}, {@link ExplorationFacet} 056 * and {@link DebugFacet}. In addition a {@link NamedFacet} is inferred from the 057 * name (taking into account the above well-known prefixes). 058 */ 059public class PostsActionInvokedEventFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements AdapterManagerAware, ServicesInjectorAware, RuntimeContextAware { 060 061 private static final String EXPLORATION_PREFIX = "Exploration"; 062 private static final String DEBUG_PREFIX = "Debug"; 063 064 private static final String[] PREFIXES = { EXPLORATION_PREFIX, DEBUG_PREFIX }; 065 066 private AdapterManager adapterManager; 067 private ServicesInjector servicesInjector; 068 private RuntimeContext runtimeContext; 069 070 /** 071 * Note that the {@link Facet}s registered are the generic ones from 072 * noa-architecture (where they exist) 073 */ 074 public PostsActionInvokedEventFacetFactory() { 075 super(FeatureType.ACTIONS_ONLY, OrphanValidation.VALIDATE, PREFIXES); 076 } 077 078 // /////////////////////////////////////////////////////// 079 // Actions 080 // /////////////////////////////////////////////////////// 081 082 @Override 083 public void process(final ProcessMethodContext processMethodContext) { 084 085 // InvocationFacet 086 attachInvocationFacet(processMethodContext); 087 088 // DebugFacet, ExplorationFacet 089 attachDebugFacetIfActionMethodNamePrefixed(processMethodContext); 090 attachExplorationFacetIfActionMethodNamePrefixed(processMethodContext); 091 092 // inferred name 093 // (must be called after the attachinvocationFacet methods) 094 attachNamedFacetInferredFromMethodName(processMethodContext); 095 } 096 097 private void attachInvocationFacet(final ProcessMethodContext processMethodContext) { 098 099 final Method actionMethod = processMethodContext.getMethod(); 100 101 try { 102 final PostsActionInvokedEvent annotation = Annotations.getAnnotation(actionMethod, PostsActionInvokedEvent.class); 103 if(annotation == null) { 104 return; 105 } 106 107 final Class<?> returnType = actionMethod.getReturnType(); 108 final ObjectSpecification returnSpec = getSpecificationLoader().loadSpecification(returnType); 109 if (returnSpec == null) { 110 return; 111 } 112 113 final Class<?> cls = processMethodContext.getCls(); 114 final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls); 115 final FacetHolder holder = processMethodContext.getFacetHolder(); 116 117 final Class<? extends ActionInvokedEvent<?>> changedEventType = annotation.value(); 118 119 FacetUtil.addFacet(new PostsActionInvokedEventFacetAnnotation(actionMethod, typeSpec, returnSpec, holder, getRuntimeContext(), getAdapterManager(), getServicesInjector(), changedEventType)); 120 } finally { 121 processMethodContext.removeMethod(actionMethod); 122 } 123 } 124 125 private void attachDebugFacetIfActionMethodNamePrefixed(final ProcessMethodContext processMethodContext) { 126 127 final Method actionMethod = processMethodContext.getMethod(); 128 final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName()); 129 if (!capitalizedName.startsWith(DEBUG_PREFIX)) { 130 return; 131 } 132 final FacetHolder facetedMethod = processMethodContext.getFacetHolder(); 133 FacetUtil.addFacet(new DebugFacetViaNamingConvention(facetedMethod)); 134 } 135 136 private void attachExplorationFacetIfActionMethodNamePrefixed(final ProcessMethodContext processMethodContext) { 137 138 final Method actionMethod = processMethodContext.getMethod(); 139 final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName()); 140 if (!capitalizedName.startsWith(EXPLORATION_PREFIX)) { 141 return; 142 } 143 final FacetHolder facetedMethod = processMethodContext.getFacetHolder(); 144 FacetUtil.addFacet(new ExplorationFacetViaNamingConvention(facetedMethod)); 145 } 146 147 /** 148 * Must be called after added the debug, exploration etc facets. 149 * 150 * <p> 151 * TODO: remove this hack 152 */ 153 private void attachNamedFacetInferredFromMethodName(final ProcessMethodContext processMethodContext) { 154 155 final Method method = processMethodContext.getMethod(); 156 final String capitalizedName = StringExtensions.asCapitalizedName(method.getName()); 157 158 // this is nasty... 159 String name = capitalizedName; 160 name = StringExtensions.removePrefix(name, DEBUG_PREFIX); 161 name = StringExtensions.removePrefix(name, EXPLORATION_PREFIX); 162 name = StringExtensions.asNaturalName2(name); 163 164 final FacetHolder facetedMethod = processMethodContext.getFacetHolder(); 165 FacetUtil.addFacet(new NamedFacetInferred(name, facetedMethod)); 166 } 167 168 // /////////////////////////////////////////////////////////////// 169 // Dependencies 170 // /////////////////////////////////////////////////////////////// 171 172 @Override 173 public void setAdapterManager(final AdapterManager adapterManager) { 174 this.adapterManager = adapterManager; 175 } 176 177 private AdapterManager getAdapterManager() { 178 return adapterManager; 179 } 180 181 @Override 182 public void setServicesInjector(final ServicesInjector servicesInjector) { 183 this.servicesInjector = servicesInjector; 184 } 185 186 private ServicesInjector getServicesInjector() { 187 return servicesInjector; 188 } 189 190 private RuntimeContext getRuntimeContext() { 191 return runtimeContext; 192 } 193 194 @Override 195 public void setRuntimeContext(RuntimeContext runtimeContext) { 196 this.runtimeContext = runtimeContext; 197 } 198}