Return-Path: Delivered-To: apmail-myfaces-commits-archive@www.apache.org Received: (qmail 4627 invoked from network); 4 Dec 2009 00:14:50 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 4 Dec 2009 00:14:50 -0000 Received: (qmail 33139 invoked by uid 500); 4 Dec 2009 00:14:50 -0000 Delivered-To: apmail-myfaces-commits-archive@myfaces.apache.org Received: (qmail 33043 invoked by uid 500); 4 Dec 2009 00:14:49 -0000 Mailing-List: contact commits-help@myfaces.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "MyFaces Development" Delivered-To: mailing list commits@myfaces.apache.org Received: (qmail 33034 invoked by uid 99); 4 Dec 2009 00:14:49 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 Dec 2009 00:14:49 +0000 X-ASF-Spam-Status: No, hits=-2.7 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 Dec 2009 00:14:43 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 5082C2388A63; Fri, 4 Dec 2009 00:14:23 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r887008 - in /myfaces/orchestra/trunk/core: ./ src/main/java/org/apache/myfaces/orchestra/annotation/ src/main/java/org/apache/myfaces/orchestra/annotation/spring/ src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ src/mai... Date: Fri, 04 Dec 2009 00:14:22 -0000 To: commits@myfaces.apache.org From: lu4242@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20091204001423.5082C2388A63@eris.apache.org> Author: lu4242 Date: Fri Dec 4 00:14:20 2009 New Revision: 887008 URL: http://svn.apache.org/viewvc?rev=887008&view=rev Log: ORCHESTRA-47 Merge orchestra core15 branch into core, since orchestra will be JDK 1.5 compatible Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfo.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfoManager.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/spring/ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/spring/AnnotationsInfoInitializer.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationName.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationRequire.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AbstractAnnotationsViewControllerManager.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerExecutor.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerManager.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerNameMapper.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/PlainAnnotationsViewControllerManager.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/InitView.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreProcess.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreRenderView.java myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/ViewController.java myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/PersistentBackingBean.java myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/TestConversationPersistence.java myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/model/ myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/model/UserData.java myfaces/orchestra/trunk/core/src/test/resources/META-INF/ myfaces/orchestra/trunk/core/src/test/resources/META-INF/persistence.xml Modified: myfaces/orchestra/trunk/core/pom.xml myfaces/orchestra/trunk/core/src/main/resources/META-INF/spring-orchestra-init.xml myfaces/orchestra/trunk/core/src/test/resources/testApplicationContext.xml Modified: myfaces/orchestra/trunk/core/pom.xml URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/pom.xml?rev=887008&r1=887007&r2=887008&view=diff ============================================================================== --- myfaces/orchestra/trunk/core/pom.xml (original) +++ myfaces/orchestra/trunk/core/pom.xml Fri Dec 4 00:14:20 2009 @@ -94,6 +94,13 @@ + commons-lang + commons-lang + 2.1 + compile + + + commons-logging commons-logging 1.1.1 @@ -122,6 +129,21 @@ 1.0.4 test + + + javax.persistence + toplink-essentials + 1.0 + test + + + + org.apache.derby + derby + 10.3.1.4 + test + + Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfo.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfo.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfo.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfo.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,119 @@ +/* + * 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.myfaces.orchestra.annotation; + +import java.lang.reflect.Method; + +import org.apache.myfaces.orchestra.conversation.annotations.ConversationName; +import org.apache.myfaces.orchestra.conversation.annotations.ConversationRequire; +import org.apache.myfaces.orchestra.viewController.annotations.ViewController; + +/** + * Holds information extracted out of a single bean using its annotations. + */ +public class AnnotationInfo +{ + private final String beanName; + private final Class beanClass; + + private ConversationName conversationName; + private ConversationRequire conversationRequire; + private ViewController viewController; + + // Method-level annotations for viewController callbacks + private Method initViewMethod; + private Method preProcessMethod; + private Method preRenderViewMethod; + + public AnnotationInfo(String beanName, Class beanClass) + { + this.beanName = beanName; + this.beanClass = beanClass; + } + + public String getBeanName() + { + return beanName; + } + + public Class getBeanClass() + { + return beanClass; + } + + public ConversationName getConversationName() + { + return conversationName; + } + + public void setConversationName(ConversationName conversationName) + { + this.conversationName = conversationName; + } + + public ConversationRequire getConversationRequire() + { + return conversationRequire; + } + + public void setConversationRequire(ConversationRequire conversationRequire) + { + this.conversationRequire = conversationRequire; + } + + public ViewController getViewController() + { + return viewController; + } + + public void setViewController(ViewController viewController) + { + this.viewController = viewController; + } + + public Method getInitViewMethod() + { + return initViewMethod; + } + + public void setInitViewMethod(Method initViewMethod) + { + this.initViewMethod = initViewMethod; + } + + public Method getPreProcessMethod() + { + return preProcessMethod; + } + + public void setPreProcessMethod(Method preProcessMethod) + { + this.preProcessMethod = preProcessMethod; + } + + public Method getPreRenderViewMethod() + { + return preRenderViewMethod; + } + + public void setPreRenderViewMethod(Method preRenderViewMethod) + { + this.preRenderViewMethod = preRenderViewMethod; + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfoManager.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfoManager.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfoManager.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/annotation/AnnotationInfoManager.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,140 @@ +/* + * 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.myfaces.orchestra.annotation; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.myfaces.orchestra.conversation.annotations.ConversationName; +import org.apache.myfaces.orchestra.conversation.annotations.ConversationRequire; +import org.apache.myfaces.orchestra.viewController.annotations.InitView; +import org.apache.myfaces.orchestra.viewController.annotations.PreProcess; +import org.apache.myfaces.orchestra.viewController.annotations.PreRenderView; +import org.apache.myfaces.orchestra.viewController.annotations.ViewController; + +/** + * Inspects a class for Orchestra annotations, and if found then caches this information + * for later access. + *

