{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "While working with legacy systems, you find often many areas in your application that are a mess. Often a mess originates in the problem that it's unclear where to put all the things. But even if you would find a structure that fits your needs, you are always restricted by time and budget which absence is the cause that you cannot put all the software entities where they belong to. In reality, you can only invest a little amount of time for refactoring tasks which prohibits a holistic improvement of your software system.\n", "\n", "But there is another way for long-running, continuous refactorings that I'll show you in this blog post. The idea originates from the outstanding book \"\n", "Object-Oriented Reengineering Patterns\" (the \"OORP book\", [freely available](http://scg.unibe.ch/download/oorp/)) that shows us many ways to tackle legacy code. One pattern that inspired me for this proof of concept \"Tie Code and Questions\" (p. 121) that says\n", "\n", "> Keep the questions and answers concerning your reengineering activities synchronized with the code by storing them directly in the source files.\n", "\n", "This pattern suggests that we write down our knowledge about the code directly in, well, the code itself. The authors even suggest a ways to do it via special comments of by the use of annotations.\n", "\n", "We, too, use this approach for finding hidden structures in software systems by annotating parts of the system with special Java annotations. In our example, we use an annotation named `@Pattern` to identify different architectural and design patterns that are in the code. We'll do that on different levels of code: Packages, classes and methods.\n", "\n", "But only marking code parts isn't sufficient to get an overview over the chaos. Thus, we utilize the annotated code by using [jQAssistant](http://buschmais.github.io/jqassistant/doc/1.3.0/) and [Neo4j](https://neo4j.com/). With these tooling, we can read out those code parts that we've annotated and inspect the dependencies between our findings.\n", "\n", "I'll explain how you can do this by annotating some code parts of the Spring Framework demo application [\"PetClinic\"](https://github.com/JavaOnAutobahn/), that comes with a fully integrated jQAssistant code analysis feature." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# The main idea\n", "The first part of our exercise is easy: We need a special annotation in our code base that we can use to mark specific parts of our code:\n", "\n", "```java\n", "package org.springframework.samples.petclinic.architecture;\n", "\n", "import java.lang.annotation.Retention;\n", "import java.lang.annotation.RetentionPolicy;\n", "\n", "/**\n", " * Marker for a code entity that implements a specific architecture or design patten.\n", " */\n", "@Retention(RetentionPolicy.RUNTIME)\n", "public @interface Pattern {\n", " String value();\n", "}\n", "```" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
t.namev.value
0NamedEntityDataModel
1PetDataModel
2PetTypeDataModel
3PersonDataModel
4BaseEntityDataModel
\n", "
" ], "text/plain": [ " t.name v.value\n", "0 NamedEntity DataModel\n", "1 Pet DataModel\n", "2 PetType DataModel\n", "3 Person DataModel\n", "4 BaseEntity DataModel" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import py2neo\n", "import pandas as pd\n", "\n", "\n", "graph = py2neo.Graph()\n", "\n", "query = \"\"\"\n", "MATCH \n", " (a:Artifact { name: \"spring-petclinic\"})-[*]->\n", " (packageInfo:Java { name: \"package-info\"})-[:ANNOTATED_BY]->\n", " (anno:Annotation)-[:OF_TYPE]->(annoType:Type { name: \"TechnicalAspect\"}),\n", "(anno)-[:HAS]->(v:Value), \n", "(packageInfo)<-[:CONTAINS]-(p:Package)-[:CONTAINS*]->(t:Type)\n", "MERGE (ta:TechnicalAspectTEST {\n", " name : v.value,\n", " type : \"per_package\",\n", " direct : false})\n", "MERGE (ta)<-[:IS_A]-(t)\n", "RETURN t.name, v.value\n", "\"\"\"\n", "\n", "pd.DataFrame(graph.data(query)).head()" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
t.namev.value
0EntityUtilsPersistenceMechanism
1CallMonitoringAspectMonitoring
\n", "
" ], "text/plain": [ " t.name v.value\n", "0 EntityUtils PersistenceMechanism\n", "1 CallMonitoringAspect Monitoring" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "graph = py2neo.Graph()\n", "\n", "query = \"\"\"\n", "MATCH\n", " (a:Artifact { name: \"spring-petclinic\"})-[:CONTAINS*]->\n", " (t:Type)-[:ANNOTATED_BY]->(annotation:Annotation)-[:OF_TYPE]->\n", " (annotationType:Type { name: \"TechnicalAspect\"}),\n", " (annotation)-[:HAS]->(v:Value)\n", "WHERE t.name <> \"package-info\"\n", "MERGE (ta:TechnicalAspect)<-[:IS_A]-(t)\n", "SET \n", " ta.name = v.value,\n", " ta.type = \"class_annotation\",\n", " ta.direct = true\n", "RETURN DISTINCT t.name, v.value\n", "\"\"\"\n", "\n", "pd.DataFrame(graph.data(query)).head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Default Methods" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
m.nameta.name
0setNameNone
1toStringNone
2<init>None
3getNameNone
4NoneNone
\n", "
" ], "text/plain": [ " m.name ta.name\n", "0 setName None\n", "1 toString None\n", "2 None\n", "3 getName None\n", "4 None None" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "graph = py2neo.Graph()\n", "\n", "query = \"\"\"\n", "MATCH\n", " (m:Method)<-[:DECLARES]-(t:Type)-[:IS_A]->(existingTa:TechnicalAspect)\n", "MERGE (ta:TechnicalAspect)<-[:IS_A]-(m)\n", "SET\n", " existingTa.name = ta.name,\n", " ta.type = \"derived_from_class\"\n", "RETURN DISTINCT m.name, ta.name\n", "\"\"\"\n", "\n", "pd.DataFrame(graph.data(query)).head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Methods Annotations" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
m.namev.value
0findPetByIdDataModel
\n", "
" ], "text/plain": [ " m.name v.value\n", "0 findPetById DataModel" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "graph = py2neo.Graph()\n", "\n", "query = \"\"\"\n", "MATCH\n", " (a:Artifact { name: \"spring-petclinic\"})-[:CONTAINS*]->\n", " (t:Type)-[:DECLARES]->(m:Method)-[:ANNOTATED_BY]->(annotation:Annotation)-[:OF_TYPE]->\n", " (annotationType:Type { name: \"TechnicalAspect\"}),\n", " (annotation)-[:HAS]->(v:Value)\n", "WHERE t.name <> \"package-info\"\n", "MERGE (ta:TechnicalAspect)<-[:IS_A]-(m)\n", "SET \n", " ta.name = v.value, \n", " ta.type = \"method_annotation\",\n", " ta.direct = true\n", "RETURN DISTINCT m.name, v.value\n", "\"\"\"\n", "\n", "pd.DataFrame(graph.data(query)).head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }