| CODENOTIFIER | HelpYou are not signed inSign in |
Project: Seam
Revision: 8919
Author: pete.muir@jboss.org
Date: 05 Sep 2008 06:49:51
Changes:Slight refactor of MEII, add logging
Files:| ... | ...@@ -1,73 +0,0 @@ | |
| 1 | package org.jboss.seam.persistence; | |
| 2 | ||
| 3 | import static org.jboss.seam.ScopeType.CONVERSATION; | |
| 4 | ||
| 5 | import org.jboss.seam.annotations.intercept.AroundInvoke; | |
| 6 | import org.jboss.seam.annotations.intercept.Interceptor; | |
| 7 | import org.jboss.seam.core.BijectionInterceptor; | |
| 8 | import org.jboss.seam.intercept.AbstractInterceptor; | |
| 9 | import org.jboss.seam.intercept.InvocationContext; | |
| 10 | import org.jboss.seam.transaction.Transaction; | |
| 11 | ||
| 12 | /** | |
| 13 | * Swizzles entity references around each invocation, maintaining referential | |
| 14 | * integrity even across passivation of the stateful bean or Seam-managed | |
| 15 | * extended persistence context, and allowing for more efficient replication. | |
| 16 | * | |
| 17 | * @author Gavin King | |
| 18 | * | |
| 19 | */ | |
| 20 | @Interceptor(around = BijectionInterceptor.class) | |
| 21 | public class ManagedEntityIdentityInterceptor extends AbstractInterceptor | |
| 22 | { | |
| 23 | ||
| 24 | private static ManagedEntityStateManager managedEntityStateManager = new ManagedEntityStateManager(); | |
| 25 | ||
| 26 | private boolean reentrant; | |
| 27 | ||
| 28 | // TODO: cache the non-ignored fields, probably on Component | |
| 29 | ||
| 30 | public boolean isInterceptorEnabled() | |
| 31 | { | |
| 32 | return getComponent().getScope() == CONVERSATION; | |
| 33 | } | |
| 34 | ||
| 35 | @AroundInvoke | |
| 36 | public Object aroundInvoke(InvocationContext ctx) throws Exception | |
| 37 | { | |
| 38 | if (reentrant) | |
| 39 | { | |
| 40 | return ctx.proceed(); | |
| 41 | } | |
| 42 | else | |
| 43 | { | |
| 44 | reentrant = true; | |
| 45 | managedEntityStateManager.entityIdsToRefs(ctx.getTarget(), getComponent()); | |
| 46 | try | |
| 47 | { | |
| 48 | return ctx.proceed(); | |
| 49 | } | |
| 50 | finally | |
| 51 | { | |
| 52 | if (!isTransactionRolledBackOrMarkedRollback()) | |
| 53 | { | |
| 54 | managedEntityStateManager.entityRefsToIds(ctx.getTarget(), getComponent()); | |
| 55 | reentrant = false; | |
| 56 | } | |
| 57 | } | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | private static boolean isTransactionRolledBackOrMarkedRollback() | |
| 62 | { | |
| 63 | try | |
| 64 | { | |
| 65 | return Transaction.instance().isRolledBackOrMarkedRollback(); | |
| 66 | } | |
| 67 | catch (Exception e) | |
| 68 | { | |
| 69 | return false; | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | } |
| ... | ...@@ -17,7 +17,7 @@ | |
| 17 | 17 | * id and persistence context name. |
| 18 | 18 | * |
| 19 | 19 | * @see EntityBean |
| 20 | * @see org.jboss.seam.persistence.ManagedEntityIdentityInterceptor | |
| 20 | * @see org.jboss.seam.persistence.ManagedEntityInterceptor | |
| 21 | 21 | * |
| 22 | 22 | * @author Gavin King |
| 23 | 23 | * |
| ... | ...@@ -1,162 +0,0 @@ | |
| 1 | package org.jboss.seam.persistence; | |
| 2 | ||
| 3 | import static org.jboss.seam.util.JSF.DATA_MODEL; | |
| 4 | import static org.jboss.seam.util.JSF.getWrappedData; | |
| 5 | import static org.jboss.seam.util.JSF.setWrappedData; | |
| 6 | ||
| 7 | import java.lang.reflect.Field; | |
| 8 | import java.lang.reflect.Modifier; | |
| 9 | import java.util.List; | |
| 10 | import java.util.Map; | |
| 11 | import java.util.Set; | |
| 12 | ||
| 13 | import org.jboss.seam.Component; | |
| 14 | import org.jboss.seam.Seam; | |
| 15 | import org.jboss.seam.annotations.In; | |
| 16 | import org.jboss.seam.contexts.Contexts; | |
| 17 | import org.jboss.seam.util.Reflections; | |
| 18 | ||
| 19 | /** | |
| 20 | * @author Gavin King | |
| 21 | * @author Pete Muir | |
| 22 | * @author Norman Richards | |
| 23 | * | |
| 24 | */ | |
| 25 | public class ManagedEntityStateManager | |
| 26 | { | |
| 27 | ||
| 28 | public void entityRefsToIds(Object controllerBean, Component component) throws Exception | |
| 29 | { | |
| 30 | if ( touchedContextsExist() ) | |
| 31 | { | |
| 32 | Class beanClass = controllerBean.getClass(); | |
| 33 | for (; beanClass!=Object.class; beanClass=beanClass.getSuperclass()) | |
| 34 | { | |
| 35 | for ( Field field: beanClass.getDeclaredFields() ) | |
| 36 | { | |
| 37 | if ( !ignore(field) ) | |
| 38 | { | |
| 39 | Object value = getFieldValue(controllerBean, field); | |
| 40 | if (value!=null) | |
| 41 | { | |
| 42 | Object dataModel = null; | |
| 43 | if ( DATA_MODEL.isInstance(value) ) | |
| 44 | { | |
| 45 | dataModel = value; | |
| 46 | value = getWrappedData(dataModel); | |
| 47 | } | |
| 48 | if ( isRef(value) ) | |
| 49 | { | |
| 50 | saveWrapper(controllerBean, component, field, dataModel, value); | |
| 51 | } | |
| 52 | else | |
| 53 | { | |
| 54 | clearWrapper(component, field); | |
| 55 | } | |
| 56 | } | |
| 57 | else | |
| 58 | { | |
| 59 | clearWrapper(component, field); | |
| 60 | } | |
| 61 | } | |
| 62 | } | |
| 63 | } | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | public void entityIdsToRefs(Object controllerBean, Component component) throws Exception | |
| 68 | { | |
| 69 | if ( touchedContextsExist() ) | |
| 70 | { | |
| 71 | Class beanClass = controllerBean.getClass(); | |
| 72 | for (; beanClass!=Object.class; beanClass=beanClass.getSuperclass()) | |
| 73 | { | |
| 74 | for ( Field field: beanClass.getDeclaredFields() ) | |
| 75 | { | |
| 76 | if ( !ignore(field) ) | |
| 77 | { | |
| 78 | Object value = getFieldValue(controllerBean, field); | |
| 79 | Object dataModel = null; | |
| 80 | if (value!=null && DATA_MODEL.isInstance(value) ) | |
| 81 | { | |
| 82 | dataModel = value; | |
| 83 | } | |
| 84 | //TODO: be more selective | |
| 85 | getFromWrapper(controllerBean, component, field, dataModel); | |
| 86 | } | |
| 87 | } | |
| 88 | } | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | private boolean isRef(Object value) | |
| 93 | { | |
| 94 | //TODO: could do better by checking if the | |
| 95 | // collection really contains an entity | |
| 96 | return value instanceof List || | |
| 97 | value instanceof Map || | |
| 98 | value instanceof Set || | |
| 99 | Seam.getEntityClass(value.getClass()) != null; | |
| 100 | } | |
| 101 | ||
| 102 | private Object getFieldValue(Object bean, Field field) throws Exception | |
| 103 | { | |
| 104 | if ( !field.isAccessible() ) field.setAccessible(true); | |
| 105 | Object value = Reflections.get(field, bean); | |
| 106 | return value; | |
| 107 | } | |
| 108 | ||
| 109 | private boolean ignore(Field field) | |
| 110 | { | |
| 111 | return Modifier.isTransient( field.getModifiers() ) || | |
| 112 | Modifier.isStatic( field.getModifiers() ) | |
| 113 | || field.isAnnotationPresent(In.class); | |
| 114 | } | |
| 115 | ||
| 116 | private boolean touchedContextsExist() | |
| 117 | { | |
| 118 | PersistenceContexts touchedContexts = PersistenceContexts.instance(); | |
| 119 | return touchedContexts!=null && touchedContexts.getTouchedContexts().size()>0; | |
| 120 | } | |
| 121 | ||
| 122 | private String getFieldId(Component component, Field field) | |
| 123 | { | |
| 124 | return component.getName() + '.' + field.getName(); | |
| 125 | } | |
| 126 | ||
| 127 | private void saveWrapper(Object bean, Component component, Field field, Object dataModel, Object value) throws Exception | |
| 128 | { | |
| 129 | Contexts.getConversationContext().set( getFieldId(component, field), value ); | |
| 130 | if (dataModel==null) | |
| 131 | { | |
| 132 | Reflections.set(field, bean, null); | |
| 133 | } | |
| 134 | else | |
| 135 | { | |
| 136 | setWrappedData(dataModel, null); | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | private void clearWrapper(Component component, Field field) throws Exception | |
| 141 | { | |
| 142 | Contexts.getConversationContext().remove( getFieldId(component, field) ); | |
| 143 | } | |
| 144 | ||
| 145 | private void getFromWrapper(Object bean, Component component, Field field, Object dataModel) throws Exception | |
| 146 | { | |
| 147 | Object value =Contexts.getConversationContext().get( getFieldId(component, field) ); | |
| 148 | if (value!=null) | |
| 149 | { | |
| 150 | if (dataModel==null) | |
| 151 | { | |
| 152 | Reflections.set(field, bean, value); | |
| 153 | } | |
| 154 | else | |
| 155 | { | |
| 156 | setWrappedData(dataModel, value); | |
| 157 | } | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | } |
| ... | ...@@ -0,0 +1,91 @@ | |
| 1 | package org.jboss.seam.persistence; | |
| 2 | ||
| 3 | import static org.jboss.seam.ScopeType.CONVERSATION; | |
| 4 | ||
| 5 | import java.io.Serializable; | |
| 6 | ||
| 7 | import org.jboss.seam.Component; | |
| 8 | import org.jboss.seam.ComponentType; | |
| 9 | import org.jboss.seam.annotations.intercept.AroundInvoke; | |
| 10 | import org.jboss.seam.annotations.intercept.Interceptor; | |
| 11 | import org.jboss.seam.annotations.intercept.PostActivate; | |
| 12 | import org.jboss.seam.annotations.intercept.PrePassivate; | |
| 13 | import org.jboss.seam.contexts.Contexts; | |
| 14 | import org.jboss.seam.core.BijectionInterceptor; | |
| 15 | import org.jboss.seam.core.Conversation; | |
| 16 | import org.jboss.seam.intercept.AbstractInterceptor; | |
| 17 | import org.jboss.seam.intercept.InvocationContext; | |
| 18 | import org.jboss.seam.log.LogProvider; | |
| 19 | import org.jboss.seam.log.Logging; | |
| 20 | import org.jboss.seam.transaction.Transaction; | |
| 21 | import org.jboss.seam.web.Session; | |
| 22 | ||
| 23 | /** | |
| 24 | * Swizzles entity references around each invocation, maintaining referential | |
| 25 | * integrity even across passivation of the stateful bean or Seam-managed | |
| 26 | * extended persistence context, and allowing for more efficient replication. | |
| 27 | * | |
| 28 | * @author Gavin King | |
| 29 | * @author Pete Muir | |
| 30 | * | |
| 31 | */ | |
| 32 | @Interceptor(around = BijectionInterceptor.class) | |
| 33 | public class ManagedEntityInterceptor extends AbstractInterceptor | |
| 34 | { | |
| 35 | ||
| 36 | private static LogProvider log = Logging.getLogProvider(ManagedEntityInterceptor.class); | |
| 37 | ||
| 38 | private static ManagedEntityWrapper managedEntityWrapper = new ManagedEntityWrapper(); | |
| 39 | ||
| 40 | private boolean reentrant; | |
| 41 | ||
| 42 | @AroundInvoke | |
| 43 | public Object aroundInvoke(InvocationContext ctx) throws Exception | |
| 44 | { | |
| 45 | if (reentrant) | |
| 46 | { | |
| 47 | return ctx.proceed(); | |
| 48 | } | |
| 49 | else | |
| 50 | { | |
| 51 | reentrant = true; | |
| 52 | log.trace("Attempting to activate " + getComponent().getName() + " component"); | |
| 53 | managedEntityWrapper.deserialize(ctx.getTarget(), getComponent()); | |
| 54 | log.debug("Activated " + getComponent().getName() + " component"); | |
| 55 | try | |
| 56 | { | |
| 57 | return ctx.proceed(); | |
| 58 | } | |
| 59 | finally | |
| 60 | { | |
| 61 | if (!isTransactionRolledBackOrMarkedRollback()) | |
| 62 | { | |
| 63 | log.trace("Attempting to passivate " + getComponent().getName() + " component"); | |
| 64 | managedEntityWrapper.wrap(ctx.getTarget(), getComponent()); | |
| 65 | reentrant = false; | |
| 66 | log.debug("Passivated " + getComponent().getName() + " component"); | |
| 67 | } | |
| 68 | } | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | ||
| 73 | ||
| 74 | public boolean isInterceptorEnabled() | |
| 75 | { | |
| 76 | return getComponent().getScope() == CONVERSATION; | |
| 77 | } | |
| 78 | ||
| 79 | private static boolean isTransactionRolledBackOrMarkedRollback() | |
| 80 | { | |
| 81 | try | |
| 82 | { | |
| 83 | return Transaction.instance().isRolledBackOrMarkedRollback(); | |
| 84 | } | |
| 85 | catch (Exception e) | |
| 86 | { | |
| 87 | return false; | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | } |
| ... | ...@@ -0,0 +1,188 @@ | |
| 1 | package org.jboss.seam.persistence; | |
| 2 | ||
| 3 | import static org.jboss.seam.util.JSF.DATA_MODEL; | |
| 4 | import static org.jboss.seam.util.JSF.getWrappedData; | |
| 5 | import static org.jboss.seam.util.JSF.setWrappedData; | |
| 6 | ||
| 7 | import java.lang.reflect.Field; | |
| 8 | import java.lang.reflect.Modifier; | |
| 9 | import java.util.List; | |
| 10 | import java.util.Map; | |
| 11 | import java.util.Set; | |
| 12 | ||
| 13 | import org.jboss.seam.Component; | |
| 14 | import org.jboss.seam.Seam; | |
| 15 | import org.jboss.seam.annotations.In; | |
| 16 | import org.jboss.seam.contexts.Contexts; | |
| 17 | import org.jboss.seam.log.LogProvider; | |
| 18 | import org.jboss.seam.log.Logging; | |
| 19 | import org.jboss.seam.util.Reflections; | |
| 20 | ||
| 21 | /** | |
| 22 | * @author Gavin King | |
| 23 | * @author Pete Muir | |
| 24 | * @author Norman Richards | |
| 25 | * | |
| 26 | */ | |
| 27 | public class ManagedEntityWrapper | |
| 28 | { | |
| 29 | ||
| 30 | private static LogProvider log = Logging.getLogProvider(ManagedEntityWrapper.class); | |
| 31 | ||
| 32 | public void wrap(Object target, Component component) throws Exception | |
| 33 | { | |
| 34 | if ( touchedContextsExist() ) | |
| 35 | { | |
| 36 | Class beanClass = target.getClass(); | |
| 37 | for (; beanClass!=Object.class; beanClass=beanClass.getSuperclass()) | |
| 38 | { | |
| 39 | log.trace("Examining fields on " + beanClass); | |
| 40 | for ( Field field: beanClass.getDeclaredFields() ) | |
| 41 | { | |
| 42 | if ( !ignore(field) ) | |
| 43 | { | |
| 44 | Object value = getFieldValue(target, field); | |
| 45 | if (value!=null) | |
| 46 | { | |
| 47 | Object dataModel = null; | |
| 48 | if ( DATA_MODEL.isInstance(value) ) | |
| 49 | { | |
| 50 | dataModel = value; | |
| 51 | value = getWrappedData(dataModel); | |
| 52 | } | |
| 53 | if ( isRef(value) ) | |
| 54 | { | |
| 55 | log.trace("Attempting to save wrapper for " + field + " (" + value + ")"); | |
| 56 | saveWrapper(target, component, field, dataModel, value); | |
| 57 | } | |
| 58 | else | |
| 59 | { | |
| 60 | log.trace("Clearing wrapper for " + field + " (" + value + ") as it isn't a entity reference"); | |
| 61 | clearWrapper(component, field); | |
| 62 | } | |
| 63 | } | |
| 64 | else | |
| 65 | { | |
| 66 | log.trace("Clearing wrapper for " + field + " as it is null"); | |
| 67 | clearWrapper(component, field); | |
| 68 | } | |
| 69 | } | |
| 70 | else | |
| 71 | { | |
| 72 | log.trace("Ignoring field " + field + " as it is static, transient or annotated with @In"); | |
| 73 | } | |
| 74 | } | |
| 75 | } | |
| 76 | } | |
| 77 | else | |
| 78 | { | |
| 79 | log.trace("No touched persistence contexts"); | |
| 80 | } | |
| 81 | } | |
| 82 | ||
| 83 | public void deserialize(Object controllerBean, Component component) throws Exception | |
| 84 | { | |
| 85 | if ( touchedContextsExist() ) | |
| 86 | { | |
| 87 | Class beanClass = controllerBean.getClass(); | |
| 88 | for (; beanClass!=Object.class; beanClass=beanClass.getSuperclass()) | |
| 89 | { | |
| 90 | log.trace("Examining fields on " + beanClass); | |
| 91 | for ( Field field: beanClass.getDeclaredFields() ) | |
| 92 | { | |
| 93 | if ( !ignore(field) ) | |
| 94 | { | |
| 95 | Object value = getFieldValue(controllerBean, field); | |
| 96 | Object dataModel = null; | |
| 97 | if (value!=null && DATA_MODEL.isInstance(value) ) | |
| 98 | { | |
| 99 | dataModel = value; | |
| 100 | } | |
| 101 | log.trace("Attempting to restore wrapper for " + field + " (" + value + ")"); | |
| 102 | //TODO: be more selective | |
| 103 | getFromWrapper(controllerBean, component, field, dataModel); | |
| 104 | } | |
| 105 | else | |
| 106 | { | |
| 107 | log.trace("Ignoring field " + field + " as it is static, transient or annotated with @In"); | |
| 108 | } | |
| 109 | } | |
| 110 | } | |
| 111 | } | |
| 112 | else | |
| 113 | { | |
| 114 | log.trace("No touched persistence contexts"); | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | private boolean isRef(Object value) | |
| 119 | { | |
| 120 | //TODO: could do better by checking if the | |
| 121 | // collection really contains an entity | |
| 122 | return value instanceof List || | |
| 123 | value instanceof Map || | |
| 124 | value instanceof Set || | |
| 125 | Seam.getEntityClass(value.getClass()) != null; | |
| 126 | } | |
| 127 | ||
| 128 | private Object getFieldValue(Object bean, Field field) throws Exception | |
| 129 | { | |
| 130 | if ( !field.isAccessible() ) field.setAccessible(true); | |
| 131 | Object value = Reflections.get(field, bean); | |
| 132 | return value; | |
| 133 | } | |
| 134 | ||
| 135 | private boolean ignore(Field field) | |
| 136 | { | |
| 137 | return Modifier.isTransient( field.getModifiers() ) || | |
| 138 | Modifier.isStatic( field.getModifiers() ) | |
| 139 | || field.isAnnotationPresent(In.class); | |
| 140 | } | |
| 141 | ||
| 142 | private boolean touchedContextsExist() | |
| 143 | { | |
| 144 | PersistenceContexts touchedContexts = PersistenceContexts.instance(); | |
| 145 | return touchedContexts!=null && touchedContexts.getTouchedContexts().size()>0; | |
| 146 | } | |
| 147 | ||
| 148 | private String getFieldId(Component component, Field field) | |
| 149 | { | |
| 150 | return component.getName() + '.' + field.getName(); | |
| 151 | } | |
| 152 | ||
| 153 | private void saveWrapper(Object bean, Component component, Field field, Object dataModel, Object value) throws Exception | |
| 154 | { | |
| 155 | Contexts.getConversationContext().set( getFieldId(component, field), value ); | |
| 156 | if (dataModel==null) | |
| 157 | { | |
| 158 | Reflections.set(field, bean, null); | |
| 159 | } | |
| 160 | else | |
| 161 | { | |
| 162 | setWrappedData(dataModel, null); | |
| 163 | } | |
| 164 | } | |
| 165 | ||
| 166 | private void clearWrapper(Component component, Field field) throws Exception | |
| 167 | { | |
| 168 | Contexts.getConversationContext().remove( getFieldId(component, field) ); | |
| 169 | } | |
| 170 | ||
| 171 | private void getFromWrapper(Object bean, Component component, Field field, Object dataModel) throws Exception | |
| 172 | { | |
| 173 | Object value =Contexts.getConversationContext().get( getFieldId(component, field) ); | |
| 174 | if (value!=null) | |
| 175 | { | |
| 176 | if (dataModel==null) | |
| 177 | { | |
| 178 | Reflections.set(field, bean, value); | |
| 179 | } | |
| 180 | else | |
| 181 | { | |
| 182 | setWrappedData(dataModel, value); | |
| 183 | } | |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 187 | ||
| 188 | } |