├── .idea ├── artifacts │ └── microservices_comm_pattern_check_jar.xml ├── compiler.xml ├── encodings.xml ├── misc.xml └── vcs.xml ├── README.md ├── microservicescommpatterncheck.iml ├── pom.xml ├── resultGraphs ├── Spring-petclinic.png ├── consul-master.png ├── ecommerce-microservices.png ├── ftgo-application-master.png ├── microservice_sample.png ├── qbike.png └── springCloudExample.png └── src ├── main ├── java │ └── com │ │ └── imranur │ │ └── microservices │ │ └── comm │ │ └── pattern │ │ └── check │ │ ├── App.java │ │ ├── Models │ │ ├── DockerServices.java │ │ ├── ServiceInOutDegClass.java │ │ ├── ServiceInterDependency.java │ │ └── Services.java │ │ └── Utils │ │ ├── DBUtilService.java │ │ └── DockerComposeUtils.java └── resources │ └── META-INF │ └── MANIFEST.MF └── test └── java └── com └── imranur └── microservices └── comm └── pattern └── check └── AppTest.java /.idea/artifacts/microservices_comm_pattern_check_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/microservices_comm_pattern_check_jar 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Microservice Dependency Graph (MicroDepGraph) 2 | 3 | MicroDepGraph analyzes the service dependecies of microservices projects based on docker configuration. It produces output as neo4j graph database and also image of dependency graph as SVG format. It analyzes a project which is in local drive. It will create two folders one is "neo4jData" and another one is "output" which holds the generated image of dependency graph. 4 | 5 | # Requirements 6 | 7 | Java jdk8 or higher. 8 | 9 | # How to use it 10 | 11 | * clone a git repository containing a java project developed with a micorservice architectural style. 12 | * execute MicroDepGraph as: java -jar microservices-dependency-check.jar 13 | 14 | An example command to run the tool from command line is, 15 | java -jar microservices-dependency-check.jar /home/myuser/ftgo-application-master ftgo-application-master 16 | 17 | 18 | 19 | # Outputs 20 | 21 | After analyzing the project, MicroDepGraph generates dependency graph in three types of export files which are, 22 | 23 | 1. Neo4j database containing output graph 24 | 2. GraphML file a common format for exchanging graph structure data 25 | 3. An SVG file 26 | 27 | ![Example Output](https://raw.githubusercontent.com/clowee/MicroDepGraph/master/resultGraphs/ftgo-application-master.png) 28 | Figure 1. Example MicroDepGraph Output (project spinnaker http://bit.ly/2YQA2S7) 29 | 30 | # List of projects the tool has been currently tested on 31 | 32 | A dataset with different projects analyzed is available on the MicroserviceDataset repository [view](https://github.com/clowee/MicroserviceDataset) 33 | 34 | ## How to cite 35 | 36 | Please, cite as "The Microservice Dataset, Version 1.0 [1]" 37 | 38 | [1] Mohammad Imranur Rahman, Sebastiano Panichella, Davide Taibi. A curated Dataset of Microservices-Based Systems. Joint Proceedings of the Summer School on Software Maintenance and Evolution. Tampere, 2019 39 | ``` 40 | @INPROCEEDINGS{Rahman2019, 41 | author = {Rahman, MI.and Taibi, Davide}, 42 | title = {A curated Dataset of Microservices-Based Systems}, 43 | booktitle={Joint Proceedings of the Summer School on Software Maintenance and Evolution}, 44 | year={2019}, 45 | month={September}, 46 | publisher = {CEUR-WS} 47 | location = {Tampere, Finland} 48 | } 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /microservicescommpatterncheck.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.imranur 8 | microservices.comm.pattern.check 9 | 1.0-SNAPSHOT 10 | 11 | microservices-comm-pattern-check 12 | 13 | http://www.example.com 14 | 15 | 16 | UTF-8 17 | 1.7 18 | 1.7 19 | 20 | 21 | 22 | 23 | junit 24 | junit 25 | 4.11 26 | test 27 | 28 | 29 | org.yaml 30 | snakeyaml 31 | 1.23 32 | 33 | 34 | org.neo4j 35 | neo4j 36 | 3.5.7 37 | 38 | 39 | org.neo4j.driver 40 | neo4j-java-driver 41 | 2.0.0-alpha02 42 | 43 | 44 | guru.nidi 45 | graphviz-java 46 | 0.8.10 47 | 48 | 49 | org.apache.tinkerpop 50 | gremlin-core 51 | 3.4.2 52 | 53 | 54 | com.tinkerpop.gremlin 55 | gremlin-java 56 | 2.6.0 57 | 58 | 59 | 60 | com.fasterxml.jackson.dataformat 61 | jackson-dataformat-csv 62 | 2.9.9 63 | 64 | 65 | 66 | org.apache.commons 67 | commons-text 68 | 1.8 69 | 70 | 71 | 72 | 73 | 74 | 75 | ${project.basedir}/src/main/java 76 | 77 | 78 | 79 | 80 | maven-clean-plugin 81 | 3.1.0 82 | 83 | 84 | 85 | maven-resources-plugin 86 | 3.0.2 87 | 88 | 89 | maven-compiler-plugin 90 | 3.8.0 91 | 92 | 93 | maven-surefire-plugin 94 | 2.22.1 95 | 96 | 97 | maven-jar-plugin 98 | 3.0.2 99 | 100 | 101 | 102 | true 103 | libs/ 104 | 105 | org.baeldung.executable.ExecutableMavenJar 106 | 107 | 108 | 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-assembly-plugin 114 | 3.1.1 115 | 116 | 117 | 118 | jar-with-dependencies 119 | 120 | 121 | 122 | 123 | com.imranur.microservices.comm.pattern.check.App 124 | 125 | 126 | 127 | 128 | 129 | 130 | make-assembly 131 | 132 | package 133 | 134 | single 135 | 136 | 137 | 138 | 139 | 140 | maven-install-plugin 141 | 2.5.2 142 | 143 | 144 | maven-deploy-plugin 145 | 2.8.2 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-dependency-plugin 150 | 151 | 152 | copy-dependencies 153 | prepare-package 154 | 155 | copy-dependencies 156 | 157 | 158 | 159 | ${project.build.directory}/libs 160 | 161 | 162 | 163 | 164 | 165 | 166 | maven-site-plugin 167 | 3.7.1 168 | 169 | 170 | maven-project-info-reports-plugin 171 | 3.0.0 172 | 173 | 174 | 175 | 176 | 177 | org.apache.maven.plugins 178 | maven-compiler-plugin 179 | 180 | 8 181 | 8 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /resultGraphs/Spring-petclinic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/Spring-petclinic.png -------------------------------------------------------------------------------- /resultGraphs/consul-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/consul-master.png -------------------------------------------------------------------------------- /resultGraphs/ecommerce-microservices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/ecommerce-microservices.png -------------------------------------------------------------------------------- /resultGraphs/ftgo-application-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/ftgo-application-master.png -------------------------------------------------------------------------------- /resultGraphs/microservice_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/microservice_sample.png -------------------------------------------------------------------------------- /resultGraphs/qbike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/qbike.png -------------------------------------------------------------------------------- /resultGraphs/springCloudExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clowee/MicroDepGraph/c0c96272802a62dbeed9970e14565b05735e36a5/resultGraphs/springCloudExample.png -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/App.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.ObjectWriter; 5 | import com.fasterxml.jackson.dataformat.csv.CsvMapper; 6 | import com.fasterxml.jackson.dataformat.csv.CsvSchema; 7 | import com.imranur.microservices.comm.pattern.check.Models.DockerServices; 8 | import com.imranur.microservices.comm.pattern.check.Models.ServiceInterDependency; 9 | import com.imranur.microservices.comm.pattern.check.Models.ServiceInOutDegClass; 10 | import com.imranur.microservices.comm.pattern.check.Models.Services; 11 | import com.imranur.microservices.comm.pattern.check.Utils.DBUtilService; 12 | import com.imranur.microservices.comm.pattern.check.Utils.DockerComposeUtils; 13 | import org.neo4j.graphdb.GraphDatabaseService; 14 | import org.neo4j.graphdb.Transaction; 15 | import org.yaml.snakeyaml.Yaml; 16 | import org.yaml.snakeyaml.constructor.Constructor; 17 | import org.yaml.snakeyaml.representer.Representer; 18 | 19 | import java.io.*; 20 | import java.math.RoundingMode; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import java.nio.file.Paths; 24 | import java.text.DecimalFormat; 25 | import java.text.NumberFormat; 26 | import java.util.*; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import java.util.concurrent.atomic.AtomicReference; 29 | import java.util.stream.Collectors; 30 | import java.util.stream.Stream; 31 | 32 | /** 33 | * Microservices dependency/communication pattern checking 34 | */ 35 | public class App { 36 | public static void main(String[] args) throws IOException { 37 | 38 | String directory = args[0]; 39 | String dbName = args[1]; 40 | if (args[0].equals("")) { 41 | System.out.println("no file path given"); 42 | System.exit(0); 43 | } 44 | //Scanner scan = new Scanner(System.in); 45 | String fileName1 = "docker-compose.yml"; 46 | String fileName2 = "docker-compose.yaml"; 47 | //System.out.println("Enter project directory to search "); 48 | //String directory = scan.next(); 49 | //String directory = "/home/imran/Thesis_Projects/spring-cloud-microservice-example-master"; 50 | // /home/imran/Thesis_Projects/qbike-master 51 | List dockerFile1 = null; 52 | List dockerFile2 = null; 53 | 54 | Properties props = System.getProperties(); 55 | props.setProperty("javax.accessibility.assistive_technologies", ""); 56 | 57 | dockerFile1 = DockerComposeUtils.find(fileName1, directory); 58 | dockerFile2 = DockerComposeUtils.find(fileName2, directory); 59 | 60 | Representer representer = new Representer(); 61 | representer.getPropertyUtils().setSkipMissingProperties(true); 62 | Yaml yaml = new Yaml(new Constructor(DockerServices.class), representer); 63 | DockerServices dockerServices = null; 64 | 65 | if (!dockerFile1.isEmpty()) { 66 | InputStream inputStream = new FileInputStream(new File(dockerFile1.get(0).toString())); 67 | dockerServices = yaml.load(inputStream); 68 | } else if (!dockerFile2.isEmpty()) { 69 | InputStream inputStream = new FileInputStream(new File(dockerFile2.get(0).toString())); 70 | dockerServices = yaml.load(inputStream); 71 | } else { 72 | System.out.println("no docker files found"); 73 | System.exit(0); 74 | } 75 | 76 | ArrayList serviceLists = new ArrayList<>(); 77 | ArrayList>> serviceMappings = new ArrayList<>(); 78 | if (dockerServices.getServices() != null) { 79 | serviceLists = new ArrayList<>(dockerServices.getServices().keySet()); 80 | } else { 81 | System.out.println("Incompatible docker compose file"); 82 | } 83 | 84 | if (!serviceLists.isEmpty()) { 85 | serviceMappings = DockerComposeUtils.getDockerServiceMapping(dockerServices, serviceLists); 86 | } 87 | 88 | 89 | StringBuilder mapping = DockerComposeUtils.getFormattedOutput(serviceMappings); 90 | System.out.println(mapping.toString()); 91 | 92 | DockerComposeUtils.generateGraphImage(dbName, serviceMappings); 93 | 94 | GraphDatabaseService graphDb = DBUtilService.getGraphDatabaseService(dbName); 95 | Transaction transaction = graphDb.beginTx(); 96 | 97 | DBUtilService.saveNodesToEmbeddedDb(serviceMappings, graphDb); 98 | 99 | DBUtilService.makeRelsToEmbeddedDb(serviceMappings, graphDb); 100 | transaction.close(); 101 | graphDb.shutdown(); 102 | 103 | // FIXME: This snippet is for saving data to neo4j local db instance 104 | /* Driver driver = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "124")); 105 | try (Session session = driver.session()) { 106 | DockerComposeUtils.saveNodes(serviceMappings, session); 107 | DockerComposeUtils.makeRelations(serviceMappings, session); 108 | session.close(); 109 | driver.close(); 110 | }*/ 111 | 112 | DockerComposeUtils.generateGraphMl(dbName, serviceMappings); 113 | 114 | HashMap servicePaths = new HashMap<>(); 115 | for (String service : serviceLists) { 116 | Services services = dockerServices.getServices().get(service); 117 | String servicePath = null; 118 | if (services.getBuild() != null) { 119 | servicePath = services.getBuild().substring(services.getBuild().lastIndexOf('/') + 1); 120 | } else if (services.getImage() != null) { 121 | servicePath = services.getImage().substring(services.getImage().lastIndexOf('/') + 1); 122 | } 123 | servicePaths.put(service, servicePath); 124 | } 125 | 126 | servicePaths.size(); 127 | 128 | HashMap serviceNumberofClasses = new HashMap<>(); 129 | servicePaths.forEach((s, s2) -> { 130 | 131 | try (Stream files = Files.walk(Paths.get(directory))) { 132 | Optional stream = files.filter(f -> f.getFileName().toString().equals(s2)).findFirst(); 133 | System.out.println(stream); 134 | 135 | if (stream.isPresent()) { 136 | Stream stream1 = Files.find(stream.get(), 10, 137 | (path, attr) -> path.getFileName().toString().endsWith(".java")); 138 | int classCount = (int) stream1.count(); 139 | serviceNumberofClasses.put(s, classCount); 140 | } else { 141 | serviceNumberofClasses.put(s, 0); 142 | } 143 | } catch (IOException e) { 144 | e.printStackTrace(); 145 | } 146 | }); 147 | calculateAvgSc(dbName, serviceMappings, serviceNumberofClasses); 148 | serviceNumberofClasses.size(); 149 | } 150 | 151 | private static void calculateAvgSc(String dbName, ArrayList>> serviceMappings, HashMap serviceNumberofClasses) throws IOException { 152 | HashMap serviceDepNumbers = new HashMap<>(); 153 | ArrayList serviceInterDependencies = new ArrayList<>(); 154 | serviceMappings.forEach(stringSetMap -> { 155 | String a = stringSetMap.keySet().toString().replace("[", "").replace("]", ""); 156 | stringSetMap.values().forEach(strings -> { 157 | String b = strings.toString().replace("[", "").replace("]", ""); 158 | String mapping = a + "-" + b; 159 | serviceDepNumbers.put(mapping, 1); 160 | 161 | ServiceInterDependency siy = new ServiceInterDependency(); 162 | siy.setFrom(a); 163 | siy.setTo(b); 164 | siy.setConnected(true); 165 | serviceInterDependencies.add(siy); 166 | System.out.println("SIY values " + siy.getFrom() + siy.getTo() + siy.isConnected()); 167 | }); 168 | }); 169 | serviceDepNumbers.size(); 170 | 171 | if (serviceInterDependencies.size() > 0) { 172 | serviceInterDependencies.forEach(serviceInterDependency -> { 173 | serviceInterDependencies.forEach(serviceInterDependency1 -> { 174 | if (serviceInterDependency.getFrom().equals(serviceInterDependency1.getTo()) && serviceInterDependency.getTo().equals(serviceInterDependency1.getFrom())) { 175 | //System.out.println(serviceInterDependency.getFrom() + "," + serviceInterDependency.getTo()); 176 | } 177 | }); 178 | }); 179 | } 180 | 181 | 182 | 183 | ArrayList inOutDegClasses = new ArrayList<>(); 184 | serviceMappings.forEach(stringSetMap -> { 185 | ServiceInOutDegClass service = new ServiceInOutDegClass(); 186 | String serviceName = stringSetMap.keySet().toString().replace("[", "").replace("]", ""); 187 | service.setServiceName(serviceName); 188 | int inDeg = stringSetMap.values().size(); 189 | service.setOutDeg(inDeg); 190 | AtomicInteger outDeg = new AtomicInteger(); 191 | serviceMappings.forEach(serviceMap -> { 192 | serviceMap.values().forEach(strings -> { 193 | String b = strings.toString().replace("[", "").replace("]", ""); 194 | if (serviceName.equals(b)) { 195 | outDeg.getAndIncrement(); 196 | } 197 | }); 198 | }); 199 | service.setInDeg(outDeg.get()); 200 | service.setMaxDeg(inDeg + outDeg.get()); 201 | service.setNumberOfClasses(serviceNumberofClasses.get(serviceName)); 202 | inOutDegClasses.add(service); 203 | }); 204 | 205 | inOutDegClasses.size(); 206 | Optional max = inOutDegClasses.stream().max(Comparator.comparing(ServiceInOutDegClass::getMaxDeg)); 207 | System.out.println("Deg max :" + max.get().getMaxDeg()); 208 | 209 | double sumDeg = inOutDegClasses.stream().mapToDouble(ServiceInOutDegClass::getMaxDeg).sum(); 210 | double avgDeg = sumDeg / inOutDegClasses.size(); 211 | 212 | System.out.println("Deg avg :" + avgDeg); 213 | List maxDegList = inOutDegClasses.stream().map(ServiceInOutDegClass::getMaxDeg).collect(Collectors.toList()); 214 | 215 | double medianDeg = median(maxDegList); 216 | System.out.println("Deg median :" + medianDeg); 217 | 218 | double degStandardDev = calculateSD(maxDegList); 219 | System.out.println("Deg Standard deviation :" + degStandardDev); 220 | 221 | 222 | 223 | // initialize and configure the mapper 224 | CsvMapper mapper = new CsvMapper(); 225 | // we ignore unknown fields or fields not specified in schema, otherwise 226 | // writing will fail 227 | mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true); 228 | 229 | // initialize the schema 230 | CsvSchema schema = CsvSchema.builder().addColumn("serviceName") 231 | .addColumn("outDeg").addColumn("inDeg").addColumn("maxDeg").addColumn("numberOfClasses").setUseHeader(true).build(); 232 | 233 | // map the bean with our schema for the writer 234 | ObjectWriter writer = mapper.writerFor(ServiceInOutDegClass.class).with(schema); 235 | 236 | File tempFile = new File(dbName + "/output.csv"); 237 | // we write the list of objects 238 | writer.writeValues(tempFile).writeAll(inOutDegClasses); 239 | 240 | HashMap CBM = new HashMap<>(); 241 | 242 | inOutDegClasses.forEach(service -> { 243 | String serviceName = service.getServiceName(); 244 | int outDeg = service.getOutDeg(); 245 | int classes = service.getNumberOfClasses(); 246 | String cbm = serviceName; 247 | if (classes != 0) { 248 | double cbmValue = (double) outDeg / (double) classes; 249 | CBM.put(cbm, String.valueOf(cbmValue)); 250 | } else { 251 | CBM.put(cbm, "N/A"); 252 | } 253 | }); 254 | 255 | String eol = System.getProperty("line.separator"); 256 | try (Writer cbmWriter = new FileWriter(dbName + "/CBM.csv")) { 257 | cbmWriter.append("serviceName").append(',').append("value").append(eol); 258 | for (Map.Entry entry : CBM.entrySet()) { 259 | cbmWriter.append(entry.getKey()) 260 | .append(',') 261 | .append(entry.getValue()) 262 | .append(eol); 263 | } 264 | } catch (IOException ex) { 265 | ex.printStackTrace(System.err); 266 | } 267 | 268 | CBM.size(); 269 | 270 | /* serviceMappings.forEach(stringSetMap -> { 271 | String serviceA = stringSetMap.keySet().toString().replace("[", "").replace("]", ""); 272 | serviceMappings.forEach(serviceB -> { 273 | String service_B = serviceB.keySet().toString().replace("[", "").replace("]", ""); 274 | if(!serviceA.equals(service_B)){ 275 | System.out.println(serviceA + "->" +service_B); 276 | AtomicInteger count = new AtomicInteger(); 277 | stringSetMap.values().forEach(strings -> { 278 | if(serviceA.equals(strings.toString().replace("[", "").replace("]", ""))){ 279 | count.getAndIncrement(); 280 | } 281 | }); 282 | System.out.println(count.get()); 283 | } 284 | }); 285 | }); 286 | */ 287 | HashMap interServiceMap = new HashMap<>(); 288 | serviceMappings.forEach(stringSetMap -> { 289 | String serviceA = stringSetMap.keySet().toString().replace("[", "").replace("]", ""); 290 | 291 | stringSetMap.values().forEach(strings -> { 292 | strings.forEach(s -> { 293 | //String serviceB = strings.toString().replace("[", "").replace("]", ""); 294 | interServiceMap.put(serviceA + "->" + s, 1); 295 | }); 296 | }); 297 | }); 298 | 299 | interServiceMap.size(); 300 | HashMap interService = new HashMap<>(); 301 | for (Map> entry : serviceMappings) { 302 | String serviceA = entry.keySet().toString().replace("[", "").replace("]", ""); 303 | for (Map> entry1 : serviceMappings) { 304 | String serviceB = entry1.keySet().toString().replace("[", "").replace("]", ""); 305 | if (!serviceA.equals(serviceB)) { 306 | String service = serviceA + "->" + serviceB; 307 | if (interServiceMap.get(service) != null) { 308 | //System.out.println(service + "-" + 1); 309 | interService.put(service, 1); 310 | } else { 311 | //System.out.println(service + "-" + 0); 312 | interService.put(service, 0); 313 | } 314 | } 315 | } 316 | } 317 | AtomicInteger maxDeg = new AtomicInteger(); 318 | 319 | serviceMappings.forEach(stringSetMap -> { 320 | AtomicInteger size = new AtomicInteger(); 321 | stringSetMap.values().forEach(strings -> { 322 | strings.forEach(s -> { 323 | size.getAndIncrement(); 324 | }); 325 | }); 326 | if(maxDeg.get() < size.get()){ 327 | maxDeg.set(size.get()); 328 | } 329 | }); 330 | 331 | HashMap scService = new HashMap(); 332 | if (!interService.isEmpty()) { 333 | interService.forEach((s, integer) -> { 334 | String[] outService = s.split("->"); 335 | String service = outService[1] + "->" + outService[0]; 336 | Integer outValue = interService.get(service); 337 | if(scService.get(service.replace("->", ",")) == null) { 338 | double lwf = (double) (1 + outValue) / (double) (1 + outValue + integer); 339 | double gwf = (double) (outValue + integer) / (double) maxDeg.get(); 340 | System.out.println("GWF " + service + gwf); 341 | System.out.println("LWF " + service + lwf); 342 | double deg = outValue + integer; 343 | double SC = 1 - (1 / deg) * lwf * gwf; 344 | //System.out.println(s + "-" + SC); 345 | if(!Double.isNaN(SC)) { 346 | scService.put(s.replace("->", ","), SC); 347 | } 348 | } 349 | }); 350 | } 351 | 352 | try (Writer cbmWriter = new FileWriter(dbName + "/SC.csv")) { 353 | cbmWriter.append("SC").append(',').append("value").append(eol); 354 | for (Map.Entry entry : scService.entrySet()) { 355 | cbmWriter.append("SC("+entry.getKey()+")") 356 | .append(',') 357 | .append(String.valueOf(entry.getValue())) 358 | .append(eol); 359 | } 360 | } catch (IOException ex) { 361 | ex.printStackTrace(System.err); 362 | } 363 | 364 | AtomicReference scTotal = new AtomicReference<>((double) 0); 365 | scService.forEach((s, aDouble) -> scTotal.updateAndGet(v -> (double) (v + aDouble))); 366 | System.out.println("SC total :" + scTotal); 367 | 368 | Double scMax = Collections.max(scService.values()); 369 | System.out.println("SC max :" + scMax); 370 | 371 | double scAvg = scTotal.get() / (double) scService.size(); 372 | System.out.println("SC Average : " + scAvg); 373 | 374 | ArrayList scValues = new ArrayList<>(scService.values()); 375 | 376 | double scMedian = medianDouble(scValues); 377 | System.out.println("SC median " + scMedian); 378 | 379 | double scStandardDeviation = calculateSDDouble(scValues); 380 | System.out.println("SC Standard Deviation : " + scStandardDeviation); 381 | 382 | AtomicReference cbmMax = new AtomicReference<>((double) 0); 383 | AtomicReference cbmTotal = new AtomicReference<>((double) 0); 384 | ArrayList cbmValues = new ArrayList<>(); 385 | CBM.forEach((s, value) -> { 386 | if(value != null && !value.equals("N/A")){ 387 | Double val = Double.valueOf(value); 388 | if(val> cbmMax.get()){ 389 | cbmMax.set(val); 390 | } 391 | cbmValues.add(val); 392 | cbmTotal.set(cbmTotal.get() + val); 393 | } 394 | }); 395 | 396 | System.out.println("CBM Total : " + cbmTotal); 397 | System.out.println("CBM Max : " + cbmMax); 398 | 399 | double cbmAverage = cbmTotal.get() / (double) CBM.size(); 400 | System.out.println("CBM Average : " + cbmAverage); 401 | double cbmMedian = medianDouble(cbmValues); 402 | System.out.println("CBM Median : " + cbmMedian); 403 | 404 | double cbmStadDev = calculateSDDouble(cbmValues); 405 | System.out.println("CBM Standard Deviation : " + cbmStadDev); 406 | 407 | NumberFormat formatter = new DecimalFormat("#0.00"); 408 | 409 | System.out.println(maxDeg+" "+formatter.format(avgDeg)+" "+formatter.format(medianDeg)+" "+formatter.format(degStandardDev)+" "+formatter.format(scTotal.get())+" "+formatter.format(scMax)+" "+formatter.format(scAvg)+" "+formatter.format(scMedian)+" "+formatter.format(scStandardDeviation)+" "+formatter.format(cbmTotal.get())+" "+formatter.format(cbmMax.get())+" "+formatter.format(cbmAverage)+" "+formatter.format(cbmMedian)+" "+formatter.format(cbmStadDev)); 410 | } 411 | 412 | public static double calculateSD(List numArray) 413 | { 414 | double sum = 0.0, standardDeviation = 0.0; 415 | int length = numArray.size(); 416 | for(double num : numArray) { 417 | sum += num; 418 | } 419 | double mean = sum/length; 420 | for(double num: numArray) { 421 | standardDeviation += Math.pow(num - mean, 2); 422 | } 423 | return Math.sqrt(standardDeviation/length); 424 | } 425 | 426 | public static double calculateSDDouble(List numArray) 427 | { 428 | double sum = 0.0, standardDeviation = 0.0; 429 | int length = numArray.size(); 430 | for(double num : numArray) { 431 | sum += num; 432 | } 433 | double mean = sum/length; 434 | for(double num: numArray) { 435 | standardDeviation += Math.pow(num - mean, 2); 436 | } 437 | return Math.sqrt(standardDeviation/length); 438 | } 439 | 440 | static double median(List values) { 441 | // sort array 442 | Collections.sort(values); 443 | double median; 444 | // get count of scores 445 | int totalElements = values.size(); 446 | // check if total number of scores is even 447 | if (totalElements % 2 == 0) { 448 | int sumOfMiddleElements = values.get(totalElements / 2) + values.get(totalElements / 2 - 1); 449 | // calculate average of middle elements 450 | median = ((double) sumOfMiddleElements) / 2; 451 | } else { 452 | // get the middle element 453 | median = (double) values.get(values.size() / 2); 454 | } 455 | return median; 456 | } 457 | 458 | static double medianDouble(List values) { 459 | // sort array 460 | Collections.sort(values); 461 | double median; 462 | // get count of scores 463 | int totalElements = values.size(); 464 | // check if total number of scores is even 465 | if (totalElements % 2 == 0) { 466 | double sumOfMiddleElements = 0; 467 | try { 468 | sumOfMiddleElements = values.get(totalElements / 2) + values.get(totalElements / 2 - 1); 469 | } catch (IndexOutOfBoundsException e) { 470 | System.err.println("IndexOutOfBoundsException: " + e.getMessage()); 471 | } 472 | // calculate average of middle elements 473 | median = ((double) sumOfMiddleElements) / 2; 474 | } else { 475 | // get the middle element 476 | median = (double) values.get(values.size() / 2); 477 | } 478 | return median; 479 | } 480 | } -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/Models/DockerServices.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check.Models; 2 | 3 | import java.util.Map; 4 | 5 | public class DockerServices { 6 | private String version; 7 | private Map services; 8 | 9 | public String getVersion() { 10 | return version; 11 | } 12 | 13 | public void setVersion(String version) { 14 | this.version = version; 15 | } 16 | 17 | public Map getServices() { 18 | return services; 19 | } 20 | 21 | public void setServices(Map services) { 22 | this.services = services; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/Models/ServiceInOutDegClass.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check.Models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 5 | 6 | @JsonPropertyOrder(value = { "serviceName", "outDeg","inDeg" , "maxDeg", "numberOfClasses"}) 7 | public class ServiceInOutDegClass { 8 | 9 | @JsonProperty("serviceName") 10 | private String serviceName; 11 | @JsonProperty("outDeg") 12 | private int outDeg; 13 | @JsonProperty("inDeg") 14 | private int inDeg; 15 | @JsonProperty("maxDeg") 16 | private int maxDeg; 17 | @JsonProperty("numberOfClasses") 18 | private int numberOfClasses; 19 | 20 | 21 | public String getServiceName() { 22 | return serviceName; 23 | } 24 | 25 | public void setServiceName(String serviceName) { 26 | this.serviceName = serviceName; 27 | } 28 | 29 | public int getOutDeg() { 30 | return outDeg; 31 | } 32 | 33 | public void setOutDeg(int outDeg) { 34 | this.outDeg = outDeg; 35 | } 36 | 37 | public int getInDeg() { 38 | return inDeg; 39 | } 40 | 41 | public void setInDeg(int inDeg) { 42 | this.inDeg = inDeg; 43 | } 44 | 45 | public int getMaxDeg() { 46 | return maxDeg; 47 | } 48 | 49 | public void setMaxDeg(int maxDeg) { 50 | this.maxDeg = maxDeg; 51 | } 52 | 53 | public int getNumberOfClasses() { 54 | return numberOfClasses; 55 | } 56 | 57 | public void setNumberOfClasses(int numberOfClasses) { 58 | this.numberOfClasses = numberOfClasses; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/Models/ServiceInterDependency.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check.Models; 2 | 3 | public class ServiceInterDependency { 4 | 5 | private String from; 6 | private String to; 7 | private boolean isConnected; 8 | 9 | public String getFrom() { 10 | return from; 11 | } 12 | 13 | public void setFrom(String from) { 14 | this.from = from; 15 | } 16 | 17 | public String getTo() { 18 | return to; 19 | } 20 | 21 | public void setTo(String to) { 22 | this.to = to; 23 | } 24 | 25 | public boolean isConnected() { 26 | return isConnected; 27 | } 28 | 29 | public void setConnected(boolean connected) { 30 | isConnected = connected; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/Models/Services.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check.Models; 2 | 3 | import java.util.List; 4 | 5 | public class Services { 6 | 7 | private String build; 8 | private String image; 9 | private List links; 10 | private List depends_on; 11 | 12 | public String getBuild() { 13 | return build; 14 | } 15 | 16 | public void setBuild(String build) { 17 | this.build = build; 18 | } 19 | 20 | public String getImage() { 21 | return image; 22 | } 23 | 24 | public void setImage(String image) { 25 | this.image = image; 26 | } 27 | 28 | public List getLinks() { 29 | return links; 30 | } 31 | 32 | public void setLinks(List links) { 33 | this.links = links; 34 | } 35 | 36 | public List getDepends_on() { 37 | return depends_on; 38 | } 39 | 40 | public void setDepends_on(List depends_on) { 41 | this.depends_on = depends_on; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/Utils/DBUtilService.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check.Utils; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 5 | 6 | import java.io.File; 7 | import java.util.ArrayList; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | public class DBUtilService { 12 | public static GraphDatabaseService getGraphDatabaseService(String dbName) { 13 | GraphDatabaseFactory graphDbFactory = new GraphDatabaseFactory(); 14 | 15 | if (dbName.equals("")) { 16 | dbName = "sampleProject"; 17 | System.out.println("no project name entered, default project name is" + dbName); 18 | } 19 | return graphDbFactory.newEmbeddedDatabase( 20 | new File("neo4jData/" + dbName)); 21 | } 22 | 23 | public static void saveNodesToEmbeddedDb(ArrayList>> serviceMappings, GraphDatabaseService graphDb) { 24 | for (Map> entry : serviceMappings) { 25 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 26 | graphDb.execute("CREATE (a:Service {name:" + "\"" + service + "\"" + "})"); 27 | } 28 | } 29 | 30 | public static void makeRelsToEmbeddedDb(ArrayList>> serviceMappings, GraphDatabaseService graphDb) { 31 | for (Map> entry : serviceMappings) { 32 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 33 | Set strings = entry.get(service); 34 | strings.forEach(s -> { 35 | graphDb.execute("MATCH (a:Service {name:" + "\"" + service + "\"" + "})," + 36 | "(b:Service {name:" + "\"" + s + "\"" + "})" + 37 | "CREATE (a) - [r:depends]-> (b)"); 38 | }); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/imranur/microservices/comm/pattern/check/Utils/DockerComposeUtils.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check.Utils; 2 | 3 | import com.imranur.microservices.comm.pattern.check.Models.DockerServices; 4 | import com.tinkerpop.blueprints.Graph; 5 | import com.tinkerpop.blueprints.impls.tg.TinkerGraph; 6 | import com.tinkerpop.blueprints.util.io.graphml.GraphMLWriter; 7 | import guru.nidi.graphviz.engine.Format; 8 | import guru.nidi.graphviz.engine.Graphviz; 9 | import guru.nidi.graphviz.model.MutableGraph; 10 | import guru.nidi.graphviz.parse.Parser; 11 | import org.neo4j.driver.Session; 12 | 13 | import java.io.*; 14 | import java.nio.file.*; 15 | import java.util.*; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | public class DockerComposeUtils { 20 | 21 | /** 22 | * TODO: Add javadoc 23 | * 24 | * @param dockerServices 25 | * @param serviceLists 26 | * @return 27 | */ 28 | public static ArrayList>> getDockerServiceMapping(DockerServices dockerServices, ArrayList serviceLists) { 29 | ArrayList>> serviceMappings = new ArrayList<>(); 30 | for (String entry : serviceLists) { 31 | dockerServices.getServices().forEach((s, services) -> { 32 | if (entry.equals(s)) { 33 | Map> service = new HashMap<>(); 34 | Set dependencies = new HashSet<>(); 35 | if (services != null && services.getLinks() != null && !services.getLinks().isEmpty()) { 36 | dependencies.addAll(services.getLinks()); 37 | } 38 | if (services != null && services.getDepends_on() != null && !services.getDepends_on().isEmpty()) { 39 | dependencies.addAll(services.getDepends_on()); 40 | } 41 | service.put(entry, dependencies); 42 | serviceMappings.add(service); 43 | } 44 | }); 45 | } 46 | return serviceMappings; 47 | } 48 | 49 | /** 50 | * TODO: Add javadoc 51 | * 52 | * @param fileName 53 | * @param searchDirectory 54 | * @return 55 | * @throws IOException 56 | */ 57 | public static List find(String fileName, String searchDirectory) throws IOException { 58 | try (Stream files = Files.walk(Paths.get(searchDirectory))) { 59 | return files 60 | .filter(f -> f.getFileName().toString().equals(fileName)) 61 | .collect(Collectors.toList()); 62 | } 63 | } 64 | 65 | /** 66 | * TODO: Add javadoc 67 | * 68 | * @param serviceMappings 69 | * @return 70 | */ 71 | public static StringBuilder getFormattedOutput(ArrayList>> serviceMappings) { 72 | StringBuilder mapping = new StringBuilder(); 73 | for (Map> entry : serviceMappings) { 74 | mapping.append(entry.keySet().toString().replace("[", "").replace("]", "")); 75 | mapping.append(" --> "); 76 | entry.values().forEach(mapping::append); 77 | mapping.append("\n"); 78 | } 79 | return mapping; 80 | } 81 | 82 | /** 83 | * TODO: Add javadoc 84 | * 85 | * @param serviceMappings 86 | * @param session 87 | */ 88 | public static void makeRelations(ArrayList>> serviceMappings, Session session) { 89 | for (Map> entry : serviceMappings) { 90 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 91 | Set strings = entry.get(service); 92 | strings.forEach(s -> { 93 | session.run("MATCH (a:Service {name:" + "\"" + service + "\"" + "})," + 94 | "(b:Service {name:" + "\"" + s + "\"" + "})" + 95 | "CREATE (a) - [r:depends]-> (b)"); 96 | }); 97 | 98 | } 99 | } 100 | 101 | /** 102 | * TODO: Add javadoc 103 | * 104 | * @param serviceMappings 105 | * @param session 106 | */ 107 | public static void saveNodes(ArrayList>> serviceMappings, Session session) { 108 | for (Map> entry : serviceMappings) { 109 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 110 | session.run("CREATE (a:Service {name:" + "\"" + service + "\"" + "})"); 111 | } 112 | } 113 | 114 | public static void generateGraphImage(String dbName, ArrayList>> serviceMappings) throws IOException { 115 | File dotFile = new File(dbName + "//" + dbName + ".dot"); 116 | 117 | if (!dotFile.getParentFile().exists()) 118 | dotFile.getParentFile().mkdirs(); 119 | if (!dotFile.exists()) 120 | dotFile.createNewFile(); 121 | 122 | FileWriter fileWriter = new FileWriter(dotFile); 123 | PrintWriter printWriter = new PrintWriter(fileWriter); 124 | printWriter.println("digraph {"); 125 | for (Map> entry : serviceMappings) { 126 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 127 | Set strings = entry.get(service); 128 | strings.forEach(s -> printWriter.println(service.replace("-", "_").replace(".", "_") + "->" + s.replace("-", "_").replace(".", "_") + "[color=blue]")); 129 | 130 | } 131 | printWriter.print("}"); 132 | printWriter.close(); 133 | // Generating a DOT(graph description language) file 134 | MutableGraph g = Parser.read(new FileInputStream(new File(dbName + "/" + dbName + ".dot"))); 135 | Graphviz.fromGraph(g).width(800).render(Format.SVG).toFile(new File(dbName + "/" + dbName + ".svg")); 136 | 137 | try { 138 | Files.deleteIfExists(Paths.get(dbName + "/" + dbName + ".dot")); 139 | } catch (NoSuchFileException e) { 140 | System.out.println("No such file/directory exists"); 141 | } catch (DirectoryNotEmptyException e) { 142 | System.out.println("Directory is not empty."); 143 | } catch (IOException e) { 144 | System.out.println("Invalid permissions."); 145 | } 146 | 147 | System.out.println("You can find graph image in output/" + dbName + ".svg"); 148 | } 149 | 150 | public static void generateGraphMl(String dbName, ArrayList>> serviceMappings) throws IOException { 151 | Graph graph = new TinkerGraph(); 152 | for (Map> entry : serviceMappings) { 153 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 154 | graph.addVertex(service); 155 | } 156 | for (Map> entry : serviceMappings) { 157 | String service = entry.keySet().toString().replace("[", "").replace("]", ""); 158 | Set strings = entry.get(service); 159 | strings.forEach(s -> { 160 | graph.addEdge(service + "->" + s, graph.getVertex(service), graph.getVertex(s), "depends").setProperty("edgelabel", "depends"); 161 | }); 162 | 163 | } 164 | OutputStream output = new FileOutputStream(dbName + "/" + dbName + ".graphml"); 165 | GraphMLWriter mlWriter = new GraphMLWriter(graph); 166 | mlWriter.setNormalize(true); 167 | mlWriter.outputGraph(graph, output); 168 | graph.shutdown(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.imranur.microservices.comm.pattern.check.App 3 | 4 | -------------------------------------------------------------------------------- /src/test/java/com/imranur/microservices/comm/pattern/check/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.imranur.microservices.comm.pattern.check; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | --------------------------------------------------------------------------------