+ * The processing of Class objects is expected to happen only at application startup. + *

+ * Note that annotation scanning is driven by the dependency-injection framework, i.e. + * only classes declared to the framework are scanned. + */ +public class AnnotationInfoManager +{ + private final Log log = LogFactory.getLog(AnnotationInfoManager.class); + + private Map annotationsInfoByName = new HashMap(); + private Map annotationsInfoByViewId = new HashMap(); + + protected void addAnnotationsInfo(AnnotationInfo annotationInfo) + { + if (annotationsInfoByName.containsKey(annotationInfo.getBeanName())) + { + log.info("duplicate bean definition: " + annotationInfo.getBeanName()); + } + + annotationsInfoByName.put(annotationInfo.getBeanName(), annotationInfo); + + ViewController viewController = annotationInfo.getViewController(); + if (viewController != null) + { + String[] viewIds = viewController.viewIds(); + for (int i = 0; i + * Currently the class-level annotations looked for are: + *

    + *
  • ConversationName + *
  • ConversationRequire + *
  • ViewController + *
+ *

+ * If the ViewController annotation is present, then the class is also scanned + * for related annotations on class methods. + */ + public void processBeanAnnotations(String beanName, Class clazz) + { + ConversationName conversationName = (ConversationName) clazz.getAnnotation(ConversationName.class); + ViewController viewController = (ViewController) clazz.getAnnotation(ViewController.class); + ConversationRequire conversationRequire = (ConversationRequire) clazz.getAnnotation(ConversationRequire.class); + + if (conversationName == null && viewController == null && conversationRequire == null) + { + return; + } + + AnnotationInfo annotationInfo = new AnnotationInfo(beanName, clazz); + annotationInfo.setConversationName(conversationName); + annotationInfo.setConversationRequire(conversationRequire); + + if (viewController != null) + { + annotationInfo.setViewController(viewController); + Method[] methods = clazz.getMethods(); + for (int i = 0; i + * Just declaring an instance of this type as a Spring Singleton will cause the + * postProcessBeanFactory to be called passing in info about all the bean + * declarations in the spring context, allowing Orchestra annotations on any + * directly declared class to be discovered and processed. + *

+ * Every class referenced from a bean declaration is then passed to the + * AnnotationInfoManager instance that has been injected into this object. + */ +public class AnnotationsInfoInitializer implements BeanFactoryPostProcessor, Ordered +{ + private Log log = LogFactory.getLog(AnnotationsInfoInitializer.class); + + private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered + + private AnnotationInfoManager annotationInfoManager; + + /** + * Implement the Spring Ordered interface. + */ + public int getOrder() + { + return order; + } + + public void setOrder(int order) + { + this.order = order; + } + + /** + * Inject the object that actually inspects each Class for Orchestra annotations. + */ + public void setAnnotationInfoManager(AnnotationInfoManager annotationInfoManager) + { + this.annotationInfoManager = annotationInfoManager; + } + + /** + * For each bean in the beanFactory, load the appropriate Class object and + * pass it to the annotationInfoManager object for inspection. + */ + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException + { + String[] beanNames = beanFactory.getBeanDefinitionNames(); + for (String beanName : beanNames) + { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + String className = beanDefinition.getBeanClassName(); + if (className != null) + { + Class beanClass = null; + try + { + beanClass = ClassUtils.classForName(className); + } + catch (ClassNotFoundException e) + { + log.debug(e.getLocalizedMessage(), e); + } + + if (beanClass != null) + { + // XXX: Hack to deal with aop:scope-proxy (scopedTarget.) beans + if (!_SpringUtils.isAlternateBeanName(beanName)) + { + // we are not on a scopedTarget ... check if there is one + String alternateBeanName = _SpringUtils.getAlternateBeanName(beanName); + if (beanFactory.containsBeanDefinition(alternateBeanName)) + { + // ... yes, we are just interested in the alternate one. + continue; + } + } + String realBeanName = _SpringUtils.getRealBeanName(beanName); + + // check whether the bean is an orchestra-annotated bean, + // and if so cache its annotation info for later use. + annotationInfoManager.processBeanAnnotations(realBeanName, beanClass); + + // Now deal with any annotation data that must be processed at startup. + AnnotationInfo info = annotationInfoManager + .getAnnotationInfoByBeanName(realBeanName); + if (info != null) + { + processStartupAnnotations(beanDefinition, info); + } + } + } + } + } + + /** + * Handle any annotations on a bean which should be processed on startup. + *

+ * One such annotation is the + * + * @ConversationName annotation, which should modify the beanDefinition + * object, as it is equivalent to putting an + * orchestra:conversationName attribute in the spring bean + * declaration. + */ + private void processStartupAnnotations(BeanDefinition beanDefinition, AnnotationInfo info) + { + ConversationName conversationName = info.getConversationName(); + if (conversationName != null) + { + String convNameFromAnno = conversationName.value(); + if (convNameFromAnno != null && convNameFromAnno.length() > 0) + { + String convNameFromDef = getConversationName(beanDefinition); + if (convNameFromDef == null) + { + setConversationName(beanDefinition, convNameFromAnno); + } + } + } + } + + /** + * Get the orchestra conversationName attribute (if any) from the spring + * bean definition. + */ + // TODO: Move this method into orchestra-core, then call it from here, from + // AbstractSpringOrchestraScope and BeanDefinition*Decorator. This of course + // creates a dependency from this code onto that modified orchestra-core + // version. + private static String getConversationName(BeanDefinition def) + { + return (String) def + .getAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE); + } + + // See getConversationName + private static void setConversationName(BeanDefinition def, String convName) + { + def.setAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE, + convName); + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationName.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationName.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationName.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationName.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,47 @@ +/* + * 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.myfaces.orchestra.conversation.annotations; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation that can be attached to a class in order to set the orchestra + * conversation-name that this bean lives in. + *

+ * When a bean declaration (in whatever dependency-injection framework is being + * used) specifies that this object is in a conversation scope, and no specific + * conversation name is defined, then the class being instantiated is checked + * for this annotation. + *

+ * This is the annotation equivalent of the spring configuration attribute + * orchestra:conversationName. + *

+ * Note that this annotation does not support setting of the bean scope at + * the same time, as that is something that the dependency framework being + * used should provide its own annotations for. + */ +@Target(value = {ElementType.TYPE}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ConversationName +{ + String value() default ""; +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationRequire.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationRequire.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationRequire.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/annotations/ConversationRequire.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,100 @@ +/* + * 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.myfaces.orchestra.conversation.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Can be used to prevent access to a view where the expected bean state + * (ie conversation) does not exist, and redirect instead to another view. + *

+ * This annotation is expected to be applied only to classes that are also + * a ViewController (i.e. have the ViewController annotation applied, or + * use one of the other mechanisms the Orchestra ViewController framework + * supports for mapping views to controller beans). This annotation has no + * effect when applied to a class that is not acting as a view controller. + *

+ * When a workflow passes through a number of different views, all views + * except the first one will expect a conversation to exist, with the + * beans in that conversation holding appropriate state. If the + * conversation does not exist then the view will fail to work correctly. + * Possible causes for this kind of invalid state include: + *

    + *
  • A user selecting a bookmark + *
  • A user directly manipulating a URL + *
  • A conversation expiring + *
+ *

+ * To resolve these issues, a bean can use the Orchestra ViewController + * to configure itself as being associated with specific views, then + * use this ConversationRequire annotation to declare what conversation(s) + * it expects to exist. When an associated view is activated and the + * conversation does not exist then a redirect immediately occurs to the + * specified page - which is usually set to the first page in the relevant + * workflow. + */ +@Target(value = {ElementType.TYPE}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ConversationRequire +{ + /** + * One or many conversation names the view require as prerequesite. + * If one of the configured conversations is not active a redirect or + * navigationAction will be issued. + */ + String[] conversationNames(); + + /** + * The servlet url to redirect to if one of the conversations is not running. + * Use either this or navigationAction. + */ + String redirect() default ""; + + /** + * The logical navigation rule to execute if one of the conversations is not running. + *

+ * In a JSF environment, the specified name must be configured as as a global navigation + * rule in the application's faces-config.xml file. + */ + String navigationAction() default ""; + + /** + * A list of viewIds which cause this annotation to be ignored. If this is defined, + * and the current viewId matches any of the specified viewIds, then no checks for + * active conversations is done. + *

+ * A ViewController with this annotation may be referenced from multiple views, eg + * in a "wizard" type page-flow. In this case, certain conversations are expected + * to exist in all pages except the first, but for the first ("entry") page of the + * wizard, they will not exist and the check should not be done. In this scenario, + * the viewId of the "entry" page can be added to the ignoredViews property of + * the annotation. + *

+ * Note that this property does require java code to contain explicit viewIds, + * which causes tight coupling between view "templates" and java code. However + * this property is not mandatory; an alternative is to have just a single + * ViewController per view, in which case this is not needed. State that needs + * to be shared between views can be on a separate bean. + */ + String[] entryPointViewIds() default ""; +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AbstractAnnotationsViewControllerManager.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AbstractAnnotationsViewControllerManager.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AbstractAnnotationsViewControllerManager.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AbstractAnnotationsViewControllerManager.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,147 @@ +/* + * 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.myfaces.orchestra.viewController; + +import org.apache.commons.lang.StringUtils; +import org.apache.myfaces.orchestra.annotation.AnnotationInfo; +import org.apache.myfaces.orchestra.annotation.AnnotationInfoManager; +import org.apache.myfaces.orchestra.conversation.ConversationManager; +import org.apache.myfaces.orchestra.conversation.annotations.ConversationRequire; +import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter; +import org.apache.myfaces.orchestra.lib.OrchestraException; + +import java.io.IOException; + +/** + * A {@link org.apache.myfaces.orchestra.viewController.ViewControllerManager} implementation which uses + * annotations on backing beans to determine the beans responsible for a given view and execute + * the appropriate annotated methods. + * + *

When using Spring, every bean declaration in the spring config files is checked to see if the + * referenced class has annotations, and if so that information is cached. Here, that information is + * then used to locate a bean which has a ViewController annotation that references the current view.

+ * + *

See also org.apache.myfaces.orchestra.viewController.annotations.*.

+ * + * @see org.apache.myfaces.orchestra.viewController.ViewControllerManager + */ +public abstract class AbstractAnnotationsViewControllerManager extends AbstractViewControllerManager +{ + private AnnotationInfoManager annotationInfoManager; + + public AbstractAnnotationsViewControllerManager() + { + } + + public abstract void initManager(); + + public void setAnnotationInfoManager(AnnotationInfoManager annotationInfoManager) + { + this.annotationInfoManager = annotationInfoManager; + } + + public AnnotationInfoManager getAnnotationInfoManager() + { + return annotationInfoManager; + } + + public void assertConversationState(String viewId) + { + String beanName = getViewControllerNameMapper().mapViewId(viewId); + if (beanName == null) + { + return; + } + + AnnotationInfo annotationInfo = annotationInfoManager.getAnnotationInfoByBeanName(beanName); + if (annotationInfo == null) + { + return; + } + + ConversationRequire conversationRequire = annotationInfo.getConversationRequire(); + if (conversationRequire == null) + { + return; + } + + // check if we are on an ignored view + String[] entryPoints = conversationRequire.entryPointViewIds(); + if (entryPoints != null) + { + for(String ignoredView: entryPoints) + { + if (!StringUtils.isEmpty(ignoredView) && ignoredView.equals(viewId)) + { + return; + } + } + } + + // check that all the required conversations already exist + ConversationManager manager = ConversationManager.getInstance(); + String[] conversationNames = conversationRequire.conversationNames(); + if (conversationNames != null) + { + for(String conversationName: conversationNames) + { + if (!manager.hasConversation(conversationName)) + { + reportInactive(manager, conversationName, conversationRequire); + return; + } + } + } + } + + private void reportInactive(ConversationManager manager, String conversationName, + ConversationRequire conversationRequire) + { + // oops..handle failure + if (manager.getMessager() != null) + { + manager.getMessager().setConversationNotActive(conversationName); + } + + if (!StringUtils.isEmpty(conversationRequire.redirect())) + { + try + { + FrameworkAdapter.getCurrentInstance().redirect(conversationRequire.redirect()); + } + catch (IOException e) + { + throw new OrchestraException(e); + } + } + else if (!StringUtils.isEmpty(conversationRequire.navigationAction())) + { + try + { + String dst = conversationRequire.navigationAction(); + FrameworkAdapter.getCurrentInstance().invokeNavigation(dst); + } + catch (IOException e) + { + throw new OrchestraException(e); + } + } + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerExecutor.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerExecutor.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerExecutor.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerExecutor.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,101 @@ +/* + * 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.myfaces.orchestra.viewController; + +import org.apache.myfaces.orchestra.annotation.AnnotationInfo; +import org.apache.myfaces.orchestra.annotation.AnnotationInfoManager; +import org.apache.myfaces.orchestra.lib.OrchestraException; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Execute the various viewController events on the viewController by calling + * the corresponding annotated method. + */ +public class AnnotationsViewControllerExecutor extends AbstractViewControllerExecutor +{ + private final AnnotationInfoManager annotationInfoManager; + + public AnnotationsViewControllerExecutor(AnnotationInfoManager annotationInfoManager) + { + this.annotationInfoManager = annotationInfoManager; + } + + protected void invokeMethod(Object bean, Method method) + { + try + { + method.invoke(bean, (Object[]) null); + } + catch (IllegalAccessException e) + { + throw new OrchestraException(e); + } + catch (InvocationTargetException e) + { + throw new OrchestraException(e); + } + } + + public boolean invokeInitView(String beanName, Object bean) + { + AnnotationInfo annotationsInfo = annotationInfoManager.getAnnotationInfoByBeanName(beanName); + if (annotationsInfo != null) + { + Method method = annotationsInfo.getInitViewMethod(); + if (method != null) + { + invokeMethod(bean, method); + return true; + } + } + return false; + } + + public boolean invokePreRenderView(String beanName, Object bean) + { + AnnotationInfo annotationsInfo = annotationInfoManager.getAnnotationInfoByBeanName(beanName); + if (annotationsInfo != null) + { + Method method = annotationsInfo.getPreRenderViewMethod(); + if (method != null) + { + invokeMethod(bean, method); + return true; + } + } + return false; + } + + public boolean invokePreProcess(String beanName, Object bean) + { + AnnotationInfo annotationsInfo = annotationInfoManager.getAnnotationInfoByBeanName(beanName); + if (annotationsInfo != null) + { + Method method = annotationsInfo.getPreProcessMethod(); + if (method != null) + { + invokeMethod(bean, method); + return true; + } + } + return false; + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerManager.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerManager.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerManager.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerManager.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,83 @@ +/* + * 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.myfaces.orchestra.viewController; + +/** + * A {@link org.apache.myfaces.orchestra.viewController.ViewControllerManager} implementation which uses + * annotations on backing beans to determine the beans responsible for a given view and execute + * the appropriate annotated methods. + * + *

When using Spring, every bean declaration in the spring config files is checked to see if the + * referenced class has annotations, and if so that information is cached. Here, that information is + * then used to locate a bean which has a ViewController annotation that references the current view.

+ * + *

See also org.apache.myfaces.orchestra.viewController.annotations.*.

+ * + *

+ * Notice: For backward compatiblity with the Orchestra-core module this ViewControllerManager + * will take the Orchestra-core naming strategy + * into account and uses reflection if no annotated method were found. + *

+ * + * @see org.apache.myfaces.orchestra.viewController.ViewControllerManager + * @see org.apache.myfaces.orchestra.viewController.PlainAnnotationsViewControllerManager + */ +public class AnnotationsViewControllerManager extends AbstractAnnotationsViewControllerManager +{ + private ViewControllerNameMapper viewControllerNameMapper; + private ViewControllerExecutor viewControllerExecutor; + + public AnnotationsViewControllerManager() + { + } + + public void initManager() + { + // Set things up so that finding a bean-name for a viewId looks first using an + // AnnotationsViewControllerNameMapper, then a DefaultViewControllerNameMapper. + viewControllerNameMapper = + new CompositeViewControllerNameMapper( + new ViewControllerNameMapper[] + { + new AnnotationsViewControllerNameMapper(getAnnotationInfoManager()), + new DefaultViewControllerNameMapper() + }); + + // Set things up so that executing a method first using an + // AnnotationsViewControllerExecutor, then a ReflectiveViewControllerExecutor. + viewControllerExecutor = + new CompositeViewControllerExecutor( + new ViewControllerExecutor[] + { + new AnnotationsViewControllerExecutor(getAnnotationInfoManager()), + new ReflectiveViewControllerExecutor() + }); + } + + protected ViewControllerNameMapper getViewControllerNameMapper() + { + return viewControllerNameMapper; + } + + protected ViewControllerExecutor getViewControllerExecutor() + { + return viewControllerExecutor; + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerNameMapper.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerNameMapper.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerNameMapper.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/AnnotationsViewControllerNameMapper.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,49 @@ +/* + * 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.myfaces.orchestra.viewController; + +import org.apache.myfaces.orchestra.annotation.AnnotationInfo; +import org.apache.myfaces.orchestra.annotation.AnnotationInfoManager; + +/** + * Lookup a bean configured using the ViewController annotation which is responsible + * for the given viewId. + * + * @see org.apache.myfaces.orchestra.viewController.annotations.ViewController + */ +public class AnnotationsViewControllerNameMapper implements ViewControllerNameMapper +{ + private final AnnotationInfoManager annotationInfoManager; + + public AnnotationsViewControllerNameMapper(AnnotationInfoManager annotationInfoManager) + { + this.annotationInfoManager = annotationInfoManager; + } + + public String mapViewId(String viewId) + { + AnnotationInfo annotationsInfo = annotationInfoManager.getAnnotationInfoByViewId(viewId); + if (annotationsInfo != null) + { + return annotationsInfo.getBeanName(); + } + + return null; + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/PlainAnnotationsViewControllerManager.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/PlainAnnotationsViewControllerManager.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/PlainAnnotationsViewControllerManager.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/PlainAnnotationsViewControllerManager.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,67 @@ +/* + * 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.myfaces.orchestra.viewController; + +/** + * A {@link ViewControllerManager} implementation which uses + * annotations on backing beans to determine the beans responsible for a given view and execute + * the appropriate annotated methods. + * + *

When using Spring, every bean declaration in the spring config files is checked to see if the + * referenced class has annotations, and if so that information is cached. Here, that information is + * then used to locate a bean which has a ViewController annotation that references the current view.

+ * + *

See also org.apache.myfaces.orchestra.viewController.annotations.*.

+ * + * @see ViewControllerManager + */ + +// TODO: add a feature to the core that allows (view, beanname) pairs to be added to a list +// held by a normal ViewController. A ViewController annotation is then just one of the +// ways in which a bean's name can be added to the list. Maybe also add a map of (event->Method) +// that can be added, so that method annotations are then just another way of configuring these +// method mappings? Watch out for environments that allow hot-deploy/replace though! +// +// TODO: how can this be configured by an application? +public class PlainAnnotationsViewControllerManager extends AbstractAnnotationsViewControllerManager +{ + private ViewControllerNameMapper viewControllerNameMapper; + private ViewControllerExecutor viewControllerExecutor; + + public PlainAnnotationsViewControllerManager() + { + } + + public void initManager() + { + viewControllerNameMapper = new AnnotationsViewControllerNameMapper(getAnnotationInfoManager()); + viewControllerExecutor = new AnnotationsViewControllerExecutor(getAnnotationInfoManager()); + } + + protected ViewControllerNameMapper getViewControllerNameMapper() + { + return viewControllerNameMapper; + } + + protected ViewControllerExecutor getViewControllerExecutor() + { + return viewControllerExecutor; + } +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/InitView.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/InitView.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/InitView.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/InitView.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,43 @@ +/* + * 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.myfaces.orchestra.viewController.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Mark a method as needing to be invoked by the ViewController. + *

+ * This method will always be called before any other method is invoked + * on any backing bean for the current request. It is invoked once for each + * request. + *

+ * This annotation will be ignored unless the class is also marked with the + * ViewController annotation. + *

+ * See also: org.apache.myfaces.orchestra.viewController.ViewController + */ +@Target(value = {ElementType.METHOD}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface InitView +{ +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreProcess.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreProcess.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreProcess.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreProcess.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,49 @@ +/* + * 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.myfaces.orchestra.viewController.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Marks a method as needing to be invoked by the ViewController. + *

+ * This method will be invoked before any "action" callbacks related to buttons, + * links, etc. are invoked on backing beans for the current request. + *

+ * For JSF, there is an exception: for command components marked as "immediate", + * the associated action method is invoked before this callback occurs. If that + * method then performs navigation then this callback will not occur at all. + *

+ * Note also that for JSF, if validation failures occur then this callback will + * not be invoked (as actions are skipped). + *

+ * This annotation will be ignored unless the class is also marked with the + * ViewController annotation. + *

+ * See also: org.apache.myfaces.orchestra.viewController.ViewController + */ +@Target(value = {ElementType.METHOD}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface PreProcess +{ +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreRenderView.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreRenderView.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreRenderView.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/PreRenderView.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,41 @@ +/* + * 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.myfaces.orchestra.viewController.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Marks a method as needing to be invoked by the ViewController. + *

+ * This method will be invoked just before starting to render output to the user. + *

+ * This annotation will be ignored unless the class is also marked with the + * ViewController annotation. + *

+ * See also: org.apache.myfaces.orchestra.viewController.ViewController + */ +@Target(value = { ElementType.METHOD }) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface PreRenderView +{ +} Added: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/ViewController.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/ViewController.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/ViewController.java (added) +++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/viewController/annotations/ViewController.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,52 @@ +/* + * 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.myfaces.orchestra.viewController.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Defines a class as ViewController to receive ViewController events for + * the given viewId(s). + *

+ * This requires that the Orchestra ViewController is active, and that the + * AnnotationsViewControllerNameMapper is in use. + *

+ * This annotation is an alternative to using the "bean name matches viewid" + * approach of the DefaultViewControllerNameMapper. + *

+ * Whether this is the best approach to pairing a view with a controller bean + * depends upon the project. This does cause tight coupling between bean and + * view; moving a view requires modifying the bean. This is not appropriate for + * a project where "ui designer" and "code developer" roles are separated. However + * for other projects where the same person plays both roles then using this + * annotation reduces the amount of configuration required. + */ +@Target(value = {ElementType.TYPE}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ViewController +{ + /** + * the ViewIds the bean is responsible for + */ + String[] viewIds() default {}; +} Modified: myfaces/orchestra/trunk/core/src/main/resources/META-INF/spring-orchestra-init.xml URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/resources/META-INF/spring-orchestra-init.xml?rev=887008&r1=887007&r2=887008&view=diff ============================================================================== --- myfaces/orchestra/trunk/core/src/main/resources/META-INF/spring-orchestra-init.xml (original) +++ myfaces/orchestra/trunk/core/src/main/resources/META-INF/spring-orchestra-init.xml Fri Dec 4 00:14:20 2009 @@ -45,4 +45,31 @@ - \ No newline at end of file + + + + + + + + + + + + + + + Added: myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/PersistentBackingBean.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/PersistentBackingBean.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/PersistentBackingBean.java (added) +++ myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/PersistentBackingBean.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,142 @@ +/* + * 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.myfaces.orchestra.conversation; + +import org.apache.myfaces.orchestra.conversation.model.UserData; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +/** + * A backing bean for a view (ie a "view controller") that has an associated + * persistence context. + *

+ * In real applications, the PersistenceContext is more likely to be on a + * DAO bean referenced from the backing bean than directly on the backing + * bean class. However that doesn't change the principles. + */ +public class PersistentBackingBean implements ConversationBindingListener +{ + /** + * Inject a PersistenceContext that is associated with the conversation + * that this bean instance is in. + */ + @PersistenceContext + private EntityManager entityManager; + + /** + * A UserData instance read via the PersistenceContext object associated + * with this bean, ie one associated with the conversation that this bean + * is in. + */ + private UserData restartedUser; + + private UserData createdUser; + + public PersistentBackingBean() + { + } + + public void valueBound(ConversationBindingEvent event) + { + } + + public void valueUnbound(ConversationBindingEvent event) + { + } + + public UserData getRestartedUser() + { + return restartedUser; + } + + /** + * Create a UserData object and commit it to the database. + *

+ * Because this method is marked with the Transactional annotation, a + * commit will be performed when the method returns successfully + * (without exception), and a rollback will be performed when the + * method throws an exception. + */ + @Transactional + public UserData createUser() + { + // Create an object that is not yet attached to a persistence context. + UserData userData = new UserData(); + userData.setUsername("test"); + + // Attach the object to the persistence context. Note that nothing + // is written to the database at this point; that only happens when + // the persistence context is flushed and committed. Due to the + // Transactional annotation on this method, that happens when this + // method returns. + entityManager.persist(userData); + + createdUser = userData; + + return userData; + } + + public UserData readUser(Long id) + { + return entityManager.find(UserData.class, id); + } + + /** + * Invalidate the conversation in which this bean lives. + *

+ * This means that the next attempt to dereference a proxy or + * EL expression will cause a new instance of this bean to + * be created. A new Conversation instance will be created to + * hold that bean, and a new PersistenceContext object will + * also be created (and then injected into this instance). + */ + public void invalidateConversation() + { + Conversation.getCurrentInstance().invalidate(); + } + + /** + * Invalidate the conversation in which this bean lives, and + * then create a new bean instance (in a new conversation) and + * copy some data from the old to the new instance. + *

+ * Using invalidateAndRestart allows information to be communicated + * between one instance of this bean and its replacement. + */ + public void invalidateAndRestartConversation() + { + PersistentBackingBean conv = (PersistentBackingBean) + ConversationUtils.invalidateAndRestartCurrent(); + conv.setRestartedUser(createdUser.getId()); + } + + public void setRestartedUser(Long id) + { + restartedUser = readUser(id); + } + + public void updateUser(Long id, String username) + { + UserData user = readUser(id); + user.setUsername(username); + } +} Added: myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/TestConversationPersistence.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/TestConversationPersistence.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/TestConversationPersistence.java (added) +++ myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/TestConversationPersistence.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,101 @@ +/* + * 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.myfaces.orchestra.conversation; + +import org.apache.myfaces.orchestra.conversation.basic.LogConversationMessager; +import org.apache.myfaces.orchestra.conversation.model.UserData; +import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter; +import org.apache.myfaces.orchestra.frameworkAdapter.local.LocalFrameworkAdapter; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; + +/** + * Test various aspects of the conversation handling + */ +public class TestConversationPersistence extends AbstractDependencyInjectionSpringContextTests +{ + protected String[] getConfigLocations() + { + return new String[] + { + "classpath:testApplicationContext.xml" + }; + } + + protected void onSetUp() throws Exception + { + super.onSetUp(); + + LocalFrameworkAdapter frameworkAdapter = new LocalFrameworkAdapter(); + frameworkAdapter.setApplicationContext(applicationContext); + frameworkAdapter.setConversationMessager(new LogConversationMessager()); + FrameworkAdapter.setCurrentInstance(frameworkAdapter); + } + + public void testPersistence() + { + final String BEAN_NAME = "persistentBackingBean"; + + PersistentBackingBean conv = (PersistentBackingBean) applicationContext.getBean(BEAN_NAME); + + /* create and reread a user */ + UserData user = conv.createUser(); + UserData user2 = conv.readUser(user.getId()); + + assertSame("has to be the same user",user , user2); + + conv.invalidateConversation(); + + /* restart conversatin */ + UserData user3 = conv.readUser(user.getId()); + + assertNotSame("should not be the same user", user ,user3); + } + + public void testRestartConversation() + { + final String BEAN_NAME = "persistentBackingBean"; + + PersistentBackingBean bean = (PersistentBackingBean) applicationContext.getBean(BEAN_NAME); + + UserData user = bean.createUser(); + + bean.invalidateAndRestartConversation(); + + /* here we access the new conversation */ + UserData restartedUser = bean.getRestartedUser(); + + assertNotNull("should have got a user", restartedUser); + assertNotSame("should not be the same user", user, restartedUser); + assertEquals("has to be the same user id", user.getId(), restartedUser.getId()); + + /* end all conversations*/ + ConversationManager.getInstance().clearCurrentConversationContext(); + + bean.updateUser(user.getId(), "test2"); + + /* invalidate conversation */ + bean.invalidateConversation(); + + UserData user4 = bean.readUser(user.getId()); + + assertNotNull(user4); + assertEquals(user.getId(), user4.getId()); + assertEquals("test", user4.getUsername()); + } +} Added: myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/model/UserData.java URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/model/UserData.java?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/model/UserData.java (added) +++ myfaces/orchestra/trunk/core/src/test/java/org/apache/myfaces/orchestra/conversation/model/UserData.java Fri Dec 4 00:14:20 2009 @@ -0,0 +1,69 @@ +/* + * 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.myfaces.orchestra.conversation.model; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; + +@Entity +public class UserData +{ + @Id + @GeneratedValue(strategy= GenerationType.IDENTITY) + private Long id; + + private String username; + + @Version + private Long version; + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public Long getVersion() + { + return version; + } + + public void setVersion(Long version) + { + this.version = version; + } +} Added: myfaces/orchestra/trunk/core/src/test/resources/META-INF/persistence.xml URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/test/resources/META-INF/persistence.xml?rev=887008&view=auto ============================================================================== --- myfaces/orchestra/trunk/core/src/test/resources/META-INF/persistence.xml (added) +++ myfaces/orchestra/trunk/core/src/test/resources/META-INF/persistence.xml Fri Dec 4 00:14:20 2009 @@ -0,0 +1,29 @@ + + + + + + + org.apache.myfaces.orchestra.conversation.model.UserData + + Modified: myfaces/orchestra/trunk/core/src/test/resources/testApplicationContext.xml URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/test/resources/testApplicationContext.xml?rev=887008&r1=887007&r2=887008&view=diff ============================================================================== --- myfaces/orchestra/trunk/core/src/test/resources/testApplicationContext.xml (original) +++ myfaces/orchestra/trunk/core/src/test/resources/testApplicationContext.xml Fri Dec 4 00:14:20 2009 @@ -32,6 +32,15 @@ + + + + + + + + + @@ -46,10 +55,53 @@ + + + + + + + + + + + + + + + + + + + + + FINE + org.apache.derby.jdbc.EmbeddedDriver + jdbc:derby:myfacesOrchestraDB/TESTS;create=true + sa + foobar + oracle.toplink.essentials.platform.database.DerbyPlatform + drop-and-create-tables + + + + + + + + - \ No newline at end of file +