xwork com.opensymphony.xwork2.config.ConfigurationManager needs better error handling for missing plugin config,class ---------------------------------------------------------------------------------------------------------------------
Key: WW-3153 URL: https://issues.apache.org/struts/browse/WW-3153 Project: Struts 2 Issue Type: Bug Environment: Struts 2.1.6 xwork 2.0-SNAPSHOT J2SE 1.6.0.10 TC 1.6 Reporter: Martin Gainty xwork com.opensymphony.xwork2.config.ConfigurationManager code /*** Get the current XWork configuration object. By default an instance of DefaultConfiguration will be returned * @see com.opensymphony.xwork2.config.impl.DefaultConfiguration */ public synchronized Configuration getConfiguration() { if (configuration == null) { setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName)); try { configuration.reload(getConfigurationProviders()); } catch (ConfigurationException e) { setConfiguration(null); /****** xwork getConfigurationProviders code inlined so we can see whats going on *** * Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method * will return a list containing only the default ConfigurationProvider, XMLConfigurationProvider. if a custom * ConfigurationProvider has been added, then the XmlConfigurationProvider must be added by hand. * </p> * <p/> * TODO: the lazy instantiation of XmlConfigurationProvider should be refactored to be elsewhere. the behavior described above seems unintuitive. * * @return the list of registered ConfigurationProvider objects * @see ConfigurationProvider */ public List<ConfigurationProvider> getConfigurationProviders() { providerLock.lock(); try { if (configurationProviders.size() == 0) { configurationProviders.add(new XmlConfigurationProvider("xwork.xml", true)); } return configurationProviders; } finally { providerLock.unlock(); } } ***********/ throw e; } } else { conditionalReload(); } return configuration; } //end configurationManager //Here is com.opensymphony.xwork2.config.impl.DefaultConfigurationProvider public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException { ContainerProperties props = new ContainerProperties(); ContainerBuilder builder = new ContainerBuilder(); for (final ContainerProvider containerProvider : providers) { containerProvider.init(this); containerProvider.register(builder, props); } props.setConstants(builder); //a quick recap of setConstants /*public void setConstants(ContainerBuilder builder) { //keySet appears to be empty so the iterator to key wont work here for (Object keyobj : keySet()) { String key = (String)keyobj; builder.factory(String.class, key, new LocatableConstantFactory<String>(getProperty(key), getPropertyLocation(key))); } } */ container = builder.create(false); /*start code com.opensymphony.xwork2.inject.ConfigurationBuilder.create() public Container create(boolean loadSingletons) { ensureNotCreated(); created = true; final ContainerImpl container = new ContainerImpl( new HashMap<Key<?>, InternalFactory<?>>(factories)); if (loadSingletons) { //wont happen as this is always false container.callInContext(new ContainerImpl.ContextualCallable<Void>() { public Void call(InternalContext context) { for (InternalFactory<?> factory : singletonFactories) { factory.create(context); } return null; } }); } //final List<Class<?>> staticInjections = new ArrayList<Class<?>>(); //no effect as staticInjections are null at this point container.injectStatics(staticInjections); return container; } //end com.opensymphony.xwork2.inject.ConfigurationBuilder.create() */ setContext(container); /******* setContext code inlined so we can see whats going on protected ActionContext setContext(Container cont) { ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack(); ActionContext context = new ActionContext(vs.getContext()); ActionContext.setContext(context); return context; //returns the context of the VS from the supplied container } ********/ // Set<String> getInstanceNames(Class<?> type); objectFactory = container.getInstance(ObjectFactory.class); //a not null objectFactory with no contained objects // Process the configuration providers first (WARNING providers could be null!) for (final ContainerProvider containerProvider : providers) { if (containerProvider instanceof PackageProvider) { container.inject(containerProvider); ((PackageProvider)containerProvider).loadPackages(); packageProviders.add((PackageProvider)containerProvider); } } // Then process any package providers from the plugins //Container.getInstanceNames Set<String> getInstanceNames(Class<?> type); /*com.opensymphony.xwork2.inject.ContainerImpl has getInstanceNames we can use public Set<String> getInstanceNames(final Class<?> type) { return factoryNamesByType.get(type); //FYI this.factoryNamesByType = Collections.unmodifiableMap(map); //now a simple get on the key should return the Factory for PackageProvider } */ Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class); if (packageProviderNames != null) { for (String name : packageProviderNames) { PackageProvider provider = container.getInstance(PackageProvider.class, name); provider.init(this); provider.loadPackages(); packageProviders.add(provider); } } rebuildRuntimeConfiguration(); } finally { ActionContext.setContext(null); } return packageProviders; //how does a factory for PackageProvider get created? /* CAUTION members needs to contain the classnames for factory to create <M extends Member & AnnotatedElement> void addInjectorsForMembers( List<M> members, boolean statics, List<Injector> injectors, InjectorFactory<M> injectorFactory) { for (M member : members) { if (isStatic(member) == statics) { Inject inject = member.getAnnotation(Inject.class); if (inject != null) { try { injectors.add(injectorFactory.create(this, member, inject.value())); } catch (MissingDependencyException e) { if (inject.required()) { throw new DependencyException(e); } } } } } } */ //where is addInjectorsForMembers called? /*addInjectorsForMembers is called as Injector parameter to method void addInjectorsForMethods(Method[] methods, boolean statics, List<Injector> injectors) { addInjectorsForMembers(Arrays.asList(methods), statics, injectors, new InjectorFactory<Method>() { public Injector create(ContainerImpl container, Method method, String name) throws MissingDependencyException { return new MethodInjector(container, method, name); } }); } */ OR addInjectorsForMember is called as parameter to Field void addInjectorsForFields(Field[] fields, boolean statics, List<Injector> injectors) { addInjectorsForMembers(Arrays.asList(fields), statics, injectors, new InjectorFactory<Field>() { public Injector create(ContainerImpl container, Field field, String name) throws MissingDependencyException { return new FieldInjector(container, field, name); } }); } //where do the injectors come from //they are called as a parameter to injectStatics here is the code: void injectStatics(List<Class<?>> staticInjections) { final List<Injector> injectors = new ArrayList<Injector>(); for (Class<?> clazz : staticInjections) { addInjectorsForFields(clazz.getDeclaredFields(), true, injectors); addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors); } callInContext(new ContextualCallable<Void>() { public Void call(InternalContext context) { for (Injector injector : injectors) { injector.inject(context, null); } return null; } }); } //the solution comes provided with test samples for xwork2 //com.opensymphony.xwork2.conf.XmlConfigurationProvider.java String impl=java.util.Properties.getProperty("struts.convention.actionConfigBuilder"); Class clazz = new Class(impl); //quick sanity check if (clazz==null) { log.debug(impl+"class is not on classpath please put actionConfigBuilder class on classpath"); } //if the instantiated class is null because of wrong name or not on classpath we should log the error! Class cimpl = ClassLoaderUtil.loadClass(impl, clazz); if (cimpl==null) { log.debug(impl+"class is not on classpath please put actionConfigBuilder class on classpath"); } //if the instantiated class is null because of wrong name or not on classpath or CL couldnt find we should log the error! /*the resourceName HAS TO BE PRECONFIGURED in context otherwise call to getResource on impl will fail public static URL getResource(String resourceName, Class callingClass) { URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName); if (url == null) { url = ClassLoaderUtil.class.getClassLoader().getResource(resourceName); } if (url == null) { ClassLoader cl = callingClass.getClassLoader(); if (cl != null) { url = cl.getResource(resourceName); } } */ // Force loading of class to detect no class def found exceptions try { cimpl.getDeclaredClasses(); } catch(ClassNotFoundException cnfe) { log.debug("current CL cant locate class "+cimpl+"place class on CLASSPATH or configuration for plugin class is missing please try again"); } //now that you have the actionConfigBuilder Class built and the configuration is verified go ahead and inject containerBuilder.injectStatics(cimpl); HTH Martin Gainty -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.