Browse Source

SONAR-1832: Create an architecture rule engine

tags/2.6
Godin 13 years ago
parent
commit
d79015f074

+ 98
- 0
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/bytecode/check/ArchitectureCheck.java View File

@@ -0,0 +1,98 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2009 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.java.bytecode.check;

import org.apache.commons.lang.StringUtils;
import org.sonar.api.utils.WildcardPattern;
import org.sonar.check.Check;
import org.sonar.check.CheckProperty;
import org.sonar.check.IsoCategory;
import org.sonar.check.Priority;
import org.sonar.java.bytecode.asm.AsmClass;
import org.sonar.java.bytecode.asm.AsmEdge;
import org.sonar.squid.api.CheckMessage;
import org.sonar.squid.api.SourceCodeEdgeUsage;
import org.sonar.squid.api.SourceFile;

@Check(key = "Dependency", title = "Respect rule architecture", isoCategory = IsoCategory.Portability, priority = Priority.MINOR, description = "<p>Links between classes must respect defined architecture rules.</p>")
public class ArchitectureCheck extends BytecodeCheck {

@CheckProperty(title = "Pattern forbidden for from classes", key = "fromClasses")
private String fromClasses = new String();

@CheckProperty(title = "Pattern forbidden for to classes", key = "toClasses")
private String toClasses = new String();

public String getFromClasses() {
return fromClasses;
}

public void setFromClasses(String fromClasses) {
this.fromClasses = fromClasses;
}

public String getToClasses() {
return toClasses;
}

public void setToClasses(String toClasses) {
this.toClasses = toClasses;
}

private AsmClass asmClass;

@Override
public void visitClass(AsmClass asmClass) {
this.asmClass = asmClass;
}

@Override
public void visitEdge(AsmEdge edge) {
if (edge != null) {
SourceCodeEdgeUsage usage = edge.getUsage();
if (usage.equals(SourceCodeEdgeUsage.USES) || usage.equals(SourceCodeEdgeUsage.CALLS_METHOD)
|| usage.equals(SourceCodeEdgeUsage.CALLS_FIELD) || usage.equals(SourceCodeEdgeUsage.CONTAINS)) {
String internalNameTargetClass = edge.getTargetAsmClass().getInternalName();
String nameAsmClass = asmClass.getInternalName();
if (matchesPattern(nameAsmClass, fromClasses) && matchesPattern(internalNameTargetClass, toClasses)) {
SourceFile sourceFile = getSourceFile(asmClass);
CheckMessage message = new CheckMessage(this, nameAsmClass + " shouldn't directly use " + internalNameTargetClass);
message.setLine(edge.getSourceLineNumber());
sourceFile.log(message);
}
}
}
}

private boolean matchesPattern(String className, String pattern) {
if (StringUtils.isEmpty(pattern)) {
return true;
}
String[] patterns = pattern.split(",");
for (String p : patterns) {
p = StringUtils.replace(p, ".", "/");
if (WildcardPattern.create(p).match(className)) {
return true;
}
}
return false;
}

}

+ 2
- 1
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/bytecode/check/BytecodeChecks.java View File

@@ -31,6 +31,7 @@ public final class BytecodeChecks {
return Arrays.asList(
(Class) CallToDeprecatedMethodCheck.class,
UnusedPrivateMethodCheck.class,
UnusedProtectedMethodCheck.class);
UnusedProtectedMethodCheck.class,
ArchitectureCheck.class);
}
}

+ 89
- 0
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/bytecode/check/ArchitectureCheckTest.java View File

@@ -0,0 +1,89 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2009 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.java.bytecode.check;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.sonar.java.ast.SquidTestUtils.getFile;

import org.junit.Test;
import org.sonar.java.ast.JavaAstScanner;
import org.sonar.java.bytecode.BytecodeScanner;
import org.sonar.java.squid.JavaSquidConfiguration;
import org.sonar.squid.Squid;
import org.sonar.squid.api.CheckMessage;
import org.sonar.squid.api.SourceFile;

