This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 81485f7 Code improvements around Swagger support.
81485f7 is described below
commit 81485f72ac73980d84c935d05735b6b913a6d68f
Author: JamesBognar <[email protected]>
AuthorDate: Wed Mar 14 18:56:36 2018 -0400
Code improvements around Swagger support.
---
.../java/org/apache/juneau/PojoExamplesTest.java | 247 +++++++
juneau-core/juneau-marshall/TODO.txt | 12 +-
.../main/java/org/apache/juneau/BeanContext.java | 77 +-
.../java/org/apache/juneau/BeanContextBuilder.java | 19 +
.../src/main/java/org/apache/juneau/BeanMeta.java | 14 +-
.../java/org/apache/juneau/BeanPropertyMeta.java | 7 +-
.../apache/juneau/BeanProxyInvocationHandler.java | 8 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 154 ++--
.../apache/juneau/ClassMetaRuntimeException.java | 73 ++
.../main/java/org/apache/juneau/Visibility.java | 55 +-
.../java/org/apache/juneau/annotation/Example.java | 44 ++
.../org/apache/juneau/internal/ClassFlags.java | 55 ++
.../org/apache/juneau/internal/ClassUtils.java | 785 ++++++++++++++++++++-
.../org/apache/juneau/transform/BuilderSwap.java | 9 +-
.../org/apache/juneau/transform/SurrogateSwap.java | 29 +-
.../java/org/apache/juneau/utils/MetadataMap.java | 4 +-
juneau-doc/src/main/javadoc/overview.html | 90 ++-
juneau-examples/juneau-examples-rest/examples.cfg | 6 +
.../apache/juneau/examples/rest/RootResources.java | 1 +
.../juneau/examples/rest/StaticFilesResource.java | 59 ++
.../juneau/examples/rest/files/petstore.html | 213 ++++++
.../java/org/apache/juneau/rest/RestContext.java | 27 +-
.../org/apache/juneau/rest/RestContextBuilder.java | 9 +-
.../org/apache/juneau/rest/RestJavaMethod.java | 2 +-
24 files changed, 1773 insertions(+), 226 deletions(-)
diff --git
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PojoExamplesTest.java
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PojoExamplesTest.java
new file mode 100644
index 0000000..7c232b0
--- /dev/null
+++
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PojoExamplesTest.java
@@ -0,0 +1,247 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you 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 org.apache.juneau;
+
+import static org.junit.Assert.*;
+import static org.apache.juneau.TestUtils.*;
+
+import org.apache.juneau.annotation.*;
+import org.junit.*;
+
+/*
+ * Tests the BEAN_examples property and @Example annotation.
+ */
+public class PojoExamplesTest {
+
+
//====================================================================================================
+ // test BEAN_examples
+
//====================================================================================================
+ @Test
+ public void testProperty() throws Exception {
+ BeanSession bs = BeanContext.create().example(A.class, new
A().init()).build().createSession();
+ assertObjectEquals("{f1:'f1a'}",
bs.getClassMeta(A.class).getExample(bs));
+ }
+
+ public static class A {
+ public String f1;
+
+ public A init() {
+ this.f1 = "f1a";
+ return this;
+ }
+ }
+
+
//====================================================================================================
+ // test @Example on public field
+
//====================================================================================================
+ @Test
+ public void testExampleField() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1b'}",
bs.getClassMeta(B1.class).getExample(bs));
+ }
+
+ public static class B1 {
+ public String f1;
+
+ @Example
+ public static B1 EXAMPLE = new B1().init();
+
+ public B1 init() {
+ this.f1 = "f1b";
+ return this;
+ }
+ }
+
+
//====================================================================================================
+ // test @Example on private field
+
//====================================================================================================
+ @Test
+ public void testExampleFieldPrivate() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1b'}",
bs.getClassMeta(B2.class).getExample(bs));
+ }
+
+ public static class B2 {
+ public String f1;
+
+ @Example
+ private static B2 EXAMPLE = new B2().init();
+
+ public B2 init() {
+ this.f1 = "f1b";
+ return this;
+ }
+ }
+
+
//====================================================================================================
+ // test @Example on public no-arg method.
+
//====================================================================================================
+ @Test
+ public void testExampleOnPublicNoArgMethod() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1c'}",
bs.getClassMeta(C1.class).getExample(bs));
+ }
+
+ public static class C1 {
+ public String f1;
+
+ public C1 init() {
+ this.f1 = "f1c";
+ return this;
+ }
+
+ @Example
+ public static C1 x() {
+ return new C1().init();
+ }
+ }
+
+
//====================================================================================================
+ // test @Example on private no-arg method.
+
//====================================================================================================
+ @Test
+ public void testExampleOnPrivateNoArgMethod() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1c'}",
bs.getClassMeta(C2.class).getExample(bs));
+ }
+
+ public static class C2 {
+ public String f1;
+
+ public C2 init() {
+ this.f1 = "f1c";
+ return this;
+ }
+
+ @Example
+ private static C2 x() {
+ return new C2().init();
+ }
+ }
+
+
//====================================================================================================
+ // test @Example on public 1-arg method
+
//====================================================================================================
+ @Test
+ public void testExampleOnPublicOneArgMethod() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1d'}",
bs.getClassMeta(D1.class).getExample(bs));
+ }
+
+ public static class D1 {
+ public String f1;
+
+ public D1 init() {
+ this.f1 = "f1d";
+ return this;
+ }
+
+ @Example
+ public static D1 x(BeanSession bs) {
+ return new D1().init();
+ }
+ }
+
+
//====================================================================================================
+ // test example() method, no annotation.
+
//====================================================================================================
+ @Test
+ public void testExampleMethod() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1e'}",
bs.getClassMeta(E1.class).getExample(bs));
+ }
+
+ public static class E1 {
+ public String f1;
+
+ public E1 init() {
+ this.f1 = "f1e";
+ return this;
+ }
+
+ public static E1 example() {
+ return new E1().init();
+ }
+ }
+
+
//====================================================================================================
+ // test example(BeanSession) method, no annotation.
+
//====================================================================================================
+ @Test
+ public void testExampleBeanSessionMethod() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ assertObjectEquals("{f1:'f1e'}",
bs.getClassMeta(E2.class).getExample(bs));
+ }
+
+ public static class E2 {
+ public String f1;
+
+ public E2 init() {
+ this.f1 = "f1e";
+ return this;
+ }
+
+ public static E2 example(BeanSession bs) {
+ return new E2().init();
+ }
+ }
+
+
//====================================================================================================
+ // test invalid uses of @Example
+
//====================================================================================================
+ @Test
+ public void testInvalidUsesOfExample() throws Exception {
+ BeanSession bs = BeanContext.create().build().createSession();
+ try {
+ bs.getClassMeta(F1.class);
+ } catch (Exception e) {
+ assertEquals("@Example used on invalid method
'org.apache.juneau.PojoExamplesTest$F1.example(String)'", e.getMessage());
+ }
+ try {
+ bs.getClassMeta(F2.class);
+ } catch (Exception e) {
+ assertEquals("@Example used on invalid method
'org.apache.juneau.PojoExamplesTest$F2.example()'", e.getMessage());
+ }
+ try {
+ bs.getClassMeta(F3.class);
+ } catch (Exception e) {
+ assertEquals("@Example used on invalid field 'public
static java.lang.String org.apache.juneau.PojoExamplesTest$F3.F3'",
e.getMessage());
+ }
+ try {
+ bs.getClassMeta(F4.class);
+ } catch (Exception e) {
+ assertEquals("@Example used on invalid field 'public
org.apache.juneau.PojoExamplesTest$F4
org.apache.juneau.PojoExamplesTest$F4.f4'", e.getMessage());
+ }
+ }
+
+ public static class F1 {
+ @Example
+ public static F1 example(String s) {
+ return null;
+ }
+ }
+ public static class F2 {
+ @Example
+ public F2 example() {
+ return null;
+ }
+ }
+ public static class F3 {
+ @Example
+ public static String F3 = "foo";
+ }
+ public static class F4 {
+ @Example
+ public F4 f4 = new F4();
+ }
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/TODO.txt
b/juneau-core/juneau-marshall/TODO.txt
index b6de073..31d37b1 100644
--- a/juneau-core/juneau-marshall/TODO.txt
+++ b/juneau-core/juneau-marshall/TODO.txt
@@ -11,14 +11,12 @@
* specific language governing permissions and limitations under the License.
*
***************************************************************************************************************************
-Create tests that ensure serializers don't close output but parsers do close
input.
+Document properties in config file.
-Content tests on examples rest.
REST example showing how to use NLS.
REST example showing how to customize look-and-feel.
+REST example showing static resources.
+Add doc link to @Example class.
+
+
-Review Javadocs:
-BeanContextBuilder
-BeanFilterBuilder
-@Bean
-@BeanProperty
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index c5e9fbc..7f69424 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -793,6 +793,59 @@ public class BeanContext extends Context {
public static final String BEAN_debug = PREFIX + "debug.b";
/**
+ * Configuration property: POJO examples.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"BeanContext.examples.smo"</js>
+ * <li><b>Data type:</b> <code>Map<String,Object></code>
+ * <li><b>Default:</b> <code>{}</code>
+ * <li><b>Session-overridable:</b> <jk>false</jk>
+ * <li><b>Annotations:</b>
+ * <ul>
+ * <li class='ja'>{@link Example}
+ * </ul>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
BeanContextBuilder#example(Class,Object)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Specifies an example of the specified class.
+ *
+ * <p>
+ * Examples are used in cases such as POJO examples in Swagger
documents.
+ *
+ * <p>
+ * Setting applies to specified class and all subclasses.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <jc>// Create a serializer that excludes the 'foo' and 'bar'
properties on the MyBean class.</jc>
+ * WriterSerializer s = JsonSerializer
+ * .<jsm>create</jsm>()
+ * .example(MyBean.<jk>class</jk>, <jk>new</jk>
MyBean().foo(<js>"foo"</js>).bar(123))
+ * .build();
+ *
+ * <jc>// Same, but use property.</jc>
+ * WriterSerializer s = JsonSerializer
+ * .<jsm>create</jsm>()
+ * .addTo(<jsf>BEAN_examples</jsf>,
MyBean.<jk>class</jk>.getName(), <jk>new</jk>
MyBean().foo(<js>"foo"</js>).bar(123))
+ * .build();
+ * </p>
+ *
+ * POJO examples can also be defined on classes via the following:
+ * <ul class='spaced-list'>
+ * <li>A static field annotated with {@link Example @Example}.
+ * <li>A static method annotated with {@link Example @Example}
with zero arguments or one {@link BeanSession} argument.
+ * <li>A static method with name <code>example</code> with no
arguments or one {@link BeanSession} argument.
+ * </ul>
+ */
+ public static final String BEAN_examples = PREFIX + "examples.smo";
+
+ /**
* Configuration property: Bean property excludes.
*
* <h5 class='section'>Property:</h5>
@@ -1758,6 +1811,7 @@ public class BeanContext extends Context {
final String[] notBeanPackageNames, notBeanPackagePrefixes;
final BeanFilter[] beanFilters;
final PojoSwap<?,?>[] pojoSwaps;
+ final Map<String,?> examples;
final BeanRegistry beanRegistry;
final Map<String,Class<?>> implClasses;
final Locale locale;
@@ -1846,6 +1900,8 @@ public class BeanContext extends Context {
}
pojoSwaps = lpf.toArray(new PojoSwap[lpf.size()]);
+ examples = getMapProperty(BEAN_examples, Object.class);
+
implClasses = getClassMapProperty(BEAN_implClasses);
Map<String,String[]> m2 = new HashMap<>();
@@ -1864,8 +1920,8 @@ public class BeanContext extends Context {
if (! cmCacheCache.containsKey(beanHashCode)) {
ConcurrentHashMap<Class,ClassMeta> cm = new
ConcurrentHashMap<>();
- cm.putIfAbsent(String.class, new
ClassMeta(String.class, this, null, null, findPojoSwaps(String.class),
findChildPojoSwaps(String.class)));
- cm.putIfAbsent(Object.class, new
ClassMeta(Object.class, this, null, null, findPojoSwaps(Object.class),
findChildPojoSwaps(Object.class)));
+ cm.putIfAbsent(String.class, new
ClassMeta(String.class, this, null, null, findPojoSwaps(String.class),
findChildPojoSwaps(String.class), null));
+ cm.putIfAbsent(Object.class, new
ClassMeta(Object.class, this, null, null, findPojoSwaps(Object.class),
findChildPojoSwaps(Object.class), null));
cmCacheCache.putIfAbsent(beanHashCode, cm);
}
cmCache = cmCacheCache.get(beanHashCode);
@@ -2074,7 +2130,7 @@ public class BeanContext extends Context {
// Note that if it has a pojo swap, we still want to cache it
so that
// we can cache something like byte[] with ByteArrayBase64Swap.
if (type.isArray() && findPojoSwaps(type) == null)
- return new ClassMeta(type, this, findImplClass(type),
findBeanFilter(type), findPojoSwaps(type), findChildPojoSwaps(type));
+ return new ClassMeta(type, this, findImplClass(type),
findBeanFilter(type), findPojoSwaps(type), findChildPojoSwaps(type),
findExample(type));
// This can happen if we have transforms defined against String
or Object.
if (cmCache == null)
@@ -2087,7 +2143,7 @@ public class BeanContext extends Context {
// Make sure someone didn't already set it
while this thread was blocked.
cm = cmCache.get(type);
if (cm == null)
- cm = new ClassMeta<>(type, this,
findImplClass(type), findBeanFilter(type), findPojoSwaps(type),
findChildPojoSwaps(type));
+ cm = new ClassMeta<>(type, this,
findImplClass(type), findBeanFilter(type), findPojoSwaps(type),
findChildPojoSwaps(type), findExample(type));
}
}
if (waitForInit)
@@ -2401,6 +2457,19 @@ public class BeanContext extends Context {
}
return null;
}
+
+ private final <T> T findExample(Class<T> c) {
+ if (c != null) {
+ Object o = examples.get(c.getName());
+ if (o != null)
+ return (T)o;
+ Class<T> c2 = (Class<T>)findImplClass(c);
+ if (c2 == null)
+ return null;
+ return (T)examples.get(c2.getName());
+ }
+ return null;
+ }
/**
* Checks whether a class has a {@link PojoSwap} associated with it in
this bean context.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContextBuilder.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContextBuilder.java
index adddb19..dd3ca9c 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContextBuilder.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContextBuilder.java
@@ -590,6 +590,25 @@ public class BeanContextBuilder extends ContextBuilder {
}
/**
+ * Configuration property: POJO example.
+ *
+ * <p>
+ * Specifies an example of the specified class.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link BeanContext#BEAN_examples}
+ * </ul>
+ *
+ * @param pojoClass The POJO class.
+ * @param o An instance of the POJO class used for examples.
+ * @return This object (for method chaining).
+ */
+ public <T> BeanContextBuilder example(Class<T> pojoClass, T o) {
+ return addTo(BEAN_examples, pojoClass.getName(), o);
+ }
+
+ /**
* Configuration property: Bean property excludes.
*
* <p>
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index 1bf3d5d..70e48f3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -12,8 +12,8 @@
//
***************************************************************************************************************************
package org.apache.juneau;
-import static org.apache.juneau.Visibility.*;
import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.internal.ClassFlags.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.ReflectionUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
@@ -219,8 +219,7 @@ public class BeanMeta<T> {
constructorArgs =
split(x.getAnnotation(BeanConstructor.class).properties());
if (constructorArgs.length !=
x.getParameterTypes().length)
throw new
BeanRuntimeException(c, "Number of properties defined in '@BeanConstructor'
annotation does not match number of parameters in constructor.");
- if (!
setAccessible(constructor))
- throw new
BeanRuntimeException(c, "Could not set accessibility to true on method with
@BeanConstructor annotation. Method=''{0}''", constructor.getName());
+ setAccessible(constructor,
false);
}
}
@@ -234,8 +233,7 @@ public class BeanMeta<T> {
if (constructor == null && beanFilter == null
&& ctx.beansRequireDefaultConstructor)
return "Class does not have the
required no-arg constructor";
- if (! setAccessible(constructor))
- throw new BeanRuntimeException(c,
"Could not set accessibility to true on no-arg constructor");
+ setAccessible(constructor, false);
// Explicitly defined property names in @Bean
annotation.
Set<String> fixedBeanProps = new
LinkedHashSet<>();
@@ -559,8 +557,7 @@ public class BeanMeta<T> {
for (Class<?> c2 : findClasses(c, stopClass)) {
for (Method m : c2.getDeclaredMethods()) {
- int mod = m.getModifiers();
- if (Modifier.isStatic(mod))
+ if (isStatic(m))
continue;
if (m.isBridge()) // This eliminates methods
with covariant return types from parent classes on child classes.
continue;
@@ -637,8 +634,7 @@ public class BeanMeta<T> {
List<Field> l = new LinkedList<>();
for (Class<?> c2 : findClasses(c, stopClass)) {
for (Field f : c2.getDeclaredFields()) {
- int m = f.getModifiers();
- if (Modifier.isStatic(m) ||
Modifier.isTransient(m))
+ if (isAny(f, STATIC, TRANSIENT))
continue;
if (f.isAnnotationPresent(BeanIgnore.class))
continue;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 1f9c64a..b54d642 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -12,7 +12,6 @@
//
***************************************************************************************************************************
package org.apache.juneau;
-import static org.apache.juneau.Visibility.*;
import static org.apache.juneau.internal.ArrayUtils.*;
import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
@@ -309,19 +308,19 @@ public final class BeanPropertyMeta {
}
BeanPropertyMeta.Builder setGetter(Method getter) {
- setAccessible(getter);
+ setAccessible(getter, false);
this.getter = getter;
return this;
}
BeanPropertyMeta.Builder setSetter(Method setter) {
- setAccessible(setter);
+ setAccessible(setter, false);
this.setter = setter;
return this;
}
BeanPropertyMeta.Builder setField(Field field) {
- setAccessible(field);
+ setAccessible(field, false);
this.field = field;
return this;
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
index 4b5626d..095c177 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
@@ -12,6 +12,8 @@
//
***************************************************************************************************************************
package org.apache.juneau;
+import static org.apache.juneau.internal.ClassUtils.*;
+
import java.lang.reflect.*;
import java.util.*;
@@ -47,7 +49,7 @@ public class BeanProxyInvocationHandler<T> implements
InvocationHandler {
@Override /* InvocationHandler */
public Object invoke(Object proxy, Method method, Object[] args) {
Class<?>[] paramTypes = method.getParameterTypes();
- if (method.getName().equals("equals") && (paramTypes.length ==
1) && (paramTypes[0] == java.lang.Object.class)) {
+ if (hasName(method, "equals") && hasArgs(method,
java.lang.Object.class)) {
Object arg = args[0];
if (arg == null)
return false;
@@ -63,10 +65,10 @@ public class BeanProxyInvocationHandler<T> implements
InvocationHandler {
return this.beanProps.equals(bean);
}
- if (method.getName().equals("hashCode") && (paramTypes.length
== 0))
+ if (hasName(method, "hashCode") && (paramTypes.length == 0))
return Integer.valueOf(this.beanProps.hashCode());
- if (method.getName().equals("toString") && (paramTypes.length
== 0))
+ if (hasName(method, "toString") && (paramTypes.length == 0))
return
JsonSerializer.DEFAULT_LAX.toString(this.beanProps);
String prop = this.meta.getterProps.get(method);
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index c905094..f3b9c4f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -14,6 +14,7 @@ package org.apache.juneau;
import static org.apache.juneau.ClassMeta.ClassCategory.*;
import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.internal.ClassFlags.*;
import static org.apache.juneau.internal.ReflectionUtils.*;
import java.io.*;
@@ -79,7 +80,10 @@ public final class ClassMeta<T> implements Type {
numberConstructorType;
private final Method
swapMethod, // The
swap() method (if it has one).
- unswapMethod; // The
unswap() method (if it has one).
+ unswapMethod, // The
unswap() method (if it has one).
+ exampleMethod; // The
example() or @Example-annotated method (if it has one).
+ private final Field
+ exampleField; // The
@Example-annotated field (if it has one).
private final Setter
namePropertyMethod, // The
method to set the name on an object (if it has one).
parentPropertyMethod; // The
method to set the parent on an object (if it has one).
@@ -113,7 +117,8 @@ public final class ClassMeta<T> implements Type {
private final InvocationHandler invocationHandler; // The
invocation handler for this class (if it has one).
private final BeanRegistry beanRegistry; // The bean
registry of this class meta (if it has one).
private final ClassMeta<?>[] args; // Arg types if
this is an array of args.
-
+ private final T example; // Example
object.
+
private ReadWriteLock lock = new ReentrantReadWriteLock(false);
private Lock rLock = lock.readLock(), wLock = lock.writeLock();
@@ -140,9 +145,10 @@ public final class ClassMeta<T> implements Type {
* Used for delayed initialization when the possibility of class
reference loops exist.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
- ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends
T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[]
childPojoSwaps) {
+ ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends
T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[]
childPojoSwaps, T example) {
this.innerClass = innerClass;
this.beanContext = beanContext;
+ this.example = example;
wLock.lock();
try {
@@ -188,6 +194,8 @@ public final class ClassMeta<T> implements Type {
this.childUnswapMap = builder.childUnswapMap;
this.childSwapMap = builder.childSwapMap;
this.childPojoSwaps = builder.childPojoSwaps;
+ this.exampleMethod = builder.exampleMethod;
+ this.exampleField = builder.exampleField;
this.args = null;
} finally {
wLock.unlock();
@@ -247,6 +255,9 @@ public final class ClassMeta<T> implements Type {
this.extMeta = mainType.extMeta;
this.initException = mainType.initException;
this.beanRegistry = mainType.beanRegistry;
+ this.exampleMethod = mainType.exampleMethod;
+ this.exampleField = mainType.exampleField;
+ this.example = mainType.example;
this.args = null;
}
@@ -294,6 +305,9 @@ public final class ClassMeta<T> implements Type {
this.extMeta = new MetadataMap();
this.initException = null;
this.beanRegistry = null;
+ this.exampleMethod = null;
+ this.exampleField = null;
+ this.example = null;
}
@SuppressWarnings({"unchecked","rawtypes","hiding"})
@@ -343,6 +357,8 @@ public final class ClassMeta<T> implements Type {
ConcurrentHashMap<Class<?>,PojoSwap<?,?>>
childSwapMap,
childUnswapMap;
+ Method exampleMethod;
+ Field exampleField;
ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext,
Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps,
PojoSwap<?,?>[] childPojoSwaps) {
this.innerClass = innerClass;
@@ -426,15 +442,9 @@ public final class ClassMeta<T> implements Type {
for (String methodName : new
String[]{"fromString","fromValue","valueOf","parse","parseString","forName","forString"})
{
if (fromStringMethod == null) {
for (Method m : c.getMethods()) {
- if (isStatic(m) && isPublic(m)
&& isNotDeprecated(m)) {
- String mName =
m.getName();
- if
(mName.equals(methodName) && m.getReturnType() == c) {
- Class<?>[] args
= m.getParameterTypes();
- if (args.length
== 1 && args[0] == String.class) {
-
fromStringMethod = m;
- break;
- }
- }
+ if (isAll(m, STATIC, PUBLIC,
NOT_DEPRECATED) && hasName(m, methodName) && hasReturnType(m, c) && hasArgs(m,
String.class)) {
+ fromStringMethod = m;
+ break;
}
}
}
@@ -450,68 +460,84 @@ public final class ClassMeta<T> implements Type {
// Find swap() method if present.
for (Method m : c.getMethods()) {
- if (isPublic(m) && isNotDeprecated(m) && !
isStatic(m)) {
- String mName = m.getName();
- if (mName.equals("swap")) {
- Class<?>[] pt =
m.getParameterTypes();
- if (pt.length == 1 && pt[0] ==
BeanSession.class) {
- swapMethod = m;
- swapMethodType =
m.getReturnType();
- break;
- }
- }
+ if (isAll(m, PUBLIC, NOT_DEPRECATED,
NOT_STATIC) && hasName(m, "swap") && hasFuzzyArgs(m, BeanSession.class)) {
+ swapMethod = m;
+ swapMethodType = m.getReturnType();
+ break;
}
}
// Find unswap() method if present.
if (swapMethod != null) {
for (Method m : c.getMethods()) {
- if (isPublic(m) && isNotDeprecated(m)
&& isStatic(m)) {
- String mName = m.getName();
- if (mName.equals("unswap")) {
- Class<?>[] pt =
m.getParameterTypes();
- if (pt.length == 2 &&
pt[0] == BeanSession.class && pt[1] == swapMethodType) {
- unswapMethod =
m;
- break;
- }
- }
+ if (isAll(m, PUBLIC, NOT_DEPRECATED,
STATIC) &&hasName(m, "unswap") && hasFuzzyArgs(m, BeanSession.class,
swapMethodType)) {
+ unswapMethod = m;
+ break;
}
}
}
+
+ // Find example() method if present.
+ for (Method m : c.getMethods()) {
+ if (isAll(m, PUBLIC, NOT_DEPRECATED, STATIC) &&
hasName(m, "example") && hasFuzzyArgs(m, BeanSession.class)) {
+ exampleMethod = m;
+ break;
+ }
+ }
for (Field f : getAllFields(c, true)) {
if
(f.isAnnotationPresent(ParentProperty.class)) {
- f.setAccessible(true);
+ if (isStatic(f))
+ throw new
ClassMetaRuntimeException("@ParentProperty used on invalid field ''{0}''", f);
+ setAccessible(f, false);
parentPropertyMethod = new
Setter.FieldSetter(f);
}
if (f.isAnnotationPresent(NameProperty.class)) {
- f.setAccessible(true);
+ if (isStatic(f))
+ throw new
ClassMetaRuntimeException("@NameProperty used on invalid field ''{0}''", f);
+ setAccessible(f, false);
namePropertyMethod = new
Setter.FieldSetter(f);
}
+ if (f.isAnnotationPresent(Example.class)) {
+ if (! (isStatic(f) &&
isParentClass(innerClass, f.getType())))
+ throw new
ClassMetaRuntimeException("@Example used on invalid field ''{0}''", f);
+ setAccessible(f, false);
+ exampleField = f;
+ }
}
// Find @NameProperty and @ParentProperty methods if
present.
for (Method m : getAllMethods(c, true)) {
- if (m.isAnnotationPresent(ParentProperty.class)
&& m.getParameterTypes().length == 1) {
- m.setAccessible(true);
+ if
(m.isAnnotationPresent(ParentProperty.class)) {
+ if (isStatic(m) || ! hasNumArgs(m, 1))
+ throw new
ClassMetaRuntimeException("@ParentProperty used on invalid method ''{0}''", m);
+ setAccessible(m, false);
parentPropertyMethod = new
Setter.MethodSetter(m);
}
- if (m.isAnnotationPresent(NameProperty.class)
&& m.getParameterTypes().length == 1) {
- m.setAccessible(true);
+ if (m.isAnnotationPresent(NameProperty.class)) {
+ if (isStatic(m) || ! hasNumArgs(m, 1))
+ throw new
ClassMetaRuntimeException("@NameProperty used on invalid method ''{0}''", m);
+ setAccessible(m, false);
namePropertyMethod = new
Setter.MethodSetter(m);
}
+ if (m.isAnnotationPresent(Example.class)) {
+ if (! (isStatic(m) && hasFuzzyArgs(m,
BeanSession.class) && isParentClass(innerClass, m.getReturnType())))
+ throw new
ClassMetaRuntimeException("@Example used on invalid method ''{0}''", m);
+ setAccessible(m, false);
+ exampleMethod = m;
+ }
}
// Note: Primitive types are normally abstract.
- isAbstract = Modifier.isAbstract(c.getModifiers()) && !
c.isPrimitive();
+ isAbstract = ClassUtils.isAbstract(c) && !
c.isPrimitive();
// Find constructor(String) method if present.
for (Constructor cs : c.getConstructors()) {
if (isPublic(cs) && isNotDeprecated(cs)) {
- Class<?>[] args =
cs.getParameterTypes();
- if (args.length == (isMemberClass ? 1 :
0) && c != Object.class && ! isAbstract) {
+ Class<?>[] pt = cs.getParameterTypes();
+ if (pt.length == (isMemberClass ? 1 :
0) && c != Object.class && ! isAbstract) {
noArgConstructor = cs;
- } else if (args.length ==
(isMemberClass ? 2 : 1)) {
- Class<?> arg =
args[(isMemberClass ? 1 : 0)];
+ } else if (pt.length == (isMemberClass
? 2 : 1)) {
+ Class<?> arg =
pt[(isMemberClass ? 1 : 0)];
if (arg == String.class)
stringConstructor = cs;
else if (swapMethodType != null
&& swapMethodType.isAssignableFrom(arg))
@@ -527,7 +553,7 @@ public final class ClassMeta<T> implements Type {
primitiveDefault = ClassUtils.getPrimitiveDefault(c);
for (Method m : c.getMethods())
- if (isPublic(m) && isNotDeprecated(m))
+ if (isAll(m, PUBLIC, NOT_DEPRECATED))
publicMethods.put(getMethodSignature(m), m);
Map<Class<?>,Remoteable> remoteableMap =
findAnnotationsMap(Remoteable.class, c);
@@ -564,7 +590,7 @@ public final class ClassMeta<T> implements Type {
@Override
public Object swap(BeanSession
session, Object o) throws SerializeException {
try {
- return
fSwapMethod.invoke(o, session);
+ return
fSwapMethod.invoke(o, getMatchingArgs(fSwapMethod.getParameterTypes(),
session));
} catch (Exception e) {
throw new
SerializeException(e);
}
@@ -573,7 +599,7 @@ public final class ClassMeta<T> implements Type {
public T unswap(BeanSession
session, Object f, ClassMeta<?> hint) throws ParseException {
try {
if
(fUnswapMethod != null)
- return
(T)fUnswapMethod.invoke(null, session, f);
+ return
(T)fUnswapMethod.invoke(null, getMatchingArgs(fSwapMethod.getParameterTypes(),
session, f));
if
(fSwapConstructor != null)
return
fSwapConstructor.newInstance(f);
return
super.unswap(session, f, hint);
@@ -707,7 +733,7 @@ public final class ClassMeta<T> implements Type {
return
(PojoSwap<T,?>)l.iterator().next();
}
- throw new FormattedRuntimeException("Invalid swap class
''{0}'' specified. Must extend from PojoSwap or Surrogate.", c);
+ throw new ClassMetaRuntimeException("Invalid swap class
''{0}'' specified. Must extend from PojoSwap or Surrogate.", c);
}
private ClassMeta<?> findClassMeta(Class<?> c) {
@@ -872,13 +898,11 @@ public final class ClassMeta<T> implements Type {
*/
@SuppressWarnings({"rawtypes","unchecked"})
protected static <T> Constructor<? extends T>
findNoArgConstructor(Class<?> c, Visibility v) {
- int mod = c.getModifiers();
- if (Modifier.isAbstract(mod))
+ if (ClassUtils.isAbstract(c))
return null;
boolean isMemberClass = c.isMemberClass() && ! isStatic(c);
for (Constructor cc : c.getConstructors()) {
- mod = cc.getModifiers();
- if (cc.getParameterTypes().length == (isMemberClass ? 1
: 0) && v.isVisible(mod) && isNotDeprecated(cc))
+ if (hasNumArgs(cc, isMemberClass ? 1 : 0) &&
v.isVisible(cc.getModifiers()) && isNotDeprecated(cc))
return v.transform(cc);
}
return null;
@@ -906,6 +930,28 @@ public final class ClassMeta<T> implements Type {
PojoSwap<T,?> ps = getPojoSwap(session);
return (ps == null ? this : ps.getSwapClassMeta(session));
}
+
+ /**
+ * Returns the example of this class.
+ *
+ * @param session
+ * The bean session.
+ * <br>Required because the example method may take it in as a
parameter.
+ * @return The serialized class type, or this object if no swap is
associated with the class.
+ */
+ @SuppressWarnings("unchecked")
+ @BeanIgnore
+ public T getExample(BeanSession session) {
+ try {
+ if (exampleMethod != null)
+ return (T)invokeMethodFuzzy(exampleMethod,
null, session);
+ if (exampleField != null)
+ return (T)exampleField.get(null);
+ return example;
+ } catch (Exception e) {
+ throw new ClassMetaRuntimeException(e);
+ }
+ }
/**
* For array and {@code Collection} types, returns the class type of
the components of the array or
@@ -1366,7 +1412,7 @@ public final class ClassMeta<T> implements Type {
*/
public boolean canCreateNewInstance(Object outer) {
if (isMemberClass)
- return outer != null && noArgConstructor != null &&
noArgConstructor.getParameterTypes()[0] == outer.getClass();
+ return outer != null && noArgConstructor != null &&
hasArgs(noArgConstructor, outer.getClass());
return canCreateNewInstance();
}
@@ -1386,7 +1432,7 @@ public final class ClassMeta<T> implements Type {
if (beanMeta.constructor == null)
return false;
if (isMemberClass)
- return outer != null &&
beanMeta.constructor.getParameterTypes()[0] == outer.getClass();
+ return outer != null && hasArgs(beanMeta.constructor,
outer.getClass());
return true;
}
@@ -1403,7 +1449,7 @@ public final class ClassMeta<T> implements Type {
return true;
if (stringConstructor != null) {
if (isMemberClass)
- return outer != null &&
stringConstructor.getParameterTypes()[0] == outer.getClass();
+ return outer != null &&
hasArgs(stringConstructor, outer.getClass(), String.class);
return true;
}
return false;
@@ -1420,7 +1466,7 @@ public final class ClassMeta<T> implements Type {
public boolean canCreateNewInstanceFromNumber(Object outer) {
if (numberConstructor != null) {
if (isMemberClass)
- return outer != null &&
numberConstructor.getParameterTypes()[0] == outer.getClass();
+ return outer != null &&
hasArgs(numberConstructor, outer.getClass());
return true;
}
return false;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMetaRuntimeException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMetaRuntimeException.java
new file mode 100644
index 0000000..1ea6da1
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMetaRuntimeException.java
@@ -0,0 +1,73 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you 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 org.apache.juneau;
+
+/**
+ * General class metadata runtime operation exception.
+ */
+public final class ClassMetaRuntimeException extends FormattedRuntimeException
{
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * @param message The error message.
+ */
+ public ClassMetaRuntimeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message The error message.
+ * @param args Arguments passed in to the {@code String.format()}
method.
+ */
+ public ClassMetaRuntimeException(String message, Object...args) {
+ super(message, args);
+ }
+
+ /**
+ * Shortcut for calling <code><jk>new</jk>
ClassMetaRuntimeException(String.format(c.getName() + <js>": "</js> + message,
args));</code>
+ *
+ * @param c The class name of the bean that caused the exception.
+ * @param message The error message.
+ * @param args Arguments passed in to the {@code String.format()}
method.
+ */
+ public ClassMetaRuntimeException(Class<?> c, String message, Object...
args) {
+ super(c.getName() + ": " + message, args);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cause The initial cause of the exception.
+ */
+ public ClassMetaRuntimeException(Throwable cause) {
+ super(cause == null ? null : cause.getLocalizedMessage());
+ initCause(cause);
+ }
+
+ /**
+ * Sets the inner cause for this exception.
+ *
+ * @param cause The inner cause.
+ * @return This object (for method chaining).
+ */
+ @Override /* Throwable */
+ public synchronized ClassMetaRuntimeException initCause(Throwable
cause) {
+ super.initCause(cause);
+ return this;
+ }
+}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Visibility.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Visibility.java
index 8d989cb..37b5b16 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Visibility.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Visibility.java
@@ -12,6 +12,8 @@
//
***************************************************************************************************************************
package org.apache.juneau;
+import static org.apache.juneau.internal.ClassUtils.*;
+
import java.lang.reflect.*;
/**
@@ -115,7 +117,7 @@ public enum Visibility {
if (x == null)
return null;
if (isVisible(x))
- if (! setAccessible(x))
+ if (! setAccessible(x, true))
return null;
return x;
}
@@ -135,7 +137,7 @@ public enum Visibility {
if (x == null)
return null;
if (isVisible(x))
- if (! setAccessible(x))
+ if (! setAccessible(x, true))
return null;
return x;
}
@@ -155,56 +157,9 @@ public enum Visibility {
if (x == null)
return null;
if (isVisible(x))
- if (! setAccessible(x))
+ if (! setAccessible(x, true))
return null;
return x;
}
- /**
- * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
- *
- * @param x The constructor.
- * @return <jk>true</jk> if call was successful.
- */
- public static boolean setAccessible(Constructor<?> x) {
- try {
- if (! (x == null || x.isAccessible()))
- x.setAccessible(true);
- return true;
- } catch (SecurityException e) {
- return false;
- }
- }
-
- /**
- * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
- *
- * @param x The method.
- * @return <jk>true</jk> if call was successful.
- */
- public static boolean setAccessible(Method x) {
- try {
- if (! (x == null || x.isAccessible()))
- x.setAccessible(true);
- return true;
- } catch (SecurityException e) {
- return false;
- }
- }
-
- /**
- * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
- *
- * @param x The field.
- * @return <jk>true</jk> if call was successful.
- */
- public static boolean setAccessible(Field x) {
- try {
- if (! (x == null || x.isAccessible()))
- x.setAccessible(true);
- return true;
- } catch (SecurityException e) {
- return false;
- }
- }
}
\ No newline at end of file
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Example.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Example.java
new file mode 100644
index 0000000..0c81d37
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Example.java
@@ -0,0 +1,44 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you 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 org.apache.juneau.annotation;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a static method or field that returns an example of a POJO.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode'>
+ * <jc>// POJO class.</jc>
+ * <jk>public class</jk> MyBean {
+ *
+ * <ja>@Example</ja>
+ * <jk>public static</jk> MyBean example() {
+ * <jk>return new</jk>
MyBean().foo(<js>"foo"</js>).bar(123);
+ * }
+ * }
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li>TODO
+ * </ul>
+ */
+@Documented
+@Target({FIELD,METHOD})
+@Retention(RUNTIME)
+@Inherited
+public @interface Example {}
\ No newline at end of file
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassFlags.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassFlags.java
new file mode 100644
index 0000000..371c9f4
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassFlags.java
@@ -0,0 +1,55 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you 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 org.apache.juneau.internal;
+
+/**
+ * Identifies possible modifiers on classes, methods, fields, and constructors.
+ */
+public enum ClassFlags {
+
+ /** PUBLIC */
+ PUBLIC,
+
+ /** NOT_PUBLIC */
+ NOT_PUBLIC,
+
+ /** STATIC */
+ STATIC,
+
+ /** NOT_STATIC */
+ NOT_STATIC,
+
+ /** HAS_ARGS */
+ HAS_ARGS,
+
+ /** HAS_NO_ARGS */
+ HAS_NO_ARGS,
+
+ /** DEPRECATED */
+ DEPRECATED,
+
+ /** NOT_DEPRECATED */
+ NOT_DEPRECATED,
+
+ /** ABSTRACT */
+ ABSTRACT,
+
+ /** NOT_ABSTRACT */
+ NOT_ABSTRACT,
+
+ /** TRANSIENT */
+ TRANSIENT,
+
+ /** NOT_TRANSIENT */
+ NOT_TRANSIENT
+}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 3b7c237..d276fc9 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -12,6 +12,8 @@
//
***************************************************************************************************************************
package org.apache.juneau.internal;
+import static org.apache.juneau.internal.ClassFlags.*;
+
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
@@ -271,6 +273,561 @@ public final class ClassUtils {
}
/**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified class.
+ *
+ * @param x The class to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified class.
+ */
+ public static boolean isAll(Class<?> x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (! isDeprecated(x))
+ return false;
+ break;
+ case NOT_DEPRECATED:
+ if (isDeprecated(x))
+ return false;
+ break;
+ case PUBLIC:
+ if (! isPublic(x))
+ return false;
+ break;
+ case NOT_PUBLIC:
+ if (isPublic(x))
+ return false;
+ break;
+ case STATIC:
+ if (! isStatic(x))
+ return false;
+ break;
+ case NOT_STATIC:
+ if (isStatic(x))
+ return false;
+ break;
+ case ABSTRACT:
+ if (! isAbstract(x))
+ return false;
+ break;
+ case NOT_ABSTRACT:
+ if (isAbstract(x))
+ return false;
+ break;
+ case HAS_ARGS:
+ case HAS_NO_ARGS:
+ case TRANSIENT:
+ case NOT_TRANSIENT:
+ default:
+ break;
+
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified method.
+ *
+ * @param x The method to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified method.
+ */
+ public static boolean isAll(Method x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (! isDeprecated(x))
+ return false;
+ break;
+ case NOT_DEPRECATED:
+ if (isDeprecated(x))
+ return false;
+ break;
+ case HAS_ARGS:
+ if (x.getParameterTypes().length == 0)
+ return false;
+ break;
+ case HAS_NO_ARGS:
+ if (x.getParameterTypes().length != 0)
+ return false;
+ break;
+ case PUBLIC:
+ if (! isPublic(x))
+ return false;
+ break;
+ case NOT_PUBLIC:
+ if (isPublic(x))
+ return false;
+ break;
+ case STATIC:
+ if (! isStatic(x))
+ return false;
+ break;
+ case NOT_STATIC:
+ if (isStatic(x))
+ return false;
+ break;
+ case ABSTRACT:
+ if (! isAbstract(x))
+ return false;
+ break;
+ case NOT_ABSTRACT:
+ if (isAbstract(x))
+ return false;
+ break;
+ case TRANSIENT:
+ case NOT_TRANSIENT:
+ default:
+ break;
+
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified constructor.
+ *
+ * @param x The constructor to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified constructor.
+ */
+ public static boolean isAll(Constructor<?> x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (! isDeprecated(x))
+ return false;
+ break;
+ case NOT_DEPRECATED:
+ if (isDeprecated(x))
+ return false;
+ break;
+ case HAS_ARGS:
+ if (x.getParameterTypes().length == 0)
+ return false;
+ break;
+ case HAS_NO_ARGS:
+ if (x.getParameterTypes().length != 0)
+ return false;
+ break;
+ case PUBLIC:
+ if (! isPublic(x))
+ return false;
+ break;
+ case NOT_PUBLIC:
+ if (isPublic(x))
+ return false;
+ break;
+ case STATIC:
+ case NOT_STATIC:
+ case ABSTRACT:
+ case NOT_ABSTRACT:
+ case TRANSIENT:
+ case NOT_TRANSIENT:
+ default:
+ break;
+
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified field.
+ *
+ * @param x The field to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified field.
+ */
+ public static boolean isAll(Field x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (! isDeprecated(x))
+ return false;
+ break;
+ case NOT_DEPRECATED:
+ if (isDeprecated(x))
+ return false;
+ break;
+ case HAS_ARGS:
+ break;
+ case HAS_NO_ARGS:
+ break;
+ case PUBLIC:
+ if (! isPublic(x))
+ return false;
+ break;
+ case NOT_PUBLIC:
+ if (isPublic(x))
+ return false;
+ break;
+ case STATIC:
+ if (! isStatic(x))
+ return false;
+ break;
+ case NOT_STATIC:
+ if (isStatic(x))
+ return false;
+ break;
+ case TRANSIENT:
+ if (! isTransient(x))
+ return false;
+ break;
+ case NOT_TRANSIENT:
+ if (isTransient(x))
+ return false;
+ break;
+ case ABSTRACT:
+ case NOT_ABSTRACT:
+ default:
+ break;
+
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified class.
+ *
+ * @param x The class to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified class.
+ */
+ public static boolean isAny(Class<?> x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (isDeprecated(x))
+ return true;
+ break;
+ case NOT_DEPRECATED:
+ if (! isDeprecated(x))
+ return true;
+ break;
+ case PUBLIC:
+ if (isPublic(x))
+ return true;
+ break;
+ case NOT_PUBLIC:
+ if (! isPublic(x))
+ return true;
+ break;
+ case STATIC:
+ if (isStatic(x))
+ return true;
+ break;
+ case NOT_STATIC:
+ if (! isStatic(x))
+ return true;
+ break;
+ case ABSTRACT:
+ if (isAbstract(x))
+ return true;
+ break;
+ case NOT_ABSTRACT:
+ if (! isAbstract(x))
+ return true;
+ break;
+ case TRANSIENT:
+ case NOT_TRANSIENT:
+ case HAS_ARGS:
+ case HAS_NO_ARGS:
+ default:
+ break;
+
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified method.
+ *
+ * @param x The method to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified method.
+ */
+ public static boolean isAny(Method x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (isDeprecated(x))
+ return true;
+ break;
+ case NOT_DEPRECATED:
+ if (! isDeprecated(x))
+ return true;
+ break;
+ case HAS_ARGS:
+ if (x.getParameterTypes().length != 0)
+ return true;
+ break;
+ case HAS_NO_ARGS:
+ if (x.getParameterTypes().length == 0)
+ return true;
+ break;
+ case PUBLIC:
+ if (isPublic(x))
+ return true;
+ break;
+ case NOT_PUBLIC:
+ if (! isPublic(x))
+ return true;
+ break;
+ case STATIC:
+ if (isStatic(x))
+ return true;
+ break;
+ case NOT_STATIC:
+ if (! isStatic(x))
+ return true;
+ break;
+ case ABSTRACT:
+ if (isAbstract(x))
+ return true;
+ break;
+ case NOT_ABSTRACT:
+ if (! isAbstract(x))
+ return true;
+ break;
+ case TRANSIENT:
+ case NOT_TRANSIENT:
+ default:
+ break;
+
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified constructor.
+ *
+ * @param x The constructor to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified constructor.
+ */
+ public static boolean isAny(Constructor<?> x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (isDeprecated(x))
+ return true;
+ break;
+ case NOT_DEPRECATED:
+ if (! isDeprecated(x))
+ return true;
+ break;
+ case HAS_ARGS:
+ if (x.getParameterTypes().length != 0)
+ return true;
+ break;
+ case HAS_NO_ARGS:
+ if (x.getParameterTypes().length == 0)
+ return true;
+ break;
+ case PUBLIC:
+ if (isPublic(x))
+ return true;
+ break;
+ case NOT_PUBLIC:
+ if (! isPublic(x))
+ return true;
+ break;
+ case STATIC:
+ case NOT_STATIC:
+ case ABSTRACT:
+ case NOT_ABSTRACT:
+ case TRANSIENT:
+ case NOT_TRANSIENT:
+ default:
+ break;
+
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if all specified flags are applicable to the
specified field.
+ *
+ * @param x The field to test.
+ * @param flags The flags to test for.
+ * @return <jk>true</jk> if all specified flags are applicable to the
specified field.
+ */
+ public static boolean isAny(Field x, ClassFlags...flags) {
+ for (ClassFlags f : flags) {
+ switch (f) {
+ case DEPRECATED:
+ if (isDeprecated(x))
+ return true;
+ break;
+ case NOT_DEPRECATED:
+ if (! isDeprecated(x))
+ return true;
+ break;
+ case PUBLIC:
+ if (isPublic(x))
+ return true;
+ break;
+ case NOT_PUBLIC:
+ if (! isPublic(x))
+ return true;
+ break;
+ case STATIC:
+ if (isStatic(x))
+ return true;
+ break;
+ case NOT_STATIC:
+ if (! isStatic(x))
+ return true;
+ break;
+ case TRANSIENT:
+ if (isTransient(x))
+ return true;
+ break;
+ case NOT_TRANSIENT:
+ if (! isTransient(x))
+ return true;
+ break;
+ case HAS_ARGS:
+ case HAS_NO_ARGS:
+ case ABSTRACT:
+ case NOT_ABSTRACT:
+ default:
+ break;
+
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method has the specified
arguments.
+ *
+ * @param x The method to test.
+ * @param args The arguments to test for.
+ * @return <jk>true</jk> if the specified method has the specified
arguments in the exact order.
+ */
+ public static boolean hasArgs(Method x, Class<?>...args) {
+ Class<?>[] pt = x.getParameterTypes();
+ if (pt.length == args.length) {
+ for (int i = 0; i < pt.length; i++)
+ if (! pt[i].equals(args[i]))
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified constructor has the specified
arguments.
+ *
+ * @param x The constructor to test.
+ * @param args The arguments to test for.
+ * @return <jk>true</jk> if the specified constructor has the specified
arguments in the exact order.
+ */
+ public static boolean hasArgs(Constructor<?> x, Class<?>...args) {
+ Class<?>[] pt = x.getParameterTypes();
+ if (pt.length == args.length) {
+ for (int i = 0; i < pt.length; i++)
+ if (! pt[i].equals(args[i]))
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method has the specified
number of arguments.
+ *
+ * @param x The method to test.
+ * @param number The number of expected arguments.
+ * @return <jk>true</jk> if the specified method has the specified
number of arguments.
+ */
+ public static boolean hasNumArgs(Method x, int number) {
+ return x.getParameterTypes().length == number;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified constructor has the specified
number of arguments.
+ *
+ * @param x The constructor to test.
+ * @param number The number of expected arguments.
+ * @return <jk>true</jk> if the specified constructor has the specified
number of arguments.
+ */
+ public static boolean hasNumArgs(Constructor<?> x, int number) {
+ return x.getParameterTypes().length == number;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method has at most only the
specified arguments in any order.
+ *
+ * @param x The method to test.
+ * @param args The arguments to test for.
+ * @return <jk>true</jk> if the specified method has at most only the
specified arguments in any order.
+ */
+ public static boolean hasFuzzyArgs(Method x, Class<?>...args) {
+ return fuzzyArgsMatch(x.getParameterTypes(), args) != -1;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified constructor has at most only
the specified arguments in any order.
+ *
+ * @param x The constructor to test.
+ * @param args The arguments to test for.
+ * @return <jk>true</jk> if the specified constructor has at most only
the specified arguments in any order.
+ */
+ public static boolean hasFuzzyArgs(Constructor<?> x, Class<?>...args) {
+ return fuzzyArgsMatch(x.getParameterTypes(), args) != -1;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified class doesn't have the {@link
Deprecated @Deprecated} annotation on it.
+ *
+ * @param c The class.
+ * @return <jk>true</jk> if the specified class doesn't have the {@link
Deprecated @Deprecated} annotation on it.
+ */
+ public static boolean isDeprecated(Class<?> c) {
+ return c.isAnnotationPresent(Deprecated.class);
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method doesn't have the
{@link Deprecated @Deprecated} annotation on it.
+ *
+ * @param m The method.
+ * @return <jk>true</jk> if the specified method doesn't have the
{@link Deprecated @Deprecated} annotation on it.
+ */
+ public static boolean isDeprecated(Method m) {
+ return m.isAnnotationPresent(Deprecated.class);
+
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified constructor doesn't have the
{@link Deprecated @Deprecated} annotation on it.
+ *
+ * @param c The constructor.
+ * @return <jk>true</jk> if the specified constructor doesn't have the
{@link Deprecated @Deprecated} annotation on it.
+ */
+ public static boolean isDeprecated(Constructor<?> c) {
+ return c.isAnnotationPresent(Deprecated.class);
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified field doesn't have the {@link
Deprecated @Deprecated} annotation on it.
+ *
+ * @param f The field.
+ * @return <jk>true</jk> if the specified field doesn't have the {@link
Deprecated @Deprecated} annotation on it.
+ */
+ public static boolean isDeprecated(Field f) {
+ return f.isAnnotationPresent(Deprecated.class);
+ }
+
+ /**
* Returns <jk>true</jk> if the specified class has the {@link
Deprecated @Deprecated} annotation on it.
*
* @param c The class.
@@ -332,6 +889,16 @@ public final class ClassUtils {
}
/**
+ * Returns <jk>true</jk> if the specified method is abstract.
+ *
+ * @param m The method.
+ * @return <jk>true</jk> if the specified method is abstract.
+ */
+ public static boolean isAbstract(Method m) {
+ return Modifier.isAbstract(m.getModifiers());
+ }
+
+ /**
* Returns <jk>true</jk> if the specified method is public.
*
* @param m The method.
@@ -342,6 +909,16 @@ public final class ClassUtils {
}
/**
+ * Returns <jk>true</jk> if the specified field is public.
+ *
+ * @param f The field.
+ * @return <jk>true</jk> if the specified field is public.
+ */
+ public static boolean isPublic(Field f) {
+ return Modifier.isPublic(f.getModifiers());
+ }
+
+ /**
* Returns <jk>true</jk> if the specified method is static.
*
* @param m The method.
@@ -352,6 +929,16 @@ public final class ClassUtils {
}
/**
+ * Returns <jk>true</jk> if the specified field is static.
+ *
+ * @param f The field.
+ * @return <jk>true</jk> if the specified field is static.
+ */
+ public static boolean isStatic(Field f) {
+ return Modifier.isStatic(f.getModifiers());
+ }
+
+ /**
* Returns <jk>true</jk> if the specified constructor is public.
*
* @param c The constructor.
@@ -360,7 +947,50 @@ public final class ClassUtils {
public static boolean isPublic(Constructor<?> c) {
return Modifier.isPublic(c.getModifiers());
}
-
+
+ /**
+ * Returns <jk>true</jk> if the specified field is transient.
+ *
+ * @param f The field.
+ * @return <jk>true</jk> if the specified field is transient.
+ */
+ public static boolean isTransient(Field f) {
+ return Modifier.isTransient(f.getModifiers());
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method has the specified name.
+ *
+ * @param m The method to test.
+ * @param name The name to test for.
+ * @return <jk>true</jk> if the specified method has the specified name.
+ */
+ public static boolean hasName(Method m, String name) {
+ return m.getName().equals(name);
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method has the specified
return type.
+ *
+ * @param m The method to test.
+ * @param c The return type to test for.
+ * @return <jk>true</jk> if the specified method has the specified
return type.
+ */
+ public static boolean hasReturnType(Method m, Class<?> c) {
+ return m.getReturnType() == c;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified method has the specified
parent return type.
+ *
+ * @param m The method to test.
+ * @param c The return type to test for.
+ * @return <jk>true</jk> if the specified method has the specified
parent return type.
+ */
+ public static boolean hasReturnTypeParent(Method m, Class<?> c) {
+ return isParentClass(c, m.getReturnType());
+ }
+
/**
* Returns the specified annotation on the specified method.
*
@@ -447,7 +1077,7 @@ public final class ClassUtils {
boolean isMemberClass = c.isMemberClass() && ! isStatic(c);
for (Constructor cc : c.getConstructors()) {
mod = cc.getModifiers();
- if (cc.getParameterTypes().length == (isMemberClass ? 1
: 0) && v.isVisible(mod) && isNotDeprecated(cc))
+ if (hasNumArgs(cc, isMemberClass ? 1 : 0) &&
v.isVisible(mod) && isNotDeprecated(cc))
return v.transform(cc);
}
return null;
@@ -518,6 +1148,57 @@ public final class ClassUtils {
throw new FormattedRuntimeException("Invalid type found
in resolveParameterType: {0}", actualType);
}
}
+
+ /**
+ * Invokes the specified method using fuzzy-arg matching.
+ *
+ * <p>
+ * Arguments will be matched to the parameters based on the parameter
types.
+ * <br>Arguments can be in any order.
+ * <br>Extra arguments will be ignored.
+ * <br>Missing arguments will be left <jk>null</jk>.
+ *
+ * <p>
+ * Note that this only works for methods that have distinguishable
argument types.
+ * <br>It's not going to work on methods with generic argument types
like <code>Object</code>
+ *
+ * @param m The method being called.
+ * @param pojo
+ * The POJO the method is being called on.
+ * <br>Can be <jk>null</jk> for static methods.
+ * @param args
+ * The arguments to pass to the method.
+ * @return
+ * The results of the method invocation.
+ * @throws Exception
+ */
+ public static Object invokeMethodFuzzy(Method m, Object pojo,
Object...args) throws Exception {
+ return m.invoke(pojo, getMatchingArgs(m.getParameterTypes(),
args));
+ }
+
+ /**
+ * Invokes the specified constructor using fuzzy-arg matching.
+ *
+ * <p>
+ * Arguments will be matched to the parameters based on the parameter
types.
+ * <br>Arguments can be in any order.
+ * <br>Extra arguments will be ignored.
+ * <br>Missing arguments will be left <jk>null</jk>.
+ *
+ * <p>
+ * Note that this only works for constructors that have distinguishable
argument types.
+ * <br>It's not going to work on constructors with generic argument
types like <code>Object</code>
+ *
+ * @param c The constructor being called.
+ * @param args
+ * The arguments to pass to the constructor.
+ * @return
+ * The results of the method invocation.
+ * @throws Exception
+ */
+ public static <T> T invokeConstructorFuzzy(Constructor<T> c,
Object...args) throws Exception {
+ return c.newInstance(getMatchingArgs(c.getParameterTypes(),
args));
+ }
private static boolean isInnerClass(GenericDeclaration od,
GenericDeclaration id) {
if (od instanceof Class && id instanceof Class) {
@@ -563,11 +1244,8 @@ public final class ClassUtils {
*/
public static Method findPublicMethod(Class<?> c, String name, Class<?>
returnType, Class<?>...argTypes) {
for (Method m : c.getMethods()) {
- if (isPublic(m) && m.getName().equals(name)) {
- Class<?> rt = m.getReturnType();
- if (isParentClass(returnType, rt) &&
argsMatch(m.getParameterTypes(), argTypes))
- return m;
- }
+ if (isPublic(m) && hasName(m, name) &&
hasReturnTypeParent(m, returnType) && argsMatch(m.getParameterTypes(),
argTypes))
+ return m;
}
return null;
}
@@ -674,7 +1352,7 @@ public final class ClassUtils {
* @param argTypes The class types of the arguments being passed to the
method.
* @return The number of matching arguments, or <code>-1</code> a
parameter was found that isn't in the list of args.
*/
- public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>[]
argTypes) {
+ public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>...
argTypes) {
int matches = 0;
outer: for (Class<?> p : paramTypes) {
p = getWrapperIfPrimitive(p);
@@ -876,7 +1554,7 @@ public final class ClassUtils {
if (fuzzyArgs) {
con = findPublicConstructor(c3, true,
args);
if (con != null)
- return
(T)con.newInstance(getMatchingArgs(con, args));
+ return
(T)con.newInstance(getMatchingArgs(con.getParameterTypes(), args));
}
throw new FormattedRuntimeException("Could not
instantiate class {0}/{1}. Constructor not found.", c.getName(), c2);
@@ -890,8 +1568,19 @@ public final class ClassUtils {
}
}
- private static Object[] getMatchingArgs(Constructor<?> con, Object[]
args) {
- Class<?>[] paramTypes = con.getParameterTypes();
+ /**
+ * Matches arguments to a list of parameter types.
+ *
+ * <p>
+ * Extra parameters are ignored.
+ * <br>Missing parameters are left null.
+ *
+ * @param paramTypes The parameter types.
+ * @param args The arguments to match to the parameter types.
+ * @return
+ * An array of parameters.
+ */
+ public static Object[] getMatchingArgs(Class<?>[] paramTypes, Object...
args) {
Object[] params = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
Class<?> pt = getWrapperIfPrimitive(paramTypes[i]);
@@ -1200,19 +1889,10 @@ public final class ClassUtils {
* @return The static method, or <jk>null</jk> if it couldn't be found.
*/
public static Method findPublicFromStringMethod(Class<?> c) {
- for (String methodName : new
String[]{"create","fromString","fromValue","valueOf","parse","parseString","forName","forString"})
{
- for (Method m : c.getMethods()) {
- if (isStatic(m) && isPublic(m) &&
isNotDeprecated(m)) {
- String mName = m.getName();
- if (mName.equals(methodName) &&
m.getReturnType() == c) {
- Class<?>[] args =
m.getParameterTypes();
- if (args.length == 1 && args[0]
== String.class) {
- return m;
- }
- }
- }
- }
- }
+ for (String methodName : new
String[]{"create","fromString","fromValue","valueOf","parse","parseString","forName","forString"})
+ for (Method m : c.getMethods())
+ if (isAll(m, STATIC, PUBLIC, NOT_DEPRECATED) &&
hasName(m, methodName) && hasReturnType(m, c) && hasArgs(m, String.class))
+ return m;
return null;
}
@@ -1255,6 +1935,63 @@ public final class ClassUtils {
return getStringify(o.getClass()).toString(o);
}
+ /**
+ * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
+ *
+ * @param x The constructor.
+ * @param ignoreExceptions Ignore {@link SecurityException
SecurityExceptions} and just return <jk>false</jk> if thrown.
+ * @return <jk>true</jk> if call was successful.
+ */
+ public static boolean setAccessible(Constructor<?> x, boolean
ignoreExceptions) {
+ try {
+ if (! (x == null || x.isAccessible()))
+ x.setAccessible(true);
+ return true;
+ } catch (SecurityException e) {
+ if (ignoreExceptions)
+ return false;
+ throw new ClassMetaRuntimeException("Could not set
accessibility to true on constructor ''{0}''", x);
+ }
+ }
+
+ /**
+ * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
+ *
+ * @param x The method.
+ * @param ignoreExceptions Ignore {@link SecurityException
SecurityExceptions} and just return <jk>false</jk> if thrown.
+ * @return <jk>true</jk> if call was successful.
+ */
+ public static boolean setAccessible(Method x, boolean ignoreExceptions)
{
+ try {
+ if (! (x == null || x.isAccessible()))
+ x.setAccessible(true);
+ return true;
+ } catch (SecurityException e) {
+ if (ignoreExceptions)
+ return false;
+ throw new ClassMetaRuntimeException("Could not set
accessibility to true on method ''{0}''", x);
+ }
+ }
+
+ /**
+ * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
+ *
+ * @param x The field.
+ * @param ignoreExceptions Ignore {@link SecurityException
SecurityExceptions} and just return <jk>false</jk> if thrown.
+ * @return <jk>true</jk> if call was successful.
+ */
+ public static boolean setAccessible(Field x, boolean ignoreExceptions) {
+ try {
+ if (! (x == null || x.isAccessible()))
+ x.setAccessible(true);
+ return true;
+ } catch (SecurityException e) {
+ if (ignoreExceptions)
+ return false;
+ throw new ClassMetaRuntimeException("Could not set
accessibility to true on field ''{0}''", x);
+ }
+ }
+
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Stringify getStringify(Class c) {
Stringify fs = STRINGIFY_CACHE.get(c);
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
index aa032bb..d129d43 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
@@ -13,6 +13,7 @@
package org.apache.juneau.transform;
import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.internal.ClassFlags.*;
import java.lang.reflect.*;
@@ -188,9 +189,9 @@ public class BuilderSwap<T,B> {
if (builderClass == null) {
for (Constructor cc : pojoClass.getConstructors()) {
- if (cVis.isVisible(cc)) {
+ if (cVis.isVisible(cc) && hasNumArgs(cc, 1)) {
Class<?>[] pt = cc.getParameterTypes();
- if (pt.length == 1 &&
isParentClass(Builder.class, pt[0])) {
+ if (isParentClass(Builder.class,
pt[0])) {
pojoConstructor = cc;
builderClass = pt[0];
}
@@ -217,14 +218,14 @@ public class BuilderSwap<T,B> {
private static Method findBuilderCreateMethod(Class<?> pojoClass) {
for (Method m : pojoClass.getDeclaredMethods())
- if (isPublic(m) && isStatic(m) &&
m.getName().equals("create") && m.getReturnType() != Void.class)
+ if (isAll(m, PUBLIC, STATIC) && hasName(m, "create") &&
! hasReturnType(m, Void.class))
return m;
return null;
}
private static Method findCreatePojoMethod(Class<?> builderClass) {
for (Method m : builderClass.getDeclaredMethods())
- if ("build".equals(m.getName()) && ! (isStatic(m) ||
m.getReturnType() == Void.class))
+ if (isAll(m, NOT_STATIC) && hasName(m, "build") && !
hasReturnType(m, Void.class))
return m;
return null;
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
index f2e4776..7ee1854 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
@@ -12,6 +12,8 @@
//
***************************************************************************************************************************
package org.apache.juneau.transform;
+import static org.apache.juneau.internal.ClassUtils.*;
+
import java.lang.reflect.*;
import java.util.*;
@@ -58,25 +60,16 @@ public class SurrogateSwap<T,F> extends PojoSwap<T,F> {
public static List<SurrogateSwap<?,?>> findPojoSwaps(Class<?> c) {
List<SurrogateSwap<?,?>> l = new LinkedList<>();
for (Constructor<?> cc : c.getConstructors()) {
- if (cc.getAnnotation(BeanIgnore.class) == null) {
- Class<?>[] pt = cc.getParameterTypes();
-
- // Only constructors with one parameter.
- // Ignore instance class constructors.
- if (pt.length == 1 && pt[0] !=
c.getDeclaringClass()) {
- int mod = cc.getModifiers();
- if (Modifier.isPublic(mod)) { // Only
public constructors.
-
- // Find the unswap method if
there is one.
- Method unswapMethod = null;
- for (Method m : c.getMethods())
{
- if
(pt[0].equals(m.getReturnType()) && Modifier.isPublic(m.getModifiers()))
- unswapMethod = m;
- }
-
- l.add(new SurrogateSwap(pt[0],
cc, unswapMethod));
- }
+ Class<?>[] pt = cc.getParameterTypes();
+ if (cc.getAnnotation(BeanIgnore.class) == null &&
hasNumArgs(cc, 1) && isPublic(cc) && pt[0] != c.getDeclaringClass()) {
+ // Find the unswap method if there is one.
+ Method unswapMethod = null;
+ for (Method m : c.getMethods()) {
+ if (pt[0].equals(m.getReturnType()) &&
isPublic(m))
+ unswapMethod = m;
}
+
+ l.add(new SurrogateSwap(pt[0], cc,
unswapMethod));
}
}
return l;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java
index 6535f82..4844ca8 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MetadataMap.java
@@ -58,8 +58,8 @@ public class MetadataMap {
Object o = null;
try {
for (Constructor<?> con : c.getConstructors()) {
- Class<?>[] params =
con.getParameterTypes();
- if (params.length == 1 &&
isParentClass(params[0], constructorArg.getClass())) {
+ Class<?>[] pt = con.getParameterTypes();
+ if (pt.length == 1 &&
isParentClass(pt[0], constructorArg.getClass())) {
o =
con.newInstance(constructorArg);
break;
}
diff --git a/juneau-doc/src/main/javadoc/overview.html
b/juneau-doc/src/main/javadoc/overview.html
index dc56b02..4a1625b 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -151,6 +151,9 @@
<li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.HtmlSerializers'>HTML Serializers</a></p>
<li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.HtmlParsers'>HTML Parsers</a></p>
<li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.HtmlAnnotation'>@Html Annotation</a></p>
+ <li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.HtmlRenderAnnotation'>@Html(render)
Annotation</a></p>
+ <li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.HtmlDocSerializer'>HtmlDocSerializer</a></p>
+ <li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.CustomTemplates'>Custom Templates</a></p>
<li><p><a class='doclink'
href='#juneau-marshall.HtmlDetails.HtmlSchema'>HTML-Schema Support</a></p>
</ol>
<li><p><a class='doclink'
href='#juneau-marshall.UonDetails'>UON Details</a></p>
@@ -299,7 +302,7 @@
<li><p><a class='doclink'
href='#juneau-rest-server.ConfigurationFiles'>Configuration Files</a></p>
<li><p><a class='doclink'
href='#juneau-rest-server.StaticFiles'>Static files</a></p>
<li><p><a class='doclink'
href='#juneau-rest-server.ClientVersioning'>Client Versioning</a></p>
- <li><p><a class='doclink'
href='#juneau-rest-server.OptionsPages'>OPTIONS pages</a></p>
+ <li><p><a class='doclink'
href='#juneau-rest-server.OptionsPages'>OPTIONS pages and Swagger</a></p>
<ol>
<li><p><a class='doclink'
href='#juneau-rest-server.RestInfoProvider'>RestInfoProvider</a></p>
<li><p><a class='doclink'
href='#juneau-rest-server.BasicRestInfoProvider'>BasicRestInfoProvider</a></p>
@@ -7160,8 +7163,29 @@
</div>
<!--
=======================================================================================================
-->
+ <a id='juneau-marshall.HtmlDetails.HtmlRenderAnnotation'></a>
+ <h4 class='topic' onclick='toggle(this)'>2.17.5 - @Html(render)
Annotation</h4>
+ <div class='topic'>
+ TODO
+ </div>
+
+ <!--
=======================================================================================================
-->
+ <a id='juneau-marshall.HtmlDetails.HtmlDocSerializer'></a>
+ <h4 class='topic' onclick='toggle(this)'>2.17.6 -
HtmlDocSerializer</h4>
+ <div class='topic'>
+ TODO
+ </div>
+
+ <!--
=======================================================================================================
-->
+ <a id='juneau-marshall.HtmlDetails.CustomTemplates'></a>
+ <h4 class='topic' onclick='toggle(this)'>2.17.7 - Custom
Templates</h4>
+ <div class='topic'>
+ TODO
+ </div>
+
+ <!--
=======================================================================================================
-->
<a id='juneau-marshall.HtmlDetails.HtmlSchema'></a>
- <h4 class='topic' onclick='toggle(this)'>2.17.5 - HTML-Schema
Support</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.17.8 - HTML-Schema
Support</h4>
<div class='topic'>
TODO
</div>
@@ -13614,7 +13638,7 @@
<!--
========================================================================================================
-->
<a id='juneau-rest-server.OptionsPages'></a>
- <h3 class='topic' onclick='toggle(this)'>7.23 - OPTIONS pages</h3>
+ <h3 class='topic' onclick='toggle(this)'>7.23 - OPTIONS pages and
Swagger</h3>
<div class='topic'>
<p>
One of the most useful features of Juneau is the
ability to generate Swagger-based OPTIONS pages for self-documenting designs
@@ -21172,22 +21196,22 @@
<h5 class='toc'>What's new in each release</h5>
<ul class='toc'>
<li><p><a class='doclink' href='#7.1.1'>7.1.1 (TBD)</a></p>
- <li><p><a class='doclink' href='#7.1.0'>7.1.0 (TBD)</a></p>
+ <li><p><a class='doclink' href='#7.1.0'>7.1.0 (Mar 08,
2018)</a></p>
<li><p><a class='doclink' href='#7.0.1'>7.0.1 (Dec 24,
2017)</a></p>
<li><p><a class='doclink' href='#7.0.0'>7.0.0 (Oct 25,
2017)</a></p>
- <li><p><a class='doclink' href='#6.4.0'>6.4.0 (Oct 5,
2017)</a></p>
- <li><p><a class='doclink' href='#6.3.1'>6.3.1 (Aug 1,
2017)</a></p>
+ <li><p><a class='doclink' href='#6.4.0'>6.4.0 (Oct 05,
2017)</a></p>
+ <li><p><a class='doclink' href='#6.3.1'>6.3.1 (Aug 01,
2017)</a></p>
<li><p><a class='doclink' href='#6.3.0'>6.3.0 (Jun 30,
2017)</a></p>
<li><p><a class='doclink' href='#6.2.0'>6.2.0 (Apr 28,
2017)</a></p>
<li><p><a class='doclink' href='#6.1.0'>6.1.0 (Feb 25,
2017)</a></p>
- <li><p><a class='doclink' href='#6.0.1'>6.0.1 (Jan 3,
2017)</a></p>
- <li><p><a class='doclink' href='#6.0.0'>6.0.0 (Oct 3,
2016)</a></p>
+ <li><p><a class='doclink' href='#6.0.1'>6.0.1 (Jan 03,
2017)</a></p>
+ <li><p><a class='doclink' href='#6.0.0'>6.0.0 (Oct 03,
2016)</a></p>
<li><p><a class='doclink' href='#5.2.0.1'>5.2.0.1 (Mar 23,
2016)</a></p>
<li><p><a class='doclink' href='#5.2.0.0'>5.2.0.0 (Dec 30,
2015)</a></p>
- <li><p><a class='doclink' href='#5.1.0.20'>5.1.0.20 (Sept 5,
2015)</a></p>
+ <li><p><a class='doclink' href='#5.1.0.20'>5.1.0.20 (Sept 05,
2015)</a></p>
<li><p><a class='doclink' href='#5.1.0.19'>5.1.0.19 (Aug 15,
2015)</a></p>
- <li><p><a class='doclink' href='#5.1.0.18'>5.1.0.18 (Aug 5,
2015)</a></p>
- <li><p><a class='doclink' href='#5.1.0.17'>5.1.0.17 (Aug 3,
2015)</a></p>
+ <li><p><a class='doclink' href='#5.1.0.18'>5.1.0.18 (Aug 05,
2015)</a></p>
+ <li><p><a class='doclink' href='#5.1.0.17'>5.1.0.17 (Aug 03,
2015)</a></p>
<li><p><a class='doclink' href='#5.1.0.16'>5.1.0.16 (June 28,
2015)</a></p>
<li><p><a class='doclink' href='#5.1.0.15'>5.1.0.15 (May 24,
2015)</a></p>
<li><p><a class='doclink' href='#5.1.0.14'>5.1.0.14 (May 10,
2015)</a></p>
@@ -21195,11 +21219,11 @@
<li><p><a class='doclink' href='#5.1.0.12'>5.1.0.12 (Mar 28,
2015)</a></p>
<li><p><a class='doclink' href='#5.1.0.11'>5.1.0.11 (Feb 14,
2015)</a></p>
<li><p><a class='doclink' href='#5.1.0.10'>5.1.0.10 (Dec 23,
2014)</a></p>
- <li><p><a class='doclink' href='#5.1.0.9'>5.1.0.9 (Dec 1,
2014)</a></p>
+ <li><p><a class='doclink' href='#5.1.0.9'>5.1.0.9 (Dec 01,
2014)</a></p>
<li><p><a class='doclink' href='#5.1.0.8'>5.1.0.8 (Oct 25,
2014)</a></p>
- <li><p><a class='doclink' href='#5.1.0.7'>5.1.0.7 (Oct 5,
2014)</a></p>
+ <li><p><a class='doclink' href='#5.1.0.7'>5.1.0.7 (Oct 05,
2014)</a></p>
<li><p><a class='doclink' href='#5.1.0.6'>5.1.0.6 (Sept 21,
2014)</a></p>
- <li><p><a class='doclink' href='#5.1.0.5'>5.1.0.5 (Sept 1,
2014)</a></p>
+ <li><p><a class='doclink' href='#5.1.0.5'>5.1.0.5 (Sept 01,
2014)</a></p>
<li><p><a class='doclink' href='#5.1.0.4'>5.1.0.4 (Aug 25,
2014)</a></p>
<li><p><a class='doclink' href='#5.1.0.3'>5.1.0.3 (Jun 28,
2014)</a></p>
<li><p><a class='doclink' href='#5.1.0.2'>5.1.0.2 (Apr 27,
2014)</a></p>
@@ -21209,20 +21233,20 @@
<li><p><a class='doclink' href='#5.0.0.35'>5.0.0.35 (Nov 26,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.34'>5.0.0.34 (Nov 10,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.33'>5.0.0.33 (Oct 20,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.32'>5.0.0.32 (Oct 5,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.31'>5.0.0.31 (Aug 9,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.30'>5.0.0.30 (Aug 8,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.29'>5.0.0.29 (Aug 2,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.28'>5.0.0.28 (July 9,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.27'>5.0.0.27 (July 7,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.26'>5.0.0.26 (Jun 5,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.32'>5.0.0.32 (Oct 05,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.31'>5.0.0.31 (Aug 09,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.30'>5.0.0.30 (Aug 08,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.29'>5.0.0.29 (Aug 02,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.28'>5.0.0.28 (July 09,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.27'>5.0.0.27 (July 07,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.26'>5.0.0.26 (Jun 05,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.25'>5.0.0.25 (May 11,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.24'>5.0.0.24 (May 9,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.24'>5.0.0.24 (May 09,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.23'>5.0.0.23 (Apr 14,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.22'>5.0.0.22 (Apr 12,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.21'>5.0.0.21 (Apr 9,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.20'>5.0.0.20 (Apr 7,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.19'>5.0.0.19 (Apr 1,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.21'>5.0.0.21 (Apr 09,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.20'>5.0.0.20 (Apr 07,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.19'>5.0.0.19 (Apr 01,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.18'>5.0.0.18 (Mar 27,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.17'>5.0.0.17 (Mar 25,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.16'>5.0.0.16 (Mar 25,
2013)</a></p>
@@ -21230,15 +21254,15 @@
<li><p><a class='doclink' href='#5.0.0.14'>5.0.0.14 (Mar 23,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.13'>5.0.0.13 (Mar 14,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.12'>5.0.0.12 (Mar 10,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.11'>5.0.0.11 (Mar 8,
2013)</a></p>
- <li><p><a class='doclink' href='#5.0.0.10'>5.0.0.10 (Mar 7,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.11'>5.0.0.11 (Mar 08,
2013)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.10'>5.0.0.10 (Mar 07,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.9'>5.0.0.9 (Feb 26,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.8'>5.0.0.8 (Jan 30,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.7'>5.0.0.7 (Jan 20,
2013)</a></p>
<li><p><a class='doclink' href='#5.0.0.6'>5.0.0.6 (Oct 30,
2012)</a></p>
<li><p><a class='doclink' href='#5.0.0.5'>5.0.0.5 (Oct 29,
2012)</a></p>
- <li><p><a class='doclink' href='#5.0.0.4'>5.0.0.4 (Oct 7,
2012)</a></p>
- <li><p><a class='doclink' href='#5.0.0.3'>5.0.0.3 (Oct 3,
2012)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.4'>5.0.0.4 (Oct 07,
2012)</a></p>
+ <li><p><a class='doclink' href='#5.0.0.3'>5.0.0.3 (Oct 03,
2012)</a></p>
<li><p><a class='doclink' href='#5.0.0.2'>5.0.0.2 (Sept 28,
2012)</a></p>
<li><p><a class='doclink' href='#5.0.0.1'>5.0.0.1 (Jun 14,
2012)</a></p>
<li><p><a class='doclink' href='#5.0.0.0'>5.0.0.0 (Jun 11,
2012)</a></p>
@@ -21258,6 +21282,12 @@
Fixed bug where
<code><ja>@Bean</ja>(typeName)</code> was not being detected on non-bean POJO
classes.
<li>
Fixed bug where HTML-Schema was not being
rendered correctly.
+ <li>
+ Support for POJO examples:
+ <ul class='doctree'>
+ <li class='jf'>{@link
org.apache.juneau.BeanContext#BEAN_examples}
+ <li class='ja'>{@link
org.apache.juneau.annotation.Example}
+ </ul>
</ul>
<h5 class='topic w800'>juneau-server</h5>
@@ -21278,7 +21308,7 @@
<!--
===========================================================================================================
-->
<a id='7.1.0'></a>
- <h3 class='topic' onclick='toggle(this)'>7.1.0 (TBD)</h3>
+ <h3 class='topic' onclick='toggle(this)'>7.1.0 (Mar 08, 2018)</h3>
<div class='topic'>
<p>
Version 7.1.0 is a major update with major
implementation refactoring across all aspects of the product.
diff --git a/juneau-examples/juneau-examples-rest/examples.cfg
b/juneau-examples/juneau-examples-rest/examples.cfg
index 62eaa8f..9be46ca 100755
--- a/juneau-examples/juneau-examples-rest/examples.cfg
+++ b/juneau-examples/juneau-examples-rest/examples.cfg
@@ -163,6 +163,12 @@ org.eclipse.jetty.LEVEL = WARN
derby.stream.error.file = $C{Logging/logDir}/derby-errors.log
+# Note that any configuration properties can also be set globally as system
properties...
+
+# Disable classpath resource caching.
+# Useful if you're attached using a debugger and you're modifying classpath
resources while running.
+RestContext.useClasspathResourceCaching.b = false
+
#=======================================================================================================================
# DockerRegistryResource properties
#=======================================================================================================================
diff --git
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
index 1e339c0..14eca5b 100644
---
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
+++
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
@@ -77,6 +77,7 @@ import org.apache.juneau.rest.widget.*;
LogsResource.class,
DockerRegistryResource.class,
PredefinedLabelsResource.class,
+ StaticFilesResource.class,
DebugResource.class,
ShutdownResource.class
}
diff --git
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
new file mode 100644
index 0000000..0966ccf
--- /dev/null
+++
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
@@ -0,0 +1,59 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you 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 org.apache.juneau.examples.rest;
+
+import static org.apache.juneau.http.HttpMethodName.*;
+
+import org.apache.juneau.dto.*;
+import org.apache.juneau.microservice.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.widget.*;
+
+/**
+ * Sample resource that shows how to generate ATOM feeds.
+ */
+@RestResource(
+ path="/staticFiles",
+ title="Sample static files resource",
+ description="Sample resource that shows how to use static files.",
+ htmldoc=@HtmlDoc(
+ widgets={
+ ContentTypeMenuItem.class,
+ StyleMenuItem.class
+ },
+ navlinks={
+ "up: request:/..",
+ "options: servlet:/?method=OPTIONS",
+ "$W{ContentTypeMenuItem}",
+ "$W{StyleMenuItem}",
+ "source:
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{staticFilesResource}.java"
+ }
+ ),
+ staticFiles= {
+ // Serve up files in /files under the child URI /static
+ "static:files"
+ }
+)
+public class StaticFilesResource extends BasicRestServletJena {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * GET request handler
+ */
+ @RestMethod(name=GET, path="/", summary="Get the sample ATOM feed")
+ public LinkString[] getFiles() throws Exception {
+ return new LinkString[] {
+ new LinkString("petstore.html","static/petstore.html")
+ };
+ }
+}
diff --git
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/files/petstore.html
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/files/petstore.html
new file mode 100644
index 0000000..ef4bcbe
--- /dev/null
+++
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/files/petstore.html
@@ -0,0 +1,213 @@
+<!--
+
***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+ * to you 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.
*
+
***************************************************************************************************************************
+-->
+
+<html>
+<head>
+
+<style class='text/css'>
+
+.method-button {
+ display: inline-block;
+ font-size: 14px;
+ font-weight: 700;
+ min-width: 60px;
+ padding: 6px 15px;
+ text-align: center;
+ border-radius: 3px;
+ text-shadow: 0 1px 0 rgba(0,0,0,.1);
+ font-family: Titillium Web,sans-serif;
+ color: #fff;
+}
+
+.get .method-button { background: rgb(97,175,254); }
+.put .method-button { background: rgb(252,161,48); }
+.post .method-button { background: rgb(73,204,144); }
+.delete .method-button { background: rgb(249,62,62); }
+.options .method-button { background: rgb(153,102,255); }
+.deprecated .method-button { background: rgb(170,170,170); }
+.other .method-button { background: rgb(230,230,0); }
+
+.opblock {
+ margin: 0 0 15px;
+ font-family: Open Sans,sans-serif;
+ align-items: center;
+ cursor: pointer;
+ border-radius: 4px;
+}
+
+.opblock.get { background: rgba(97,175,254,.1); border: 1px solid
rgb(97,175,254); }
+.opblock.put { background: rgba(252,161,48,.1); border: 1px solid
rgb(252,161,48); }
+.opblock.post { background: rgba(73,204,144,.1); border: 1px solid
rgb(73,204,144); }
+.opblock.options { background: rgba(153,102,255,.1); border: 1px solid
rgb(153,102,255); }
+.opblock.delete { background: rgba(249,62,62,.1); border: 1px solid
rgb(249,62,62); }
+.opblock.deprecated { background: rgba(170,170,170,.1); border: 1px solid
rgb(170,170,170); }
+.opblock.other { background: rgba(230,230,0,0.1); border: 1px solid
rgb(230,230,0); }
+
+.opblock-summary {
+ padding:5px;
+}
+
+.opblock-summary .path {
+ font-size: 16px;
+ word-break: break-all;
+ font-family: Source Code Pro,monospace;
+ font-weight: 600;
+ color: #3b4151;
+ padding:10px;
+}
+
+.opblock.deprecated .opblock-summary .path { color: #8f9199; text-decoration:
line-through;}
+.opblock.deprecated .opblock-summary .description { color: #8f9199 }
+
+.opblock-summary .description {
+ font-family: Open Sans,sans-serif;
+ color: #3b4151;
+ font-size: 13px;
+ padding:10px;
+}
+
+.opblock-section-header {
+ padding: 8px 20px;
+ background: hsla(0,0%,100%,.8);
+ box-shadow: 0 1px 2px rgba(0,0,0,.1);
+ font-family: Open Sans,sans-ser;
+ color: #8f9199;
+}
+
+.opblock-section-header .title {
+ font-size: 14px;
+ font-family: Titillium Web,sans-serif;
+ color: #3b4151;
+ margin: 0px;
+}
+
+.is-open .opblock-contents {
+ display: block;
+}
+.is-closed .opblock-contents {
+ display: none;
+}
+
+.parameters {
+ padding: 0 10px;
+ border-collapse: collapse;
+ margin: 20px;
+ width: 95%;
+}
+
+.parameters th {
+ font-size: 12px;
+ font-weight: 700;
+ padding: 12px 0;
+ text-align: left;
+ border-bottom: 1px solid rgba(59,65,81,.2);
+ font-family: Open Sans,sans-serif;
+ color: #3b4151;
+}
+
+ </style>
+ <script>
+
+ function toggle(e) {
+ var isOpen = e.classList.contains("is-open");
+ if (isOpen) {
+ e.classList.add("is-closed");
+ e.classList.remove("is-open");
+ } else {
+ e.classList.add("is-open");
+ e.classList.remove("is-closed");
+ }
+ window.getSelection().removeAllRanges();
+ }
+ </script>
+</head>
+<body>
+
+<div class='opblock get is-open' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>GET</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+ <div class='opblock-contents'>
+ <div class="table-container">
+ <div class="opblock-section-header">
+ <h4 class="title">Parameters</h4>
+ </div>
+ <table class="parameters">
+ <tr>
+ <th>Name</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td>foo</td>
+ <td>bar</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+</div>
+
+<div class='opblock put is-closed' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>PUT</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+</div>
+
+<div class='opblock post is-closed' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>POST</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+</div>
+<div class='opblock delete is-closed' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>DELETE</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+</div>
+<div class='opblock deprecated is-closed' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>GET</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+</div>
+
+<div class='opblock other is-closed' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>OTHER</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+</div>
+
+<div class='opblock options is-closed' onclick='toggle(this)'>
+ <div class='opblock-summary'>
+ <span class='method-button'>OPTIONS</span>
+ <span class='path'>/pet</span>
+ <span class='description'>Everything about your Pets</span>
+ </div>
+</div>
+
+
+</body>
+
+</html>
\ No newline at end of file
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index c6f1cc5..73cb895 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -2739,7 +2739,8 @@ public final class RestContext extends BeanContext {
allowHeaderParams,
allowBodyParam,
renderResponseStackTraces,
- useStackTraceHashes;
+ useStackTraceHashes,
+ useClasspathResourceCaching;
private final String
defaultCharset,
clientVersionHeader,
@@ -2897,7 +2898,7 @@ public final class RestContext extends BeanContext {
mimetypesFileTypeMap.addMimeTypes(mimeType);
ClasspathResourceFinder rf =
getInstanceProperty(REST_classpathResourceFinder,
ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class, true, this);
- boolean useClasspathResourceCaching =
getProperty(REST_useClasspathResourceCaching, boolean.class, true);
+ useClasspathResourceCaching =
getProperty(REST_useClasspathResourceCaching, boolean.class, true);
staticResourceManager = new
ClasspathResourceManager(resourceClass, rf, useClasspathResourceCaching);
consumes = getListProperty(REST_consumes,
MediaType.class, parsers.getSupportedMediaTypes());
@@ -2957,7 +2958,7 @@ public final class RestContext extends BeanContext {
RestMethod a =
method.getAnnotation(RestMethod.class);
methodsFound.add(method.getName() + ","
+ a.name() + "," + a.path());
try {
- if (!
Modifier.isPublic(method.getModifiers()))
+ if (! isPublic(method))
throw new
RestServletException("@RestMethod method {0}.{1} must be defined as public.",
resourceClass.getName(), method.getName());
RestJavaMethod sm = new
RestJavaMethod(resource, method, this);
@@ -3032,7 +3033,7 @@ public final class RestContext extends BeanContext {
switch(he) {
case PRE_CALL: {
if (!
_preCallMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_preCallMethods.put(sig, m);
_preCallMethodParams.add(findParams(m, null, true));
}
@@ -3040,7 +3041,7 @@ public final class RestContext extends BeanContext {
}
case POST_CALL: {
if (!
_postCallMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_postCallMethods.put(sig, m);
_postCallMethodParams.add(findParams(m, null, true));
}
@@ -3048,7 +3049,7 @@ public final class RestContext extends BeanContext {
}
case START_CALL: {
if (!
_startCallMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_startCallMethods.put(sig, m);
_startCallMethodParams.add(m.getParameterTypes());
ClassUtils.assertArgsOfType(m, HttpServletRequest.class,
HttpServletResponse.class);
@@ -3057,7 +3058,7 @@ public final class RestContext extends BeanContext {
}
case END_CALL: {
if (!
_endCallMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_endCallMethods.put(sig, m);
_endCallMethodParams.add(m.getParameterTypes());
ClassUtils.assertArgsOfType(m, HttpServletRequest.class,
HttpServletResponse.class);
@@ -3066,7 +3067,7 @@ public final class RestContext extends BeanContext {
}
case POST_INIT: {
if (!
_postInitMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_postInitMethods.put(sig, m);
_postInitMethodParams.add(m.getParameterTypes());
ClassUtils.assertArgsOfType(m, RestContext.class);
@@ -3075,7 +3076,7 @@ public final class RestContext extends BeanContext {
}
case POST_INIT_CHILD_FIRST: {
if (!
_postInitChildFirstMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_postInitChildFirstMethods.put(sig, m);
_postInitChildFirstMethodParams.add(m.getParameterTypes());
ClassUtils.assertArgsOfType(m, RestContext.class);
@@ -3084,7 +3085,7 @@ public final class RestContext extends BeanContext {
}
case DESTROY: {
if (!
_destroyMethods.containsKey(sig)) {
-
Visibility.setAccessible(m);
+
setAccessible(m, false);
_destroyMethods.put(sig, m);
_destroyMethodParams.add(m.getParameterTypes());
ClassUtils.assertArgsOfType(m, RestContext.class);
@@ -3302,8 +3303,10 @@ public final class RestContext extends BeanContext {
String name =
(i == -1 ? p2 : p2.substring(i+1));
String
mediaType = mimetypesFileTypeMap.getContentType(name);
Map<String,Object> responseHeaders = sfm.responseHeaders != null ?
sfm.responseHeaders : staticFileResponseHeaders;
-
staticFilesCache.put(pathInfo, new
StreamResource(MediaType.forString(mediaType), responseHeaders, is));
- return
staticFilesCache.get(pathInfo);
+ StreamResource
sr = new StreamResource(MediaType.forString(mediaType), responseHeaders, is);
+ if
(useClasspathResourceCaching)
+
staticFilesCache.put(pathInfo, sr);
+ return sr;
}
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index 0a3a2d8..358d26a 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -15,6 +15,7 @@ package org.apache.juneau.rest;
import static org.apache.juneau.internal.ArrayUtils.*;
import static org.apache.juneau.internal.ReflectionUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.parser.Parser.*;
import static org.apache.juneau.rest.RestContext.*;
import static org.apache.juneau.serializer.Serializer.*;
@@ -285,7 +286,7 @@ public class RestContextBuilder extends BeanContextBuilder
implements ServletCon
Map<String,Method> map = new LinkedHashMap<>();
for (Method m : ClassUtils.getAllMethods(this.resourceClass,
true)) {
if (m.isAnnotationPresent(RestHook.class) &&
m.getAnnotation(RestHook.class).value() == HookEvent.INIT) {
- Visibility.setAccessible(m);
+ setAccessible(m, false);
String sig = ClassUtils.getMethodSignature(m);
if (! map.containsKey(sig))
map.put(sig, m);
@@ -293,10 +294,10 @@ public class RestContextBuilder extends
BeanContextBuilder implements ServletCon
}
for (Method m : map.values()) {
ClassUtils.assertArgsOfType(m,
RestContextBuilder.class, ServletConfig.class);
- Class<?>[] argTypes = m.getParameterTypes();
- Object[] args = new Object[argTypes.length];
+ Class<?>[] pt = m.getParameterTypes();
+ Object[] args = new Object[pt.length];
for (int i = 0; i < args.length; i++) {
- if (argTypes[i] == RestContextBuilder.class)
+ if (pt[i] == RestContextBuilder.class)
args[i] = this;
else
args[i] = this.inner;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
index 65a2361..1229aa4 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
@@ -392,7 +392,7 @@ public class RestJavaMethod implements
Comparable<RestJavaMethod> {
params = context.findParams(method,
pathPattern, false);
// Need this to access methods in anonymous
inner classes.
- method.setAccessible(true);
+ setAccessible(method, true);
} catch (RestServletException e) {
throw e;
} catch (Exception e) {
--
To stop receiving notification emails like this one, please contact
[email protected].