1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 package org.apache.myfaces.orchestra.conversation.spring; 21 22 import org.apache.myfaces.orchestra.conversation.Conversation; 23 import org.apache.myfaces.orchestra.conversation.ConversationAccessLifetimeAspect; 24 import org.apache.myfaces.orchestra.conversation.ConversationAspect; 25 import org.apache.myfaces.orchestra.conversation.ConversationContext; 26 import org.apache.myfaces.orchestra.conversation.ConversationTimeoutableAspect; 27 28 /** 29 * Handles creation and lookup of any bean whose bean-definition specifies a scope 30 * that maps to an instance of this type. 31 * <p> 32 * A scope bean handles Spring-specific callbacks in order to locate or create any beans whose definition 33 * specifies that scope. A scope can also be thought of as a "conversation template", as this object 34 * is responsible for creating a conversation when one is needed but does not yet exist. 35 * <p> 36 * <h2>Example</h2> 37 * A sample configuration for a conversation scope with persistence: 38 * <pre> 39 * <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> 40 * <property name="scopes"> 41 * <map> 42 * <entry key="conversation.manual"> 43 * <bean class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope"> 44 * <property name="advices"> 45 * <list> 46 * <ref bean="persistentContextConversationInterceptor" /> 47 * </list> 48 * </property> 49 * </bean> 50 * </entry> 51 * </map> 52 * </property> 53 * </bean> 54 * 55 * 56 * <bean id="persistentContextConversationInterceptor" 57 * class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor"> 58 * <property name="persistenceContextFactory" ref="yourPersistentContextFactory" /> 59 * </bean> 60 * </pre> 61 * <p> 62 * 63 * <h2>Conversation properties</h2> 64 * The following properties can be defined on a scope and then apply to any conversation that is created 65 * to hold a bean of this scope: 66 * <ul> 67 * <li>lifetime: may be "manual" or "access". If not specified, then defaults to "manual".</li> 68 * <li>timeout: a time period (in minutes) after which inactive conversations are terminated. 69 * If not specified, then inactive conversations are never automatically terminated.</li> 70 * </ul> 71 * 72 * <h2>Other Notes</h2> 73 * If the bean definition does not specify a conversation name, then the bean name is used 74 * as the conversation name. 75 * <p> 76 * As shown above, a list of AOP Advices can be specified for the scope, in which case each of the 77 * advices gets configured for any bean declared with this scope. 78 */ 79 public class SpringConversationScope extends AbstractSpringOrchestraScope 80 { 81 /** @deprecated Use LIFETIME_ACCESS instead. */ 82 public static final String LIFETIME_FLASH = "flash"; 83 84 public static final String LIFETIME_ACCESS = "access"; 85 public static final String LIFETIME_MANUAL = "manual"; 86 87 private Integer timeout; 88 private String lifetime = LIFETIME_MANUAL; 89 90 /** 91 * See {@link #setTimeout}. 92 */ 93 public Integer getTimeout() 94 { 95 return timeout; 96 } 97 98 /** 99 * The timeout in minutes when the conversation will end. 100 * See {@link ConversationTimeoutableAspect#timeout} for the default timeout. 101 */ 102 public void setTimeout(Integer timeout) 103 { 104 this.timeout = timeout; 105 } 106 107 /** 108 * See {@link #setLifetime}. 109 */ 110 public String getLifetime() 111 { 112 return lifetime; 113 } 114 115 /** 116 * Must be one of "manual" or "access". 117 * <p> 118 * Defaults to "manual". 119 * <p> 120 * Note that "flash" is also supported as an alias for "access", for 121 * reasons of backwards compatibility with release 1.0. 122 */ 123 public void setLifetime(String lifetime) 124 { 125 // Check for validity here so that an exception gets thrown on startup 126 // rather than when the first bean in this scope is created. 127 if (LIFETIME_FLASH.equals(lifetime)) 128 { 129 this.lifetime = LIFETIME_ACCESS; 130 } 131 else if (LIFETIME_ACCESS.equals(lifetime)) 132 { 133 this.lifetime = LIFETIME_ACCESS; 134 } 135 else if (LIFETIME_MANUAL.equals(lifetime)) 136 { 137 this.lifetime = LIFETIME_MANUAL; 138 } 139 else 140 { 141 throw new IllegalArgumentException("Invalid lifetime:" + lifetime); 142 } 143 } 144 145 /** 146 * Implementation of ConversationFactory interface. 147 */ 148 public Conversation createConversation(ConversationContext context, String name) 149 { 150 Conversation conversation = new Conversation(context, name, this); 151 conversation.setBinder(new SpringConversationBinder(this, conversation)); 152 153 // invoke child scope classes so they can add any aspects they desire. 154 initAspects(conversation); 155 156 return conversation; 157 } 158 159 /** 160 * Add aspects to a newly-created conversation. 161 * <p> 162 * Subclasses are expected to call super.initAspects, then make 163 * zero or more calls to conversation.addAspect. 164 */ 165 protected void initAspects(Conversation conversation) 166 { 167 // conversation timeout 168 if (timeout != null) 169 { 170 long timeoutMsecs = timeout.longValue() * 60L * 1000L; 171 ConversationTimeoutableAspect aspect = new ConversationTimeoutableAspect(conversation); 172 aspect.setTimeout(timeoutMsecs); 173 conversation.addAspect(aspect); 174 } 175 176 // conversation lifetime 177 if (LIFETIME_ACCESS.equals(lifetime)) 178 { 179 ConversationAspect aspect = new ConversationAccessLifetimeAspect(conversation); 180 conversation.addAspect(aspect); 181 } 182 } 183 184 /** 185 * Mark the specified conversation as having been accessed. 186 * <p> 187 * This affects conversation timeouts, and the removal of access-scoped conversations. 188 */ 189 protected void notifyAccessConversation(Conversation conversation) 190 { 191 super.notifyAccessConversation(conversation); 192 193 ConversationAccessLifetimeAspect aspect = (ConversationAccessLifetimeAspect) 194 conversation.getAspect(ConversationAccessLifetimeAspect.class); 195 if (aspect != null) 196 { 197 aspect.markAsAccessed(); 198 } 199 } 200 }