act.apidoc.Endpoint Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of act Show documentation
Show all versions of act Show documentation
The ACT full stack MVC framework
package act.apidoc;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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.
* #L%
*/
import static act.apidoc.SampleDataCategory.EMAIL;
import static act.apidoc.SimpleEndpointIdProvider.className;
import static act.apidoc.SimpleEndpointIdProvider.id;
import act.Act;
import act.app.data.StringValueResolverManager;
import act.conf.AppConfig;
import act.data.DataPropertyRepository;
import act.data.Sensitive;
import act.handler.RequestHandler;
import act.handler.builtin.controller.RequestHandlerProxy;
import act.handler.builtin.controller.impl.ReflectedHandlerInvoker;
import act.inject.DefaultValue;
import act.inject.DependencyInjector;
import act.inject.HeaderVariable;
import act.inject.SessionVariable;
import act.inject.param.NoBind;
import act.inject.param.ParamValueLoaderService;
import act.util.*;
import act.validation.NotBlank;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.bval.constraints.NotEmpty;
import org.joda.time.*;
import org.osgl.$;
import org.osgl.OsglConfig;
import org.osgl.http.H;
import org.osgl.inject.BeanSpec;
import org.osgl.inject.Injector;
import org.osgl.logging.Logger;
import org.osgl.mvc.annotation.*;
import org.osgl.mvc.result.Result;
import org.osgl.storage.ISObject;
import org.osgl.storage.impl.SObject;
import org.osgl.util.*;
import java.beans.Transient;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import javax.validation.constraints.NotNull;
/**
* An `Endpoint` represents an API that provides specific service
*/
public class Endpoint implements Comparable, EndpointIdProvider {
private static final Logger LOGGER = ApiManager.LOGGER;
private static BeanSpecInterpreter beanSpecInterpreter = new BeanSpecInterpreter();
private static boolean mapTypeQuerySampleWarned = false;
public static class ParamInfo {
public String bindName;
public transient BeanSpec beanSpec;
public String type;
public String description;
public String defaultValue;
public boolean required;
public boolean sessionVariable;
public boolean headerVariable;
public List options;
public String fieldKey;
private ParamInfo() {}
private ParamInfo(String bindName, BeanSpec beanSpec, String description, String fieldKey) {
this.bindName = bindName;
this.beanSpec = beanSpec;
this.description = description;
this.defaultValue = checkDefaultValue(beanSpec);
this.required = checkRequired(beanSpec);
this.sessionVariable = checkSessionVariable(beanSpec);
this.headerVariable = checkHeaderVariable(beanSpec);
this.options = checkOptions(beanSpec);
this.fieldKey = fieldKey;
}
public String getName() {
if (sessionVariable) {
return bindName + "[S]";
} else if (headerVariable) {
return bindName + "[H]";
}
return bindName;
}
public String getTooltip() {
if (sessionVariable) {
return "Session variable: " + bindName;
} else if (headerVariable) {
return "Header variable: " + bindName;
} else {
return "Bind name: " + bindName;
}
}
public String getType() {
return null == beanSpec ? type : beanSpecInterpreter.interpret(beanSpec);
}
public String getDescription() {
if (S.blank(description)) {
if (sessionVariable) {
return "Session variable";
} else if (headerVariable) {
return "Header variable";
}
}
return description;
}
public void setDescription(String description) {
this.description = description.replaceAll("\\n\\s+","\n");
}
public boolean isRequired() {
return required;
}
public List getOptions() {
return options;
}
public String getDefaultValue() {
return defaultValue;
}
private String checkDefaultValue(BeanSpec spec) {
DefaultValue def = spec.getAnnotation(DefaultValue.class);
if (null != def) {
return def.value();
}
Class type = spec.rawType();
if (type.isPrimitive()) {
Object o = Act.app().resolverManager().resolve("", type);
return null != o ? o.toString() : null;
}
return null;
}
private boolean checkRequired(BeanSpec spec) {
return (spec.hasAnnotation(NotNull.class)
|| spec.hasAnnotation(NotBlank.class)
|| spec.hasAnnotation(NotEmpty.class));
}
private boolean checkSessionVariable(BeanSpec spec) {
return spec.hasAnnotation(SessionVariable.class);
}
private boolean checkHeaderVariable(BeanSpec spec) {
return spec.hasAnnotation(HeaderVariable.class);
}
private List checkOptions(BeanSpec spec) {
Class type = spec.rawType();
if (type.isEnum()) {
return C.listOf(type.getEnumConstants()).map($.F.asString());
}
return null;
}
}
/**
* The scheme defines the protocol used to access the endpoint
*
* At the moment we support HTTP only
*/
public enum Scheme {
HTTP
}
/**
* unique identify an endpoint in an application.
*
* Generated from class and method name via {@link SimpleEndpointIdProvider#id(Class, Method)}
*/
public String id;
public transient EndpointIdProvider parent;
/**
* The scheme used to access the endpoint
*/
public Scheme scheme = Scheme.HTTP;
public int port;
/**
* The HTTP method
*/
public H.Method httpMethod;
/**
* The URL path
*/
public String path;
/**
* The handler.
*
* In most case should be `pkg.Class.method`
*/
public String handler;
FastJsonPropertyPreFilter fastJsonPropertyPreFilter;
/**
* The description
*/
public String description;
/**
* The return info description
*/
public String returnDescription;
public String module;
private transient BeanSpec returnType;
private Map typeLookups;
public String returnSample;
public transient Object returnSampleObject;
/**
* Param list.
*
* Only available when handler is driven by
* {@link act.handler.builtin.controller.impl.ReflectedHandlerInvoker}
*/
public List params = new ArrayList<>();
public String sampleJsonPost;
public String sampleQuery;
public Class controllerClass;
public transient SampleDataProviderManager sampleDataProviderManager;
private Endpoint() {}
Endpoint(int port, H.Method httpMethod, String path, RequestHandler handler) {
AppConfig conf = Act.appConfig();
this.httpMethod = $.requireNotNull(httpMethod);
String urlContext = conf.urlContext();
this.path = null == urlContext || path.startsWith("/~/") ? $.requireNotNull(path) : S.concat(urlContext, $.requireNotNull(path));
this.handler = handler.toString();
this.port = port;
this.sampleDataProviderManager = Act.app().sampleDataProviderManager();
explore(handler);
}
@Override
public int compareTo(Endpoint o) {
int n = path.compareTo(o.path);
if (0 != n) {
return n;
}
return httpMethod.ordinal() - o.httpMethod.ordinal();
}
@Override
public String toString() {
return id;
}
public String getId() {
return id;
}
@Override
public String getParentId() {
return null == parent ? null : parent.getId();
}
/**
* Returns extends id. This is the concatenation of
* {@link #httpMethod} and {@link #id}. This will
* be used by the frontend UI.
*
* @return the extended id
*/
public String getXid() {
return S.concat(httpMethod, id.replace('.', '_'));
}
public Scheme getScheme() {
return scheme;
}
public int getPort() {
return port;
}
public H.Method getHttpMethod() {
return httpMethod;
}
public String getPath() {
return path;
}
public String getPathHtml() {
String s = path.replace("{", "{");
return s.replace("}", "}");
}
public String getHandler() {
return handler;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = processTypeImplSubstitution(description);
}
public String getModule() {
return module;
}
public void setModule(String module) {
this.module = module;
}
public List getParams() {
return params;
}
public BeanSpec returnType() {
return returnType;
}
public String getReturnSample() {
return returnSample;
}
public String getReturnType() {
return null == returnType ? null : returnType.toString();
}
public String getSampleJsonPost() {
return sampleJsonPost;
}
public String getSampleQuery() {
return sampleQuery;
}
public Class controllerClass() {
return controllerClass;
}
public String processTypeImplSubstitution(String s) {
if (null == s) {
return null;
}
int n = s.indexOf("${");
if (n < 0) {
return s;
}
int a = 0;
int z = n;
S.Buffer buf = S.buffer();
while (true) {
buf.append(s.substring(a, z));
n = s.indexOf("}", z);
a = n;
E.illegalArgumentIf(n < -1, "Invalid string: " + s);
String key = s.substring(z + 2, a);
Class impl = typeLookups.get(key);
if (null != impl) {
buf.append(Keyword.of(className(impl)).readable().toLowerCase());
} else {
buf.append("${").append(key).append("}");
}
n = s.indexOf("${", a);
if (n < 0) {
buf.append(s.substring(a + 1));
return buf.toString();
}
z = n;
}
}
private void explore(RequestHandler handler) {
RequestHandlerProxy proxy = $.cast(handler);
ReflectedHandlerInvoker invoker = $.cast(proxy.actionHandler().invoker());
Class controllerClass = invoker.controllerClass();
typeLookups = Generics.buildTypeParamImplLookup(controllerClass);
Method method = invoker.method();
returnType = BeanSpec.of(method.getGenericReturnType(), Act.injector(), typeLookups);
PropertySpec pspec = method.getAnnotation(PropertySpec.class);
if (null != pspec) {
PropertySpec.MetaInfo propSpec = new PropertySpec.MetaInfo();
for (String v : pspec.value()) {
propSpec.onValue(v);
}
for (String v : pspec.http()) {
propSpec.onValue(v);
}
List outputs = propSpec.outputFieldsForHttp();
Set excluded = propSpec.excludeFieldsForHttp();
if (!(outputs.isEmpty() && excluded.isEmpty())) {
fastJsonPropertyPreFilter = new FastJsonPropertyPreFilter(returnType.rawType(), outputs, excluded, Act.app().service(DataPropertyRepository.class));
}
// just ignore cli value here
}
Module classModule = controllerClass.getAnnotation(Module.class);
String classModuleText = null == classModule ? inferModule(controllerClass) : classModule.value();
this.id = id(controllerClass, method);
Class superClass = controllerClass.getSuperclass();
try {
Method overwrittenMethod = superClass.getMethod(method.getName(), method.getParameterTypes());
parent = new SimpleEndpointIdProvider(overwrittenMethod.getDeclaringClass(), method);
} catch (Exception e) {
// so we don't have an overwritten method, that's fine, just ignore the exception
}
Map typeParamLookup = C.Map();
if (controllerClass.getGenericSuperclass() instanceof ParameterizedType) {
typeParamLookup = Generics.buildTypeParamImplLookup(controllerClass);
}
Description descAnno = method.getAnnotation(Description.class);
this.description = null == descAnno ? id(controllerClass, method) : descAnno.value();
Module methodModule = method.getAnnotation(Module.class);
this.module = null == methodModule ? classModuleText : methodModule.value();
boolean payloadMethod = H.Method.POST == httpMethod || H.Method.PUT == httpMethod || H.Method.PATCH == httpMethod;
boolean body = payloadMethod && null != invoker.singleJsonFieldName();
exploreParamInfo(method, typeParamLookup, body);
if (!Modifier.isStatic(method.getModifiers())) {
exploreParamInfo(controllerClass, typeParamLookup, body);
}
this.controllerClass = controllerClass;
try {
this.returnSample = null == returnType ? null : generateSampleJson(returnType, typeParamLookup, true);
} catch (Exception e) {
LOGGER.warn(e, "Error creating returnSample of endpoint for request handler [%s] for [%s %s]", handler, httpMethod, path);
}
}
private String inferModule(Class controllerClass) {
Class enclosingClass = controllerClass.getEnclosingClass();
if (null != enclosingClass) {
return inferModule(enclosingClass);
}
return controllerClass.getSimpleName();
}
private void exploreParamInfo(Method method, Map typeParamLookup, boolean body) {
Type[] paramTypes = method.getGenericParameterTypes();
int paramCount = paramTypes.length;
if (0 == paramCount) {
return;
}
DependencyInjector injector = Act.injector();
Method declaredMethod = overridenRequestHandlerMethod(method);
if (null == declaredMethod) {
return;
}
Annotation[][] allAnnos = declaredMethod.getParameterAnnotations();
Map sampleData = new HashMap<>();
StringValueResolverManager resolver = Act.app().resolverManager();
List sampleQuery = new ArrayList<>();
for (int i = 0; i < paramCount; ++i) {
Type type = paramTypes[i];
Annotation[] annos = allAnnos[i];
ParamInfo info = paramInfo(type, typeParamLookup, annos, injector, null, null, body);
if (null != info) {
params.add(info);
if (path.contains("{" + info.getName() + "}")) {
// no sample data for URL path variable
continue;
}
Object sample;
sample = generateSampleData(info.beanSpec, typeParamLookup, new HashSet(), new ArrayList(), false);
if (null == sample && null != info.defaultValue) {
sample = resolver.resolve(info.defaultValue, info.beanSpec.rawType());
}
if (H.Method.GET == this.httpMethod) {
String query = generateSampleQuery(info.beanSpec.withoutName(), typeParamLookup, info.bindName, new HashSet(), C.newList());
if (S.notBlank(query)) {
sampleQuery.add(query);
}
} else {
sampleData.put(info.bindName, sample);
}
}
}
if (!sampleData.isEmpty()) {
Object payload = sampleData;
if (sampleData.size() == 1) {
payload = sampleData.values().iterator().next();
}
if (null != payload && $.isSimpleType(payload.getClass())) {
payload = sampleData;
}
sampleJsonPost = null == payload ? null : JSON.toJSONString(payload, true);
}
if (!sampleQuery.isEmpty()) {
this.sampleQuery = S.join("&", sampleQuery);
}
}
// we don't need fields declared in `@NoBind` or `@Stateless` classes
private static final $.Predicate FIELD_PREDICATE = new $.Predicate() {
@Override
public boolean test(Field field) {
return !ParamValueLoaderService.shouldWaive(field);
}
};
private void exploreParamInfo(Class controller, Map typeParamLookup, boolean body) {
DependencyInjector injector = Act.injector();
List fields = $.fieldsOf(controller, FIELD_PREDICATE);
for (Field field : fields) {
Type type = field.getGenericType();
Annotation[] annos = field.getAnnotations();
String fieldKey = field.getDeclaringClass().getName().replace('$', '.') + "." + field.getName();
ParamInfo info = paramInfo(type, typeParamLookup, annos, injector, field.getName(), fieldKey, body);
if (null != info) {
params.add(info);
}
}
}
private ParamInfo paramInfo(Type type, Map typeParamLookup, Annotation[] annos, DependencyInjector injector, String name, String fieldKey, boolean body) {
if (isLoginUser(annos)) {
return null;
}
BeanSpec spec = BeanSpec.of(type, annos, name, injector, typeParamLookup);
if (ParamValueLoaderService.providedButNotDbBind(spec, injector)) {
return null;
}
if (ParamValueLoaderService.hasDbBind(spec.allAnnotations())) {
if (org.osgl.util.S.blank(name)) {
name = spec.name();
}
return new ParamInfo(name, BeanSpec.of(String.class, injector, typeParamLookup), name + " id", fieldKey);
}
String description = "";
Description descAnno = spec.getAnnotation(Description.class);
if (null != descAnno) {
description = descAnno.value();
}
return new ParamInfo(body ? spec.name() + " (body)" : spec.name(), spec, description, fieldKey);
}
private boolean isLoginUser(Annotation[] annos) {
for (Annotation a : annos) {
if ("LoginUser".equals(a.annotationType().getSimpleName())) {
return true;
}
}
return false;
}
private String generateSampleJson(BeanSpec spec, Map typeParamLookup, boolean isReturn) {
Class type = spec.rawType();
if (Result.class.isAssignableFrom(type) || void.class == type) {
return null;
}
returnSampleObject = generateSampleData(spec, typeParamLookup, new HashSet(), new ArrayList(), isReturn);
if (null == returnSampleObject) {
return null;
}
if (returnSampleObject instanceof Map && ((Map) returnSampleObject).isEmpty()) {
return null;
}
if ($.isSimpleType(type)) {
returnSampleObject = C.Map("result", returnSampleObject);
}
SerializeFilter[] filters = new SerializeFilter[null == fastJsonPropertyPreFilter ? 0 : 1];
if (null != fastJsonPropertyPreFilter) {
filters[0] = fastJsonPropertyPreFilter;
}
SerializerFeature[] features = new SerializerFeature[] {
SerializerFeature.PrettyFormat
};
return JSON.toJSONString(returnSampleObject, SerializeConfig.globalInstance, filters, features);
}
private String generateSampleQuery(BeanSpec spec, Map typeParamLookup, String bindName, Set typeChain, List nameChain) {
Class type = spec.rawType();
String specName = spec.name();
if (S.notBlank(specName)) {
nameChain.add(specName);
}
if ($.isSimpleType(type)) {
Object o = generateSampleData(spec, typeParamLookup, typeChain, nameChain, false);
if (null == o) {
return "";
}
return bindName + "=" + o;
}
if (type.isArray()) {
// TODO handle datetime component type
Class elementType = type.getComponentType();
BeanSpec elementSpec = BeanSpec.of(elementType, Act.injector(), typeParamLookup);
if ($.isSimpleType(elementType)) {
Object o = generateSampleData(elementSpec, typeParamLookup, typeChain, nameChain, false);
if (null == o) {
return "";
}
return bindName + "=" + o
+ "&" + bindName + "=" + generateSampleData(elementSpec, typeParamLookup, typeChain, nameChain, false);
}
} else if (Collection.class.isAssignableFrom(type)) {
// TODO handle datetime component type
List typeParams = spec.typeParams();
Type elementType = typeParams.isEmpty() ? Object.class : typeParams.get(0);
BeanSpec elementSpec = BeanSpec.of(elementType, null, Act.injector(), typeParamLookup);
if ($.isSimpleType(elementSpec.rawType())) {
Object o = generateSampleData(elementSpec, typeParamLookup, typeChain, nameChain, false);
if (null == o) {
return "";
}
return bindName + "=" + o
+ "&" + bindName + "=" + generateSampleData(elementSpec, typeParamLookup, typeChain, nameChain, false);
}
} else if (Map.class.isAssignableFrom(type)) {
if (!mapTypeQuerySampleWarned) {
mapTypeQuerySampleWarned = true;
LOGGER.warn("Query sample does not support Map type! Make sure your @GetAction handler params/fields be simple types");
}
return "";
} else if (ReadableInstant.class.isAssignableFrom(type)) {
return bindName + "=";
}
if (null != stringValueResolver(type)) {
return bindName + "=" + sampleDataFromAnnotation(spec, bindName);
}
List queryPairs = new ArrayList<>();
List fields = $.fieldsOf(type);
for (Field field : fields) {
if (ParamValueLoaderService.shouldWaive(field)) {
continue;
}
String fieldBindName = bindName + "." + field.getName();
String pair = generateSampleQuery(BeanSpec.of(field, Act.injector(), typeParamLookup), typeParamLookup, fieldBindName, C.newSet(typeChain), C.newList(nameChain));
if (S.notBlank(pair)) {
queryPairs.add(pair);
}
}
return S.join(queryPairs).by("&").get();
}
private static Object sampleDataFromAnnotation(BeanSpec spec, String bindName) {
SampleData.ProvidedBy annoProvided = spec.getAnnotation(SampleData.ProvidedBy.class);
if (null != annoProvided) {
return Act.getInstance(annoProvided.value()).get();
}
SampleData.StringList annoStrList = spec.getAnnotation(SampleData.StringList.class);
if (null != annoStrList) {
return $.random(annoStrList.value());
}
SampleData.IntList annoIntList = spec.getAnnotation(SampleData.IntList.class);
if (null != annoIntList) {
int[] ia = annoIntList.value();
return $.random(ia);
}
SampleData.DoubleList annoDblList = spec.getAnnotation(SampleData.DoubleList.class);
if (null != annoDblList) {
return $.random(annoDblList.value());
}
SampleData.Category anno = spec.getAnnotation(SampleData.Category.class);
SampleDataCategory category = null != anno ? anno.value() : null;
return Act.getInstance(SampleDataProviderManager.class).getSampleData(category, bindName, spec.rawType());
}
private static boolean isCollection(Type type) {
if (type instanceof Class) {
Class clazz = $.cast(type);
if (Iterable.class.isAssignableFrom(clazz)) {
return true;
}
return false;
}
if (type instanceof ParameterizedType) {
ParameterizedType ptype = $.cast(type);
return isCollection(ptype.getRawType());
}
return false;
}
private Object generateSampleData(BeanSpec spec, Map typeParamLookup, Set typeChain, List nameChain, boolean isReturn) {
return generateSampleData(spec, typeParamLookup, typeChain, nameChain, isReturn ? fastJsonPropertyPreFilter : null, isReturn);
}
public static Object generateSampleData(
BeanSpec spec, Map typeParamLookup,
Set typeChain, List nameChain,
FastJsonPropertyPreFilter fastJsonPropertyPreFilter,
boolean isReturn
) {
Type type = spec.type();
if (void.class == type) {
return null;
}
if (typeChain.contains(type) && !isCollection(type) && !$.isSimpleType(spec.rawType())) {
return S.concat(spec.name(), ":", type); // circular reference detected
}
typeChain.add(type);
String name = spec.name();
if (S.notBlank(name)) {
nameChain.add(name);
}
if (null != fastJsonPropertyPreFilter) {
String path = S.join(nameChain).by(".").get();
if (S.notBlank(path) && !fastJsonPropertyPreFilter.matches(path)) {
return null;
}
}
Object o = sampleDataFromAnnotation(spec, name);
if (null != o) {
return o;
}
Class classType = spec.rawType();
SampleDataProviderManager sampleDataProviderManager = Act.getInstance(SampleDataProviderManager.class);
SampleData.Category anno = spec.getAnnotation(SampleData.Category.class);
SampleDataCategory category = null != anno ? anno.value() : null;
try {
if (void.class == classType || Void.class == classType || Result.class.isAssignableFrom(classType)) {
return null;
}
if (Object.class == classType) {
return "";
}
try {
if (classType.isEnum()) {
Object[] ea = classType.getEnumConstants();
int len = ea.length;
return 0 < len ? ea[N.randInt(len)] : null;
} else if (Locale.class == classType) {
return (Act.appConfig().locale());
} else if (String.class == classType || Keyword.class == classType) {
String mockValue = sampleDataProviderManager.getSampleData(category, name, String.class);
if (spec.hasAnnotation(Sensitive.class)) {
return Act.crypto().encrypt(mockValue);
}
return Keyword.class == classType ? Keyword.of(mockValue) : mockValue;
} else if (classType.isArray()) {
Object sample = Array.newInstance(classType.getComponentType(), 2);
Array.set(sample, 0, generateSampleData(BeanSpec.of(classType.getComponentType(), Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn));
Array.set(sample, 1, generateSampleData(BeanSpec.of(classType.getComponentType(), Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn));
return sample;
} else if ($.isSimpleType(classType)) {
if (Enum.class == classType) {
return "";
}
if (!classType.isPrimitive()) {
classType = $.primitiveTypeOf(classType);
}
return StringValueResolver.predefined().get(classType).resolve(null);
} else if (LocalDateTime.class.isAssignableFrom(classType)) {
return sampleDataProviderManager.getSampleData(category, name, LocalDateTime.class);
} else if (DateTime.class.isAssignableFrom(classType)) {
return sampleDataProviderManager.getSampleData(category, name, DateTime.class);
} else if (LocalDate.class.isAssignableFrom(classType)) {
return sampleDataProviderManager.getSampleData(category, name, LocalDate.class);
} else if (LocalTime.class.isAssignableFrom(classType)) {
return LocalTime.now();
} else if (Date.class.isAssignableFrom(classType)) {
return sampleDataProviderManager.getSampleData(category, name, Date.class);
} else if (BigDecimal.class == classType) {
return BigDecimal.valueOf(1.1);
} else if (BigInteger.class == classType) {
return BigInteger.valueOf(1);
} else if (ISObject.class.isAssignableFrom(classType)) {
return SObject.of("blob data");
} else if (Map.class.isAssignableFrom(classType)) {
Map map = $.cast(Act.getInstance(classType));
List typeParams = spec.typeParams();
if (typeParams.isEmpty()) {
typeParams = Generics.tryGetTypeParamImplementations(classType, Map.class);
}
if (typeParams.size() < 2) {
return null;
} else {
Type keyType = typeParams.get(0);
Type valType = typeParams.get(1);
if (Object.class == valType) {
return null;
}
Object key1 = "foo";
Object key2 = "bar";
if (keyType != String.class) {
key1 = generateSampleData(BeanSpec.of(keyType, null, Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn);
key2 = generateSampleData(BeanSpec.of(keyType, null, Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn);
}
Object val1 = generateSampleData(BeanSpec.of(valType, null, Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn);
Object val2 = generateSampleData(BeanSpec.of(valType, null, Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn);
map.put(key1, val1);
map.put(key2, val2);
}
return map;
} else if (Iterable.class.isAssignableFrom(classType)) {
Collection col = $.cast(Act.getInstance(classType));
List typeParams = spec.typeParams();
if (typeParams.isEmpty()) {
typeParams = Generics.tryGetTypeParamImplementations(classType, Map.class);
}
if (typeParams.isEmpty()) {
return null;
} else {
Type componentType = typeParams.get(0);
col.add(generateSampleData(BeanSpec.of(componentType, null, Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn));
col.add(generateSampleData(BeanSpec.of(componentType, null, Act.injector(), typeParamLookup), typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn));
}
return col;
}
Object obj = sampleDataProviderManager.getSampleData(category, name, classType);
if (null != obj) {
return obj;
}
Injector injector = Act.injector();
try {
obj = Act.getInstance(classType);
} catch (Exception e) {
Method emailGetter = null;
Map map = new HashMap<>();
Method[] ma = classType.getMethods();
for (Method m : ma) {
if (!Modifier.isStatic(m.getModifiers()) && m.getName().startsWith("get") && m.getReturnType() != void.class) {
if (shouldWaive(m, classType)) {
continue;
}
Class propertyClass = m.getReturnType();
Object val;
try {
String propertyName = m.getName().substring(3);
if ("name".equalsIgnoreCase(propertyName)) {
propertyName = m.getDeclaringClass().getSimpleName();
}
Annotation[] annotations = m.getDeclaredAnnotations();
Type propertyType = m.getGenericReturnType();
if (propertyType instanceof TypeVariable) {
propertyType = propertyClass;
}
BeanSpec propertySpec = BeanSpec.of(propertyType, annotations, propertyName, injector, m.getModifiers(), typeParamLookup);
if (null == emailGetter && isEmail(propertySpec)) {
emailGetter = m;
} else {
val = generateSampleData(propertySpec, typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn);
if (null != val) {
Class valType = val.getClass();
if (!propertyClass.isAssignableFrom(valType)) {
val = $.convert(val).to(propertyClass);
}
if (null != val) {
map.put(propertyName, val);
}
}
}
} catch (Exception e1) {
}
}
}
if (null != emailGetter) {
String mockEmail = sampleDataProviderManager.getSampleData(SampleDataCategory.EMAIL, name, String.class);
map.put(emailGetter.getName().substring(3), mockEmail);
}
return map;
}
List fields = $.fieldsOf(classType);
Field emailField = null;
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (!isReturn) {
if (ParamValueLoaderService.shouldWaive(field)) {
continue;
}
} else {
// for return type we shouldn't waive NoBind fields
int modifiers = field.getModifiers();
if (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)) {
continue;
}
String fieldName = field.getName();
Class entityType = field.getDeclaringClass();
boolean shouldWaive = Object.class.equals(entityType)
|| Class.class.equals(entityType)
|| OsglConfig.globalMappingFilter_shouldIgnore(fieldName);
if (shouldWaive) {
continue;
}
}
Class fieldClass = field.getType();
Object val = null;
try {
field.setAccessible(true);
String fieldName = field.getName();
if ("name".equalsIgnoreCase(fieldName)) {
fieldName = field.getDeclaringClass().getSimpleName();
}
Annotation[] annotations = field.getDeclaredAnnotations();
Type fieldType = field.getGenericType();
if (fieldType instanceof TypeVariable) {
fieldType = fieldClass;
}
BeanSpec fieldSpec = BeanSpec.of(fieldType, annotations, fieldName, injector, field.getModifiers(), typeParamLookup);
if (null == emailField && isEmail(fieldSpec)) {
if (null != fastJsonPropertyPreFilter) {
List newNameChain = C.newList(nameChain).append(fieldSpec.name());
String path = S.join(newNameChain).by(".").get();
if (fastJsonPropertyPreFilter.matches(path)) {
emailField = field;
}
} else {
emailField = field;
}
} else {
val = generateSampleData(fieldSpec, typeParamLookup, C.newSet(typeChain), C.newList(nameChain), fastJsonPropertyPreFilter, isReturn);
if (null == val) {
continue;
}
Class valType = val.getClass();
if (!fieldClass.isAssignableFrom(valType)) {
val = $.convert(val).to(fieldClass);
}
if (null != val) {
if (valType == String.class && fieldSpec.hasAnnotation(Sensitive.class)) {
val = Act.app().crypto().encrypt((String) val);
}
field.set(obj, val);
}
}
} catch (Exception e2) {
LOGGER.warn("Error setting value[%s] to field[%s.%s]", val, classType.getSimpleName(), field.getName());
}
}
if (null != emailField) {
String mockEmail = sampleDataProviderManager.getSampleData(SampleDataCategory.EMAIL, name, String.class);
$.setFieldValue(obj, emailField, mockEmail);
}
return obj;
} catch (Exception e) {
LOGGER.warn("error generating sample data for type: %s", classType);
return null;
}
} finally {
//typeChain.remove(classType);
}
}
private static boolean isEmail(BeanSpec spec) {
SampleData.Category anno = spec.getAnnotation(SampleData.Category.class);
SampleDataCategory category = null != anno ? anno.value() : null;
if (null != category && category != EMAIL) {
return false;
}
category = SampleDataCategory.of(spec.name());
return category == EMAIL;
}
private static StringValueResolver stringValueResolver(Class type) {
return Act.app().resolverManager().resolver(type);
}
// see ParamValueLoaderService.shouldWaive(Field)
private static boolean shouldWaive(Method getter, Class implementClass) {
int modifiers = getter.getModifiers();
if (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)) {
return true;
}
String fieldName = getter.getName().substring(3);
Class entityType = Generics.getReturnType(getter, implementClass);
return ParamValueLoaderService.noBind(entityType)
|| getter.isAnnotationPresent(NoBind.class)
|| getter.isAnnotationPresent(Stateless.class)
|| getter.isAnnotationPresent(Global.class)
|| ParamValueLoaderService.isInBlackList(fieldName)
|| Object.class.equals(entityType)
|| Class.class.equals(entityType)
|| OsglConfig.globalMappingFilter_shouldIgnore(fieldName);
}
private static Method overridenRequestHandlerMethod(Method method) {
if (!Modifier.isPublic(method.getModifiers())) {
return null;
}
if (isRequestHandler(method)) {
return method;
}
Method overridenMethod = overridenMethod(method);
return null == overridenMethod ? null : overridenRequestHandlerMethod(overridenMethod);
}
private static Method overridenMethod(Method method) {
Class host = method.getDeclaringClass();
Class superHost = host.getSuperclass();
if (null == superHost || Object.class == superHost) {
return null;
}
Method[] ma = superHost.getMethods();
for (Method m : ma) {
if (m.getName().equals(method.getName()) && $.eq2(method.getParameterTypes(), method.getParameterTypes())) {
return m;
}
}
return null;
}
private static boolean isRequestHandler(Method method) {
return method.isAnnotationPresent(Action.class)
|| method.isAnnotationPresent(GetAction.class)
|| method.isAnnotationPresent(PutAction.class)
|| method.isAnnotationPresent(PostAction.class)
|| method.isAnnotationPresent(PatchAction.class)
|| method.isAnnotationPresent(DeleteAction.class);
}
}