/** * Copyright OPS4J * * 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. * * @author nmw * @version $Id: $Id */ package org.ops4j.pax.wicket.spi.support; import static org.ops4j.lang.NullArgumentException.validateNotEmpty; import static org.ops4j.lang.NullArgumentException.validateNotNull; import static org.ops4j.pax.wicket.api.Constants.APPLICATION_NAME; import static org.osgi.framework.Constants.OBJECTCLASS; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import net.sf.cglib.proxy.Factory; import org.ops4j.pax.wicket.api.InjectorHolder; import org.ops4j.pax.wicket.api.PaxWicketInjector; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class DelegatingComponentInstanciationListener extends AbstractPaxWicketInjector { private static final Logger LOGGER = LoggerFactory.getLogger(DelegatingComponentInstanciationListener.class); private final BundleContext context; private final String applicationName; private final List<PaxWicketInjector> resolvers; private ComponentInstanciationListenerTracker tracker; /** * <p>Constructor for DelegatingComponentInstanciationListener.</p> * * @param context a {@link org.osgi.framework.BundleContext} object. * @param applicationName a {@link java.lang.String} object. * @throws java.lang.IllegalArgumentException if any. */ public DelegatingComponentInstanciationListener(BundleContext context, String applicationName) throws IllegalArgumentException { validateNotNull(context, "context"); validateNotEmpty(applicationName, "applicationName"); this.context = context; this.applicationName = applicationName; resolvers = new ArrayList<PaxWicketInjector>(); InjectorHolder.setInjector(applicationName, this); } /** * <p>intialize.</p> * * @throws java.lang.IllegalStateException if any. */ public final void intialize() throws IllegalStateException { synchronized (this) { if (tracker != null) { throw new IllegalStateException( "DelegatingComponentInstanciationListener [" + this + "] had been initialized."); } tracker = new ComponentInstanciationListenerTracker(context, applicationName); tracker.open(); } } /** * <p>dispose.</p> * * @throws java.lang.IllegalStateException if any. */ public void dispose() throws IllegalStateException { synchronized (this) { if (tracker == null) { throw new IllegalStateException( "DelegatingComponentInstanciationListener [" + this + "] had not been initialized."); } tracker.close(); tracker = null; } } /** {@inheritDoc} */ public void inject(Object toInject, Class<?> toHandle) { Set<String> foundAnnotation = countComponentContainPaxWicketBeanAnnotatedFieldsHierachical(toHandle); if (foundAnnotation.isEmpty()) { LOGGER.trace("Component {} doesn't contain any PaxWicketBean fields. Therefore ignore", toInject .getClass().getName()); return; } Set<String> handledAnnotations = new HashSet<String>(); synchronized (resolvers) { Class<?> currentAnalysingClass = toHandle; boolean handledFactory = false; if (Factory.class.isInstance(toInject)) { handledFactory = true; } LOGGER.debug("Component {} trying to find injector", toInject .getClass().getName()); while (!isBoundaryClass(currentAnalysingClass)) { for (PaxWicketInjector listener : resolvers) { try { listener.inject(toInject, currentAnalysingClass); // if we reach here the bean had been injected correctly if (handledFactory) { handledAnnotations.addAll( countComponentContainPaxWicketBeanAnnotatedOneLevel(currentAnalysingClass .getSuperclass())); } else { handledAnnotations.addAll( countComponentContainPaxWicketBeanAnnotatedOneLevel(currentAnalysingClass)); } // once we've found it we could take the next level break; } catch (IllegalStateException e) { LOGGER.debug("Nothing found for component {}, using injector {} got this exception: {}", toInject .getClass().getName(),listener.getClass().getCanonicalName(),e.getMessage()); // well, not found... retry with the next listener } } currentAnalysingClass = currentAnalysingClass.getSuperclass(); if (handledFactory) { currentAnalysingClass = currentAnalysingClass.getSuperclass(); handledFactory = false; } } } if (handledAnnotations.size() != foundAnnotation.size()) { throw new IllegalStateException(String.format( "For Component %s %d %s fields should be injected but only %d %s had been injected.", toInject .getClass() .getName(), foundAnnotation.size(), Arrays.toString(foundAnnotation.toArray(new String[0])), handledAnnotations.size(), Arrays.toString(handledAnnotations.toArray(new String[0])))); } } private final class ComponentInstanciationListenerTracker extends ServiceTracker<PaxWicketInjector, PaxWicketInjector> { private final String m_applicationName; ComponentInstanciationListenerTracker(BundleContext context, String applicationName) { super(context, createFilter(context, applicationName), null); m_applicationName = applicationName; } @Override public final PaxWicketInjector addingService(ServiceReference<PaxWicketInjector> reference) { PaxWicketInjector resolver = super.addingService(reference); synchronized (resolvers) { resolvers.add(resolver); } return resolver; } @Override public final void modifiedService(ServiceReference<PaxWicketInjector> reference, PaxWicketInjector service) { Object objAppName = reference.getProperty(APPLICATION_NAME); if (objAppName != null) { Class<?> nameClass = objAppName.getClass(); if (String.class.isAssignableFrom(nameClass)) { if (!nameClass.isArray()) { String appName = (String) objAppName; if (m_applicationName.equals(appName)) { return; } } else { String[] appNames = (String[]) objAppName; for (String appName : appNames) { if (m_applicationName.equals(appName)) { return; } } } } } removedService(reference, service); } @Override public final void removedService(ServiceReference<PaxWicketInjector> reference, PaxWicketInjector service) { PaxWicketInjector resolver = service; synchronized (resolvers) { resolvers.remove(resolver); } super.removedService(reference, service); } } private static Filter createFilter(BundleContext context, String applicationName) { String filterStr = "(&(" + OBJECTCLASS + "=" + PaxWicketInjector.class.getName() + ")(" + APPLICATION_NAME + "=" + applicationName + "))"; try { return context.createFilter(filterStr); } catch (InvalidSyntaxException e) { String message = APPLICATION_NAME + "[" + applicationName + "] has an invalid format. "; throw new IllegalArgumentException(message); } } }