public class ArchitectureCheckTest {

private Squid squid;

@Test
public void testDependencyCheckOneErrorMessage() {
check("", "java.**.Pattern");

SourceFile file = (SourceFile) squid.search("ArchitectureCheckOneErrorMessage.java");
assertThat(file.getCheckMessages().size(), is(1));
CheckMessage message = file.getCheckMessages().iterator().next();
assertThat(message.getDefaultMessage(), is("ArchitectureCheckOneErrorMessage shouldn't directly use java/util/regex/Pattern"));
assertThat(message.getLine(), is(6));
}

@Test
public void testDependencyCheckDateForbidden() {
check("", "**.Date");

SourceFile file = (SourceFile) squid.search("ArchitectureCheckDateForbidden.java");
assertThat(file.getCheckMessages().size(), is(3));
// for (CheckMessage message : file.getCheckMessages()) {
// System.out.println(message.getDefaultMessage());
// }
}

@Test
public void testDependencyCheckToSqlFromUI() {
check("*UI", "java.sql.*");

SourceFile file = (SourceFile) squid.search("ArchitectureCheckToSqlFromUI.java");
assertThat(file.getCheckMessages().size(), is(7));
// for (CheckMessage message : file.getCheckMessages()) {
// System.out.println(message.getDefaultMessage() + " at line " + message.getLine());
// }
}

@Test
public void testDependencyCheckOKFromClassesToClasses() {
check("*SA", "java.sql.*");

SourceFile file = (SourceFile) squid.search("ArchitectureCheckToSqlFromUI.java");
assertThat(file.getCheckMessages().size(), is(0));
}

private void check(String fromClasses, String toClasses) {
ArchitectureCheck check = new ArchitectureCheck();
check.setFromClasses(fromClasses);
check.setToClasses(toClasses);

squid = new Squid(new JavaSquidConfiguration());
squid.register(JavaAstScanner.class).scanDirectory(getFile("/bytecode/architecture/src"));
squid.registerVisitor(check);
squid.register(BytecodeScanner.class).scanDirectory(getFile("/bytecode/architecture/bin"));
}
}

BIN
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/bin/ArchitectureCheckDateForbidden.class View File


BIN
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/bin/ArchitectureCheckOneErrorMessage.class View File


BIN
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/bin/ArchitectureCheckToSqlFromUI.class View File


+ 23
- 0
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/pom.xml View File

@@ -0,0 +1,23 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.sonar</groupId>
<version>0.1-SNAPSHOT</version>
<artifactId>sonar-bytecode</artifactId>
<packaging>jar</packaging>

<build>
<sourceDirectory>src</sourceDirectory>
<outputDirectory>bin</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<target>1.5</target>
<source>1.5</source>
</configuration>
</plugin>
</plugins>
</build>
</project>

+ 11
- 0
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/src/ArchitectureCheckDateForbidden.java View File

@@ -0,0 +1,11 @@
import java.sql.Date;
import java.util.Calendar;

public class ArchitectureCheckDateForbidden {

public ArchitectureCheckDateForbidden() {
Date dateSql = new Date(200000);
java.util.Date dateUtil = Calendar.getInstance().getTime();
long time = dateUtil.getTime();
}
}

+ 8
- 0
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/src/ArchitectureCheckOneErrorMessage.java View File

@@ -0,0 +1,8 @@
import java.util.regex.Pattern;

public class ArchitectureCheckOneErrorMessage {

public ArchitectureCheckOneErrorMessage() {
Pattern.compile("*.java");
}
}

+ 21
- 0
plugins/sonar-squid-java-plugin/test-resources/bytecode/architecture/src/ArchitectureCheckToSqlFromUI.java View File

@@ -0,0 +1,21 @@
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class ArchitectureCheckToSqlFromUI {

ResultSet result;

Connection connection;

public ArchitectureCheckToSqlFromUI(Statement statement, String requete) {
try {
connection = statement.getConnection();
result = statement.executeQuery(requete);
} catch (SQLException sql) {
sql.printStackTrace();
}
}
}

Loading…
Cancel
Save