Skip to content

Commit

Permalink
Added a pluggable way to deal with duplicate components being identif…
Browse files Browse the repository at this point in the history
…ied.
  • Loading branch information
simonbrowndotje committed Sep 16, 2018
1 parent 9a8f314 commit fd3fd68
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public abstract class AbstractComponentFinderStrategy implements ComponentFinder

protected List<SupportingTypesStrategy> supportingTypesStrategies = new ArrayList<>();

private DuplicateComponentStrategy duplicateComponentStrategy = new ThrowExceptionDuplicateComponentStrategy();

protected AbstractComponentFinderStrategy(SupportingTypesStrategy... strategies) {
Arrays.stream(strategies).forEach(this::addSupportingTypesStrategy);
}
Expand Down Expand Up @@ -162,15 +164,36 @@ protected Set<Component> findClassesWithAnnotation(Class<? extends Annotation> t
for (Class<?> componentType : componentTypes) {
if (!includePublicTypesOnly || Modifier.isPublic(componentType.getModifiers())) {
final Container container = getComponentFinder().getContainer();
components.add(container.addComponent(
componentType.getSimpleName(),
componentType.getCanonicalName(),
"",
technology));
Component newComponent = addComponent(
container,
componentType.getSimpleName(),
componentType.getCanonicalName(),
"",
technology);

if (newComponent != null) {
components.add(newComponent);
}
}
}

return components;
}

public void setDuplicateComponentStrategy(DuplicateComponentStrategy duplicateComponentStrategy) {
if (duplicateComponentStrategy != null) {
this.duplicateComponentStrategy = duplicateComponentStrategy;
} else {
this.duplicateComponentStrategy = new ThrowExceptionDuplicateComponentStrategy();
}
}

protected Component addComponent(Container container, String name, String type, String description, String technology) {
if (container.getComponentWithName(name) == null) {
return container.addComponent(name, type, description, technology);
} else {
return duplicateComponentStrategy.duplicateComponentFound(container.getComponentWithName(name), name, type, description, technology);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.structurizr.analysis;

public class DuplicateComponentException extends RuntimeException {

DuplicateComponentException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.structurizr.analysis;

import com.structurizr.model.Component;

/**
* Defines a strategy that should be called when a duplicate component is found.
*/
public interface DuplicateComponentStrategy {

/**
* Called when a duplicate component is found.
*
* @param component the existing Component object
* @param name the new component name
* @param type the new component type
* @param description the new description
* @param technology the new technology
*
* @return a Component instance, or null if a new component should not be created
*/
Component duplicateComponentFound(Component component, String name, String type, String description, String technology);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.structurizr.analysis;

import com.structurizr.model.Component;

/**
* Throws an exception when a duplicate component is found.
*/
public class ThrowExceptionDuplicateComponentStrategy implements DuplicateComponentStrategy {

@Override
public Component duplicateComponentFound(Component component, String name, String type, String description, String technology) {
throw new DuplicateComponentException(String.format(
"A component named \"%s\" already exists in the container named \"%s\".",
component.getName(),
component.getContainer().getName()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ protected Set<Component> doFindComponents() {
for (TypeMatcher typeMatcher : typeMatchers) {
if (typeMatcher.matches(type)) {
final Container container = getComponentFinder().getContainer();
Component component = container.addComponent(
Component newComponent = addComponent(
container,
type.getSimpleName(),
type.getCanonicalName(),
typeMatcher.getDescription(),
typeMatcher.getTechnology());
components.add(component);

if (newComponent != null) {
components.add(newComponent);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public void test_findingDuplicateComponentsThrowsAnException() throws Exception
try {
componentFinder.findComponents();
fail();
} catch (IllegalArgumentException iae) {
assertTrue(iae.getMessage().startsWith("A component named '"));
assertTrue(iae.getMessage().endsWith("' already exists for this container."));
} catch (DuplicateComponentException dce) {
assertTrue(dce.getMessage().startsWith("A component named \""));
assertTrue(dce.getMessage().endsWith("\" already exists in the container named \"Name\"."));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ protected Set<Component> findInterfacesForImplementationClassesWithAnnotation(Cl
continue;
} else if (annotatedType.isInterface()) {
// the annotated type is an interface, so we're done
components.add(container.addComponent(
annotatedType.getSimpleName(), annotatedType.getCanonicalName(), "", technology));
Component newComponent = addComponent(container, annotatedType.getSimpleName(), annotatedType.getCanonicalName(), "", technology);

if (newComponent != null) {
components.add(newComponent);
}
} else {
// The Spring @Component, @Service and @Repository annotations are typically used to annotate implementation
// classes, but we really want to find the interface type and use that to represent the component. Why?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,17 @@ private Set<Component> findSpringRepositoryInterfaces() {
for (Class<?> componentType : componentTypes) {
if (!includePublicTypesOnly || Modifier.isPublic(componentType.getModifiers())) {
final Container container = getComponentFinder().getContainer();
componentsFound.add(container.addComponent(
Component newComponent = addComponent(
container,
componentType.getSimpleName(),
componentType.getCanonicalName(),
"",
SPRING_REPOSITORY));
SPRING_REPOSITORY);


if (newComponent != null) {
componentsFound.add(newComponent);
}
}
}

Expand Down

0 comments on commit fd3fd68

Please sign in to comment.