/** * Finds @UsedByContainer annotations. */ private void findUsedByContainerAnnotations(Component component, String typeName) { try { Class<?> type = getTypeRepository().loadClass(typeName); UsedByContainer[] annotations = type.getAnnotationsByType(UsedByContainer.class); for (UsedByContainer annotation : annotations) { String name = annotation.name(); String description = annotation.description(); String technology = annotation.technology(); Container container = findContainerByNameOrCanonicalName(component, name); if (container != null) { container.uses(component, description, technology); } else { log.warn("A container named \"" + name + "\" could not be found."); } } } catch (ClassNotFoundException e) { log.warn("Could not load type " + typeName); } }
@Override protected Set<Component> doFindComponents() { Set<Component> components = new HashSet<>(); Container container = getComponentFinder().getContainer(); // find all types that have been annotated @Component Set<Class<?>> componentTypes = findTypesAnnotatedWith(com.structurizr.annotation.Component.class); for (Class<?> componentType : componentTypes) { Component component = container.addComponent( componentType.getSimpleName(), componentType, componentType.getAnnotation(com.structurizr.annotation.Component.class).description(), componentType.getAnnotation(com.structurizr.annotation.Component.class).technology() ); components.add(component); } return components; }
@Override public void afterFindComponents() { // this will find component dependencies, but the relationship descriptions // will be empty because we can't get that from the code super.afterFindComponents(); for (Component component : getComponentFinder().getContainer().getComponents()) { for (CodeElement codeElement : component.getCode()) { // find the efferent dependencies findUsesComponentAnnotations(component, codeElement.getType()); findUsesSoftwareSystemsAnnotations(component, codeElement.getType()); findUsesContainerAnnotations(component, codeElement.getType()); // and also the afferent dependencies findUsedByPersonAnnotations(component, codeElement.getType()); findUsedBySoftwareSystemAnnotations(component, codeElement.getType()); findUsedByContainerAnnotations(component, codeElement.getType()); } } }
@Test public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheDefaultStrategyIsUsed() throws Exception { ComponentFinder componentFinder = new ComponentFinder( webApplication, "com.structurizr.analysis.reflections.supportingTypes.myapp", new StructurizrAnnotationsComponentFinderStrategy() ); componentFinder.findComponents(); assertEquals(2, webApplication.getComponents().size()); Component myController = webApplication.getComponentWithName("MyController"); Component myRepository = webApplication.getComponentWithName("MyRepository"); assertEquals(1, myController.getRelationships().size()); assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); // the default strategy for supporting types is to find the first implementation // class if the component type is an interface assertEquals(1, myController.getCode().size()); assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.web.MyController", CodeElementRole.Primary); assertEquals(2, myRepository.getCode().size()); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); }
@Test public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheReferencedTypesInSamePackageSupportingTypesStrategyIsUsed() throws Exception { ComponentFinder componentFinder = new ComponentFinder( webApplication, "com.structurizr.analysis.reflections.supportingTypes.myapp", new StructurizrAnnotationsComponentFinderStrategy( new FirstImplementationOfInterfaceSupportingTypesStrategy(), new ReferencedTypesInSamePackageSupportingTypesStrategy() ) ); componentFinder.findComponents(); assertEquals(2, webApplication.getComponents().size()); Component myController = webApplication.getComponentWithName("MyController"); Component myRepository = webApplication.getComponentWithName("MyRepository"); assertEquals(1, myController.getRelationships().size()); assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); assertEquals(1, myController.getCode().size()); assertEquals(3, myRepository.getCode().size()); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryRowMapper", CodeElementRole.Supporting); }
@Test public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheReferencedTypesStrategyIsUsedAndIndirectlyReferencedTypesShouldBeExcluded() throws Exception { ComponentFinder componentFinder = new ComponentFinder( webApplication, "com.structurizr.analysis.reflections.supportingTypes.myapp", new StructurizrAnnotationsComponentFinderStrategy( new FirstImplementationOfInterfaceSupportingTypesStrategy(), new ReferencedTypesSupportingTypesStrategy(false) ) ); componentFinder.findComponents(); assertEquals(2, webApplication.getComponents().size()); Component myController = webApplication.getComponentWithName("MyController"); Component myRepository = webApplication.getComponentWithName("MyRepository"); assertEquals(1, myController.getRelationships().size()); assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); assertEquals(2, myController.getCode().size()); assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.web.MyController", CodeElementRole.Primary); assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); assertEquals(4, myRepository.getCode().size()); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryRowMapper", CodeElementRole.Supporting); }
/** * Find the @UsesContainer annotations. */ private void findUsesContainerAnnotations(Component component, String typeName) { try { Class<?> type = getTypeRepository().loadClass(typeName); UsesContainer[] annotations = type.getAnnotationsByType(UsesContainer.class); for (UsesContainer annotation : annotations) { String name = annotation.name(); String description = annotation.description(); String technology = annotation.technology(); Container container = findContainerByNameOrCanonicalName(component, name); if (container != null) { component.uses(container, description, technology); } else { log.warn("A container named \"" + name + "\" could not be found."); } } } catch (ClassNotFoundException e) { log.warn("Could not load type " + typeName); } }
@Test public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheReferencedTypesStrategyIsUsedAndIndirectlyReferencedTypesShouldBeIncluded() throws Exception { ComponentFinder componentFinder = new ComponentFinder( webApplication, "com.structurizr.analysis.reflections.supportingTypes.myapp", new StructurizrAnnotationsComponentFinderStrategy( new FirstImplementationOfInterfaceSupportingTypesStrategy(), new ReferencedTypesSupportingTypesStrategy() ) ); componentFinder.findComponents(); assertEquals(2, webApplication.getComponents().size()); Component myController = webApplication.getComponentWithName("MyController"); Component myRepository = webApplication.getComponentWithName("MyRepository"); assertEquals(1, myController.getRelationships().size()); assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); assertEquals(2, myController.getCode().size()); assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.web.MyController", CodeElementRole.Primary); assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); assertEquals(5, myRepository.getCode().size()); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryRowMapper", CodeElementRole.Supporting); assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.util.RowMapperHelper", CodeElementRole.Supporting); }
@Before public void setUp() throws Exception { Workspace workspace = new Workspace("Name", ""); Model model = workspace.getModel(); external1 = model.addSoftwareSystem("External 1", ""); external2 = model.addSoftwareSystem("External 2", ""); anonymousUser = model.addPerson("Anonymous User", ""); authenticatedUser = model.addPerson("Authenticated User", ""); softwareSystem = model.addSoftwareSystem("Software System", ""); webBrowser = softwareSystem.addContainer("Web Browser", "", ""); apiClient = softwareSystem.addContainer("API Client", "", ""); webApplication = softwareSystem.addContainer("Name", "", ""); database = softwareSystem.addContainer("Database", "", ""); // the default usage of the StructurizrAnnotationsComponentFinderStrategy // just has the FirstImplementationOfInterfaceSupportingTypesStrategy included componentFinder = new ComponentFinder( webApplication, "test.StructurizrAnnotationsComponentFinderStrategy", new StructurizrAnnotationsComponentFinderStrategy() ); componentFinder.findComponents(); }
webApplication, "com.structurizr.example.annotations", new StructurizrAnnotationsComponentFinderStrategy() ); componentFinder.findComponents();