/*
* Copyright 2011 FatWire Corporation. All Rights Reserved.
*
* 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 com.fatwire.gst.foundation.controller.support;
import com.fatwire.gst.foundation.controller.AppContext;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @author Tony Field
* @since 2016-07-20
*/
public class WebAppContext implements AppContext {
protected static final Logger LOG = LoggerFactory.getLogger("tools.gsf.legacy.config.WebAppContext");
public static final String WEB_CONTEXT_NAME = "gsf/AppContext";
private final AppContext parent;
private Map<String, Object> localScope = new HashMap<>();
/**
* This constructor was needed for the SimpleWebAppContextLoader (now deprecated).
*
* @param context servlet context
* @param parent application context
*/
public WebAppContext(ServletContext context, AppContext parent) {
super();
this.parent = parent;
}
@Override
public void init() {
}
@SuppressWarnings("unchecked")
@Override
public final <T> T getBean(String name, Class<T> c) {
if (StringUtils.isBlank(name))
throw new IllegalArgumentException("name cannot be null or empty");
if (c.isArray()) {
throw new IllegalArgumentException("Arrays are not supported");
}
Object o = localScope.get(name);
if (o == null) {
LOG.debug("Asking for bean by name {} of type {}.",name, c.getName());
try {
// TODO: medium: check for other method signatures
o = createByMethod(this, c);
if (o != null && c.isAssignableFrom(o.getClass())) {
localScope.put(name, o);
}
} catch (final NoSuchMethodException e) {
try {
if (parent != null)
o = parent.getBean(name, c); // don't register locally if found
else {
LOG.debug("Could not create a {} via a Template method, trying via constructor.",c.getName());
o = createByConstructor(c);
if (o != null && c.isAssignableFrom(o.getClass())) {
localScope.put(name, o);
}
}
} catch (final RuntimeException e1) {
throw e1;
} catch (final Exception e1) {
throw new RuntimeException(e1);
}
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
return (T) o;
}
private static final Class<?>[] NO_PARAMS = new Class[0];
private static final Object[] NO_ARGS = new Object[0];
@SuppressWarnings("unchecked")
private static <T> T createByMethod(Object template, Class<T> c) throws SecurityException, NoSuchMethodException,
IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Method m;
m = template.getClass().getMethod("create" + c.getSimpleName(), NO_PARAMS);
if (m != null) {
Object o = m.invoke(template, NO_ARGS);
if (o != null && c.isAssignableFrom(o.getClass())) {
return (T) o;
}
}
return null;
}
private static <T> T createByConstructor(Class<T> c) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
if(c.isInterface()) return null; //TODO medium test for abstract class
final Constructor<T> constr = c.getConstructor(NO_PARAMS);
return constr.newInstance();
}
}