001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.spring.injection.annot;
018:
019: import java.lang.reflect.Field;
020: import java.util.concurrent.ConcurrentHashMap;
021:
022: import org.apache.wicket.injection.IFieldValueFactory;
023: import org.apache.wicket.proxy.LazyInitProxyFactory;
024: import org.apache.wicket.spring.ISpringContextLocator;
025: import org.apache.wicket.spring.SpringBeanLocator;
026:
027: /**
028: * {@link IFieldValueFactory} that uses {@link LazyInitProxyFactory} to create
029: * proxies for Spring dependencies based on the {@link SpringBean} annotation
030: * applied to a field. This class is usually used by the
031: * {@link AnnotSpringInjector} to inject objects with lazy init proxies.
032: * However, this class can be used on its own to create proxies for any field
033: * decorated with a {@link SpringBean} annotation.
034: * <p>
035: * Example:
036: *
037: * <pre>
038: * IFieldValueFactory factory = new AnnotProxyFieldValueFactory(contextLocator);
039: * field = obj.getClass().getDeclaredField("dependency");
040: * IDependency dependency = (IDependency) factory.getFieldValue(field, obj);
041: * </pre>
042: *
043: * In the example above the <code>dependency</code> object returned is a lazy
044: * init proxy that will look up the actual IDependency bean from spring context
045: * upon first access to one of the methods.
046: * <p>
047: * This class will also cache any produced proxies so that the same proxy is
048: * always returned for the same spring dependency. This helps cut down on
049: * session size beacause proxies for the same dependency will not be serialized
050: * twice.
051: *
052: * @see LazyInitProxyFactory
053: * @see SpringBean
054: * @see SpringBeanLocator
055: *
056: * @author Igor Vaynberg (ivaynberg)
057: * @author Istvan Devai
058: *
059: */
060: public class AnnotProxyFieldValueFactory implements IFieldValueFactory {
061: private ISpringContextLocator contextLocator;
062: private boolean failFast = true;
063:
064: private final ConcurrentHashMap<SpringBeanLocator, Object> cache = new ConcurrentHashMap<SpringBeanLocator, Object>();
065:
066: /**
067: * @param contextLocator
068: * spring context locator
069: */
070: public AnnotProxyFieldValueFactory(
071: ISpringContextLocator contextLocator) {
072: if (contextLocator == null) {
073: throw new IllegalArgumentException(
074: "[contextLocator] argument cannot be null");
075: }
076: this .contextLocator = contextLocator;
077: }
078:
079: /**
080: * @see org.apache.wicket.injection.IFieldValueFactory#getFieldValue(java.lang.reflect.Field,
081: * java.lang.Object)
082: */
083: public Object getFieldValue(Field field, Object fieldOwner) {
084:
085: if (field.isAnnotationPresent(SpringBean.class)) {
086: SpringBean annot = field.getAnnotation(SpringBean.class);
087: SpringBeanLocator locator = new SpringBeanLocator(annot
088: .name(), field.getType(), contextLocator);
089:
090: // only check the cache if the bean is a singleton
091: if (locator.isSingletonBean() && cache.containsKey(locator)) {
092: return cache.get(locator);
093: }
094:
095: if (failFast) {
096: testLocator(locator, fieldOwner, field);
097: }
098:
099: Object proxy = LazyInitProxyFactory.createProxy(field
100: .getType(), locator);
101: // only put the proxy into the cache if the bean is a singleton
102: if (locator.isSingletonBean()) {
103: cache.put(locator, proxy);
104: }
105: return proxy;
106: } else {
107: return null;
108: }
109: }
110:
111: /**
112: * Tests if the locator can retrieve the bean it is responsible for.
113: *
114: * @param locator
115: * @param fieldOwner
116: * @param field
117: */
118: private void testLocator(SpringBeanLocator locator,
119: Object fieldOwner, Field field) {
120: try {
121: locator.locateProxyTarget();
122: } catch (Throwable e) {
123: String errorMessage = "Could not locate spring bean of class [["
124: + locator.getBeanType().getName() + "]] ";
125: if (locator.getBeanName() != null
126: && locator.getBeanName().length() > 0) {
127: errorMessage += "and id [[" + locator.getBeanName()
128: + "]] ";
129: }
130: errorMessage += "needed in class [["
131: + fieldOwner.getClass().getName() + "]] field [["
132: + field.getName() + "]]";
133: throw new RuntimeException(errorMessage, e);
134: }
135: }
136:
137: /**
138: * @param failFast true if the locator fails if a bean can't be located
139: */
140: public void setFailFast(boolean failFast) {
141: this .failFast = failFast;
142: }
143:
144: /**
145: * @see org.apache.wicket.injection.IFieldValueFactory#supportsField(java.lang.reflect.Field)
146: */
147: public boolean supportsField(Field field) {
148: return field.isAnnotationPresent(SpringBean.class);
149: }
150: }
|