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 */ 017package org.apache.wicket.spring.injection.annot; 018 019import javax.servlet.ServletContext; 020 021import org.apache.wicket.Application; 022import org.apache.wicket.Component; 023import org.apache.wicket.IBehaviorInstantiationListener; 024import org.apache.wicket.MetaDataKey; 025import org.apache.wicket.Session; 026import org.apache.wicket.application.IComponentInstantiationListener; 027import org.apache.wicket.behavior.Behavior; 028import org.apache.wicket.injection.IFieldValueFactory; 029import org.apache.wicket.injection.Injector; 030import org.apache.wicket.model.Model; 031import org.apache.wicket.protocol.http.WebApplication; 032import org.apache.wicket.spring.ISpringContextLocator; 033import org.apache.wicket.util.lang.Args; 034import org.springframework.context.ApplicationContext; 035import org.springframework.web.context.support.WebApplicationContextUtils; 036 037/** 038 * {@link IComponentInstantiationListener} that injects component and behavior properties 039 * annotated with {@link SpringBean} annotations. 040 * 041 * To install in yourapplication.init() call 042 * <code>getComponentInstantiationListeners().add(new SpringComponentInjector(this));</code> 043 * <p> 044 * Only Wicket {@link Component}s and {@link Behavior}s are automatically injected, other classes 045 * such as {@link Session}, {@link Model}, and any other POJO can be injected by calling 046 * <code>Injector.get().inject(this)</code> in their constructor. 047 * </p> 048 * 049 * @author Igor Vaynberg (ivaynberg) 050 * @author <a href="mailto:jlee@antwerkz.com">Justin Lee</a> 051 * 052 */ 053public class SpringComponentInjector extends Injector 054 implements 055 IComponentInstantiationListener, 056 IBehaviorInstantiationListener 057{ 058 private final IFieldValueFactory fieldValueFactory; 059 060 /** 061 * Metadata key used to store application context in application's metadata 062 */ 063 private static MetaDataKey<ApplicationContext> CONTEXT_KEY = new MetaDataKey<>() 064 { 065 private static final long serialVersionUID = 1L; 066 067 }; 068 069 /** 070 * Constructor used when spring application context is declared in the spring standard way and 071 * can be located through 072 * {@link WebApplicationContextUtils#getRequiredWebApplicationContext(ServletContext)}. 073 * 074 * @param webapp 075 * wicket web application 076 */ 077 public SpringComponentInjector(final WebApplication webapp) 078 { 079 this(webapp, getDefaultContext(webapp)); 080 } 081 082 /** 083 * Constructor 084 * 085 * @param webapp 086 * wicket web application 087 * @param ctx 088 * spring's application context 089 */ 090 public SpringComponentInjector(final WebApplication webapp, final ApplicationContext ctx) 091 { 092 this(webapp, ctx, true); 093 } 094 095 /** 096 * Constructor 097 * 098 * @param webapp 099 * wicket web application 100 * @param ctx 101 * spring's application context 102 * 103 * @param wrapInProxies 104 * whether or not wicket should wrap dependencies with specialized proxies that can 105 * be safely serialized. in most cases this should be set to true. 106 */ 107 public SpringComponentInjector(final WebApplication webapp, final ApplicationContext ctx, 108 final boolean wrapInProxies) 109 { 110 Args.notNull(webapp, "webapp"); 111 112 Args.notNull(ctx, "ctx"); 113 114 // store context in application's metadata ... 115 webapp.setMetaData(CONTEXT_KEY, ctx); 116 fieldValueFactory = new AnnotProxyFieldValueFactory(new ContextLocator(), wrapInProxies); 117 webapp.getBehaviorInstantiationListeners().add(this); 118 bind(webapp); 119 } 120 121 @Override 122 public void inject(final Object object) 123 { 124 inject(object, fieldValueFactory); 125 } 126 127 @Override 128 public void onInstantiation(final Component component) 129 { 130 inject(component); 131 } 132 133 @Override 134 public void onInstantiation(Behavior behavior) 135 { 136 inject(behavior); 137 } 138 139 /** 140 * A context locator that locates the context in application's metadata. This locator also keeps 141 * a transient cache of the lookup. 142 * 143 * @author ivaynberg 144 */ 145 private static class ContextLocator implements ISpringContextLocator 146 { 147 private transient ApplicationContext context; 148 149 private static final long serialVersionUID = 1L; 150 151 @Override 152 public ApplicationContext getSpringContext() 153 { 154 if (context == null) 155 { 156 context = Application.get().getMetaData(CONTEXT_KEY); 157 } 158 return context; 159 } 160 161 } 162 163 /** 164 * Try to use an already pre-configured application context or locate it through Spring's default 165 * location mechanism. 166 * 167 * @param webapp 168 * @return the application context to use for injection 169 */ 170 private static ApplicationContext getDefaultContext(final WebApplication webapp) 171 { 172 ApplicationContext context = webapp.getMetaData(CONTEXT_KEY); 173 if (context == null) 174 { 175 context = WebApplicationContextUtils.getRequiredWebApplicationContext(webapp.getServletContext()); 176 } 177 return context; 178 } 179 180 /** 181 * Set the default context for the given webapp. 182 * 183 * @param webapp 184 * web application 185 * @param context 186 * context to use as default if non is explicitely specified for the injector 187 */ 188 public static void setDefaultContext(final WebApplication webapp, ApplicationContext context) 189 { 190 Args.notNull(context, "context"); 191 192 if (webapp.getMetaData(CONTEXT_KEY) == null) 193 { 194 webapp.setMetaData(CONTEXT_KEY, context); 195 } 196 } 197}