support requires annotation on WebElements

This commit is contained in:
Sebastian Sdorra
2020-04-16 11:57:38 +02:00
parent b9acc7c9f6
commit 81e8dc428c
9 changed files with 188 additions and 150 deletions

View File

@@ -26,6 +26,7 @@ package sonia.scm.plugin;
//~--- non-JDK imports --------------------------------------------------------
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@@ -43,12 +44,12 @@ import java.util.Set;
* @since 2.0.0
*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@EqualsAndHashCode
@XmlAccessorType(XmlAccessType.FIELD)
public final class ClassElement {
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class ClassElement {
@XmlElement(name = "class")
private String clazz;
private String description;

View File

@@ -76,5 +76,5 @@ public interface ExtensionProcessor
*
* @return collected web elements
*/
public Iterable<WebElementDescriptor> getWebElements();
public Iterable<WebElementExtension> getWebElements();
}

View File

@@ -26,18 +26,20 @@ package sonia.scm.plugin;
//~--- non-JDK imports --------------------------------------------------------
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import sonia.scm.xml.XmlArrayStringAdapter;
//~--- JDK imports ------------------------------------------------------------
import java.util.Arrays;
import java.util.Objects;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.Arrays;
/**
* Descriptor for web elements such as filter or servlets. A web element can be registered by using the
@@ -46,130 +48,33 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* @author Sebastian Sdorra
* @since 2.0.0
*/
@Getter
@AllArgsConstructor
@ToString
@EqualsAndHashCode
@XmlRootElement(name = "web-element")
@XmlAccessorType(XmlAccessType.FIELD)
public final class WebElementDescriptor
{
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public final class WebElementDescriptor extends ClassElement {
/**
* Constructs ...
*
*/
WebElementDescriptor() {}
/**
* Constructs ...
*
*
* @param clazz
* @param pattern
* @param morePatterns
* @param regex
*/
public WebElementDescriptor(Class<?> clazz, String pattern,
String[] morePatterns, boolean regex)
{
this.clazz = clazz;
this.pattern = pattern;
this.morePatterns = morePatterns;
this.regex = regex;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Class<?> getClazz()
{
return clazz;
}
/**
* Method description
*
*
* @return
*/
public String[] getMorePatterns()
{
String[] patterns;
if (morePatterns != null)
{
patterns = Arrays.copyOf(morePatterns, morePatterns.length);
}
else
{
patterns = new String[0];
}
return patterns;
}
/**
* Method description
*
*
* @return
*/
public String getPattern()
{
return pattern;
}
/**
* Method description
*
*
* @return
*/
public boolean isRegex()
{
return regex;
}
@Override
public int hashCode() {
return Objects.hash(clazz, pattern, Arrays.hashCode(morePatterns), regex);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final WebElementDescriptor other = (WebElementDescriptor) obj;
return Objects.equals(clazz, other.clazz)
&& Objects.equals(pattern, other.pattern)
&& Arrays.equals(morePatterns, other.morePatterns)
&& this.regex == other.regex;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlElement(name = "class")
private Class<?> clazz;
/** Field description */
@XmlJavaTypeAdapter(XmlArrayStringAdapter.class)
private String[] morePatterns;
/** Field description */
@XmlElement(name = "value")
private String pattern;
/** Field description */
@XmlJavaTypeAdapter(XmlArrayStringAdapter.class)
private String[] morePatterns = {};
private boolean regex = false;
public String[] getMorePatterns() {
String[] patterns;
if (morePatterns != null) {
patterns = Arrays.copyOf(morePatterns, morePatterns.length);
} else {
patterns = new String[0];
}
return patterns;
}
}

View File

@@ -0,0 +1,43 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.plugin;
import lombok.Value;
/**
* WebElementExtension can be a servlet or filter which is ready to bind.
* Those extensions are loaded by the {@link ExtensionProcessor} from a {@link WebElementDescriptor}.
*
* We don't know if we can load the defined class from the descriptor, because the class could be optional
* (annotated with {@link Requires}). So we have to load the class as string with {@link WebElementDescriptor} and when
* we know that it is safe to load the class (all requirements are fulfilled), we will create our WebElementExtension.
*
* @since 2.0.0
*/
@Value
public class WebElementExtension {
Class<?> clazz;
WebElementDescriptor descriptor;
}

View File

@@ -0,0 +1,77 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.plugin;
import org.junit.jupiter.api.Test;
import javax.xml.bind.JAXB;
import java.io.StringReader;
import static org.assertj.core.api.Assertions.assertThat;
class WebElementDescriptorTest {
private static final String XML_1 = String.join("\n",
"<web-element>",
" <class>com.hitchhiker.SpaceShip</class>",
" <value>/space/*</value>",
" <regex>false</regex>",
" <morePatterns>/ship/*,/star/*</morePatterns>",
" <requires>scm-magrathea-plugin</requires>",
" <requires>scm-earth-plugin</requires>",
"</web-element>"
);
private static final String XML_2 = String.join("\n",
"<web-element>",
" <class>com.hitchhiker.SpaceShip</class>",
" <value>/space/.*</value>",
" <regex>true</regex>",
"</web-element>"
);
@Test
void shouldUnmarshall() {
WebElementDescriptor descriptor = unmarshal(XML_1);
assertThat(descriptor.getClazz()).isEqualTo("com.hitchhiker.SpaceShip");
assertThat(descriptor.getPattern()).isEqualTo("/space/*");
assertThat(descriptor.getMorePatterns()).containsExactly("/ship/*", "/star/*");
assertThat(descriptor.isRegex()).isFalse();
assertThat(descriptor.getRequires()).containsExactlyInAnyOrder("scm-magrathea-plugin", "scm-earth-plugin");
}
@Test
void shouldUnmarshallWithoutMorePatterns() {
WebElementDescriptor descriptor = unmarshal(XML_2);
assertThat(descriptor.getClazz()).isEqualTo("com.hitchhiker.SpaceShip");
assertThat(descriptor.getMorePatterns()).isEmpty();
assertThat(descriptor.isRegex()).isTrue();
}
private WebElementDescriptor unmarshal(String content) {
return JAXB.unmarshal(new StringReader(content), WebElementDescriptor.class);
}
}

View File

@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
import sonia.scm.Priorities;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.WebElementDescriptor;
import sonia.scm.plugin.WebElementExtension;
//~--- JDK imports ------------------------------------------------------------
@@ -67,25 +67,25 @@ public final class WebElementCollector
* @param elements
*/
@SuppressWarnings("unchecked")
private WebElementCollector(Iterable<WebElementDescriptor> elements)
private WebElementCollector(Iterable<WebElementExtension> elements)
{
List<TypedWebElementDescriptor<? extends Filter>> fl = Lists.newArrayList();
List<TypedWebElementDescriptor<? extends HttpServlet>> sl =
Lists.newArrayList();
for (WebElementDescriptor element : elements)
for (WebElementExtension element : elements)
{
if (Filter.class.isAssignableFrom(element.getClazz()))
{
fl.add(
new TypedWebElementDescriptor<>(
(Class<? extends Filter>) element.getClazz(), element));
(Class<? extends Filter>) element.getClazz(), element.getDescriptor()));
}
else if (Servlet.class.isAssignableFrom(element.getClazz()))
{
sl.add(
new TypedWebElementDescriptor<>(
(Class<? extends HttpServlet>) element.getClazz(), element));
(Class<? extends HttpServlet>) element.getClazz(), element.getDescriptor()));
}
else
{

View File

@@ -115,7 +115,7 @@ public class DefaultExtensionProcessor implements ExtensionProcessor
* @return
*/
@Override
public Iterable<WebElementDescriptor> getWebElements()
public Iterable<WebElementExtension> getWebElements()
{
return collector.getWebElements();
}

View File

@@ -28,15 +28,12 @@ package sonia.scm.plugin;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -45,6 +42,8 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
//~--- JDK imports ------------------------------------------------------------
/**
*
* @author Sebastian Sdorra
@@ -63,6 +62,7 @@ public final class ExtensionCollector
for (ScmModule module : modules) {
collectRootElements(moduleClassLoader, module);
}
for (InstalledPlugin plugin : installedPlugins) {
collectRootElements(plugin.getClassLoader(), plugin.getDescriptor());
}
@@ -218,7 +218,7 @@ public final class ExtensionCollector
*
* @return
*/
public Set<WebElementDescriptor> getWebElements()
public Set<WebElementExtension> getWebElements()
{
return webElements;
}
@@ -272,6 +272,17 @@ public final class ExtensionCollector
return classes;
}
private Set<WebElementExtension> collectWebElementExtensions(ClassLoader defaultClassLoader, Iterable<WebElementDescriptor> descriptors) {
Set<WebElementExtension> webElementExtensions = new HashSet<>();
for (WebElementDescriptor descriptor : descriptors) {
if (isRequirementFulfilled(descriptor)) {
Class<?> loadedClass = loadExtension(defaultClassLoader, descriptor);
webElementExtensions.add(new WebElementExtension(loadedClass, descriptor));
}
}
return webElementExtensions;
}
private Class<?> loadExtension(ClassLoader classLoader, ClassElement extension) {
try {
return classLoader.loadClass(extension.getClazz());
@@ -307,13 +318,14 @@ public final class ExtensionCollector
restProviders.addAll(collectClasses(classLoader, module.getRestProviders()));
restResources.addAll(collectClasses(classLoader, module.getRestResources()));
Iterables.addAll(webElements, module.getWebElements());
webElements.addAll(collectWebElementExtensions(classLoader, module.getWebElements()));
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private final Set<WebElementDescriptor> webElements = Sets.newHashSet();
private final Set<WebElementExtension> webElements = Sets.newHashSet();
/** Field description */
private final Set<Class> restResources = Sets.newHashSet();