GuiceComponentInjectorを
CustomInjectionsを
Injectできなくて
※正確には、
参考サイト
Wicket 1.5 と
Custom Injector SLF4JAdd Star
Wicketと
問題
wicket guice dropwizardの
原因
処理シーケンスで
※嘘を
1. GuiceComponentInjectorの コンストラクタで GuiceFieldValueFactoryが newされる。
/** * Creates a new Wicket GuiceComponentInjector instance, using the provided Guice * {@link Injector} instance. * * @param app * @param injector * @param wrapInProxies * whether or not wicket should wrap dependencies with specialized proxies that can * be safely serialized. in most cases this should be set to true. */ public GuiceComponentInjector(final Application app, final Injector injector, final boolean wrapInProxies) { app.setMetaData(GuiceInjectorHolder.INJECTOR_KEY, new GuiceInjectorHolder(injector)); fieldValueFactory = new GuiceFieldValueFactory(wrapInProxies); app.getBehaviorInstantiationListeners().add(this); bind(app); }
2. GuiceComponentInjector#onInstantiation()が Wicketの 処理シーケンスの どっかで 呼び出される。
3. GuiceComponentInjector#onInstantiation()から 規定クラスorg.apache.wicket.injection.Injectorの inject()メソッドが 呼び出される。
4. org.apache.wicket.injection.Injector#inject()から、 factory#getFieldValue()で、 GuiceFieldValueFactoryの getFieldValue()が 呼び出さる。
5. GuiceFieldValueFactory#getFieldValue()内で、 GuiceFieldValueFactory#supportsField()が 呼び出さる。
/** * {@inheritDoc} */ @Override public boolean supportsField(final Field field) { return field.isAnnotationPresent(Inject.class) || field.isAnnotationPresent(javax.inject.Inject.class); }
6.[5].で CustomInjectionsは あからさまに サポートされていないので、 Null、
なので NullPointerExceptionとなる。
※予想です…
対処方法(暫定1) IPageFactoryの 実装クラスを 作成する。
以下の
/* * Copyright 2016 Kem. * * Licensed 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 xyz.monotalk.xxx; import com.google.inject.Injector; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.concurrent.ConcurrentMap; import javax.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.wicket.IPageFactory; import org.apache.wicket.Page; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.authorization.AuthorizationException; import org.apache.wicket.markup.MarkupException; import org.apache.wicket.request.RequestHandlerStack.ReplaceHandlerException; import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.session.DefaultPageFactory; import org.apache.wicket.util.lang.Generics; /** * AppGuicePageFactory * * @author Kem */ public class AppGuicePageFactory implements IPageFactory { @Inject private Injector injector; /** * Log for reporting. */ private static final Logger log = LogManager.getLogger(DefaultPageFactory.class); /** * Map of Constructors for Page subclasses */ private final ConcurrentMap<Class<?>, Constructor<?>> constructorForClass = Generics.newConcurrentHashMap(); /** * {@link #isBookmarkable(Class)} is expensive, we cache the result here */ private final ConcurrentMap<String, Boolean> pageToBookmarkableCache = Generics.newConcurrentHashMap(); @Override public final <C extends IRequestablePage> C newPage(final Class<C> pageClass) { try { // throw an exception in case default constructor is missing // => improved error message Constructor<C> constructor = pageClass.getDeclaredConstructor((Class<?>[]) null); return processPage(newPage(constructor, null), null); } catch (NoSuchMethodException e) { // a bit of a hack here.. Constructor<C> constructor = constructor(pageClass, PageParameters.class); if (constructor != null) { PageParameters pp = new PageParameters(); return processPage(newPage(constructor, pp), pp); } else { throw new WicketRuntimeException("Unable to create page from " + pageClass + ". Class does not have a visible default constructor.", e); } } } @Override public final <C extends IRequestablePage> C newPage(final Class<C> pageClass, final PageParameters parameters) { // Try to get constructor that takes PageParameters Constructor<C> constructor = constructor(pageClass, PageParameters.class); // If we got a PageParameters constructor if (constructor != null) { final PageParameters nullSafeParams = parameters == null ? new PageParameters() : parameters; // return new Page(parameters) return processPage(newPage(constructor, nullSafeParams), nullSafeParams); } // Always try default constructor if one exists return processPage(newPage(pageClass), parameters); } /** * Looks up a one-arg Page constructor by class and argument type. * * @param pageClass The class of page * @param argumentType The argument type * @return The page constructor, or null if no one-arg constructor can be * found taking the given argument type. */ private <C extends IRequestablePage> Constructor<C> constructor(final Class<C> pageClass, final Class<PageParameters> argumentType) { // Get constructor for page class from cache Constructor<C> constructor = (Constructor<C>) constructorForClass.get(pageClass); // Need to look up? if (constructor == null) { try { // Try to find the constructor constructor = pageClass.getDeclaredConstructor(new Class[]{argumentType}); // Store it in the cache Constructor<C> tmpConstructor = (Constructor<C>) constructorForClass.putIfAbsent(pageClass, constructor); if (tmpConstructor != null) { constructor = tmpConstructor; } log.debug("Found constructor for Page of type '{}' and argument of type '{}'.", pageClass, argumentType); } catch (NoSuchMethodException e) { log.debug( "Page of type '{}' has not visible constructor with an argument of type '{}'.", pageClass, argumentType); return null; } } return constructor; } /** * Creates a new Page using the given constructor and argument. * * @param constructor The constructor to invoke * @param argument The argument to pass to the constructor or null to pass * no arguments * @return The new page * @throws WicketRuntimeException Thrown if the Page cannot be instantiated * using the given constructor and argument. */ private <C extends IRequestablePage> C newPage(final Constructor<C> constructor, final PageParameters argument) { try { if (argument != null) { return constructor.newInstance(argument); } else { return constructor.newInstance(); } } catch (InstantiationException | IllegalAccessException e) { throw new WicketRuntimeException(createDescription(constructor, argument), e); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof ReplaceHandlerException || e.getTargetException() instanceof AuthorizationException || e.getTargetException() instanceof MarkupException) { throw (RuntimeException) e.getTargetException(); } throw new WicketRuntimeException(createDescription(constructor, argument), e); } } private <C extends IRequestablePage> C processPage(final C page, final PageParameters pageParameters) { // the page might have not propagate page parameters from constructor. if that's the case // we force the parameters if ((pageParameters != null) && (page.getPageParameters() != pageParameters)) { page.getPageParameters().overwriteWith(pageParameters); } ((Page) page).setWasCreatedBookmarkable(true); injector.injectMembers(page); return page; } private String createDescription(final Constructor<?> constructor, final Object argument) { StringBuilder msg = new StringBuilder(); msg.append("Can't instantiate page using constructor '").append(constructor).append('\''); if (argument != null) { msg.append(" and argument '").append(argument).append('\''); } msg.append('.'); if (constructor != null) { if (Modifier.isPrivate(constructor.getModifiers())) { msg.append(" This constructor is private!"); } else { msg.append(" An exception has been thrown during construction!"); } } else { msg.append(" There is no such constructor!"); } return msg.toString(); } @Override public <C extends IRequestablePage> boolean isBookmarkable(Class<C> pageClass) { Boolean bookmarkable = pageToBookmarkableCache.get(pageClass.getName()); if (bookmarkable == null) { try { if (pageClass.getDeclaredConstructor(new Class[]{}) != null) { bookmarkable = Boolean.TRUE; } } catch (NoSuchMethodException | SecurityException ignore) { try { if (pageClass.getDeclaredConstructor(new Class[]{PageParameters.class}) != null) { bookmarkable = Boolean.TRUE; } } catch (NoSuchMethodException | SecurityException ignored) { log.warn("Error Occured..", ignored); } } if (bookmarkable == null) { bookmarkable = Boolean.FALSE; } Boolean tmpBookmarkable = pageToBookmarkableCache.putIfAbsent(pageClass.getName(), bookmarkable); if (tmpBookmarkable != null) { bookmarkable = tmpBookmarkable; } } return bookmarkable; } }
上記は、
((Page) page).setWasCreatedBookmarkable(true); injector.injectMembers(page); return page;
対処方法(暫定2) コンストラクター内で Injectしてしまう。
onInitialize()等で、
WebApplication webApp = (WebApplication) getApplication(); Injector i = (Injector) webApp.getServletContext().getAttribute("com.google.inject.Injector"); i.injectMembers(this);
恒久な 対処方法
GuiceComponentInjectorあたりから
個人で 実装してる 感じでの 結論
Pagaクラスで
と
@Injectで
自分ルールで
コメント