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.guice;
018
019import java.lang.reflect.InvocationTargetException;
020
021import javax.servlet.ServletContext;
022
023import com.google.inject.Guice;
024import com.google.inject.Injector;
025import com.google.inject.Module;
026import com.google.inject.Stage;
027import org.apache.wicket.protocol.http.IWebApplicationFactory;
028import org.apache.wicket.protocol.http.WebApplication;
029import org.apache.wicket.protocol.http.WicketFilter;
030import org.apache.wicket.util.string.Strings;
031
032
033/**
034 * Implementation of IWebApplicationFactory that pulls the WebApplication object out of a Guice
035 * Module.
036 * 
037 * Configuration example:
038 * 
039 * <pre>
040 * &lt;filter&gt;
041 *   &lt;filter-name&gt;MyApplication&lt;/filter-name&gt;
042 *   &lt;filter-class&gt;org.apache.wicket.protocol.http.WicketFilter&lt;/filter-class&gt;
043 *   &lt;init-param&gt;
044 *     &lt;param-name&gt;applicationFactoryClassName&lt;/param-name&gt;
045 *     &lt;param-value&gt;org.apache.wicket.guice.GuiceWebApplicationFactory&lt;/param-value&gt;
046 *   &lt;/init-param&gt;
047 *   &lt;init-param&gt;
048 *     &lt;param-name&gt;module&lt;/param-name&gt;
049 *     &lt;param-value&gt;com.company.MyGuiceModule,com.company.MyOtherGuiceModule&lt;/param-value&gt;
050 *   &lt;/init-param&gt;
051 *   &lt;init-param&gt;
052 *     &lt;param-name&gt;wicket-guice.stage&lt;/param-name&gt;
053 *     &lt;param-value&gt;DEVELOPMENT&lt;/param-value&gt;
054 *   &lt;/init-param&gt;
055 * &lt;/filter&gt;
056 * </pre>
057 * 
058 * This factory will create an Injector configured using the Guice Module implementation you pass it
059 * above. Multiple modules can be specified by naming multiple classes separated by a comma. The
060 * Guice Module (MyGuiceModule in the example above) needs to bind WebApplication.class and provide
061 * a concrete implementation of it.
062 * 
063 * The stage used when creating the Injector may be specified by the optional wicket-guice.stage
064 * parameter. When this parameter is not present this factory does not use specify a Stage when
065 * creating the Injector. This parameter can also be set as a context parameter to provide
066 * configuration for all instances in the web application.
067 * 
068 * Alternatively, you can dig the Injector out of the ServletContext as an attribute, like so:
069 * 
070 * <pre>
071 * &lt;filter&gt;
072 *   &lt;filter-name&gt;MyApplication&lt;/filter-name&gt;
073 *   &lt;filter-class&gt;org.apache.wicket.protocol.http.WicketFilter&lt;/filter-class&gt;
074 *   &lt;init-param&gt;
075 *     &lt;param-name&gt;applicationFactoryClassName&lt;/param-name&gt;
076 *     &lt;param-value&gt;org.apache.wicket.guice.GuiceWebApplicationFactory&lt;/param-value&gt;
077 *   &lt;/init-param&gt;
078 *   &lt;init-param&gt;
079 *     &lt;param-name&gt;injectorContextAttribute&lt;/param-name&gt;
080 *     &lt;param-value&gt;GuiceInjector&lt;/param-value&gt;
081 *   &lt;/init-param&gt;
082 * &lt;/filter&gt;
083 * </pre>
084 * 
085 * <b>NB: You no longer have to add a GuiceComponentInjector manually in your
086 * {@link WebApplication#init()} method - this factory will do that for you automatically.</b>
087 * 
088 * @author Alastair Maw (almaw)
089 * 
090 */
091public class GuiceWebApplicationFactory implements IWebApplicationFactory
092{
093        /** */
094        public static final String STAGE_PARAMETER = "wicket-guice.stage";
095
096        /**
097         * @see IWebApplicationFactory#createApplication(WicketFilter)
098         */
099        @Override
100        public WebApplication createApplication(final WicketFilter filter)
101        {
102                Injector injector;
103
104                String injectorContextAttribute = filter.getFilterConfig().getInitParameter(
105                        "injectorContextAttribute");
106
107                Stage stage = null;
108
109                String stageContextAttribute = filter.getFilterConfig().getInitParameter(STAGE_PARAMETER);
110                if (stageContextAttribute == null)
111                {
112                        stageContextAttribute = filter.getFilterConfig()
113                                .getServletContext()
114                                .getInitParameter(STAGE_PARAMETER);
115                }
116                if (stageContextAttribute != null)
117                {
118                        stage = Stage.valueOf(stageContextAttribute.trim());
119                }
120
121                if (injectorContextAttribute != null)
122                {
123                        ServletContext sc = filter.getFilterConfig().getServletContext();
124
125                        // Try to dig the Injector out of the ServletContext, for integration with context
126                        // listener-based instantiation of Guice.
127                        injector = (Injector)sc.getAttribute(injectorContextAttribute);
128                        if (injector == null)
129                        {
130                                throw new RuntimeException(
131                                        "Could not find Guice Injector in the ServletContext under attribute: " +
132                                                injectorContextAttribute);
133                        }
134                }
135                else if (filter.getFilterConfig().getInitParameter("module") != null)
136                {
137                        String paramValue = filter.getFilterConfig().getInitParameter("module");
138                        String moduleNames[] = Strings.split(paramValue, ',');
139                        Module modules[] = new Module[moduleNames.length];
140                        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
141                        for (int i = 0; i < moduleNames.length; i++)
142                        {
143                                String moduleName = moduleNames[i].trim();
144                                try
145                                {
146                                        // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6500212
147                                        Class<?> moduleClazz = Class.forName(moduleName, false, classLoader);
148                                        Object moduleObject = moduleClazz.getDeclaredConstructor().newInstance();
149                                        modules[i] = (Module)moduleObject;
150                                }
151                                catch (InstantiationException | ClassNotFoundException | IllegalAccessException
152                                                | NoSuchMethodException | InvocationTargetException e)
153                                {
154                                        throw new RuntimeException(
155                                                "Could not create new instance of Guice Module class " + moduleName, e);
156                                }
157                        }
158                        if (stage != null)
159                        {
160                                injector = Guice.createInjector(stage, modules);
161                        }
162                        else
163                        {
164                                injector = Guice.createInjector(modules);
165                        }
166                }
167                else
168                {
169                        throw new RuntimeException(
170                                "To use GuiceWebApplicationFactory, you must specify either an 'injectorContextAttribute' or a 'module' init-param.");
171                }
172                WebApplication app = injector.getInstance(WebApplication.class);
173                app.getComponentInstantiationListeners().add(new GuiceComponentInjector(app, injector));
174                return app;
175        }
176
177        /** {@inheritDoc} */
178        @Override
179        public void destroy(final WicketFilter filter)
180        {
181        }
182}