jQA logo

The powerful Quality Assurance Tool for your Java application.

This document describes the concepts of jQAssistant and usage information.

Overview

jQAssistant is a QA tool which allows the definition and validation of project specific rules on a structural level. It is built upon the graph database Neo4j and can easily be plugged into the build process to automate detection of constraint violations and generate reports about user defined concepts and metrics.

Example use cases:

  • Enforce naming conventions, e.g. EJBs, JPA entities, test classes, packages, maven modules etc.

  • Validate dependencies between modules of your project

  • Separate API and implementation packages

  • Detect common problems like cyclic dependencies or tests without assertions

The rules are expressed in Cypher - the easy-to-learn query language of Neo4j:

MATCH
  (t:Test:Method)
WHERE NOT
  (t)-[:INVOKES]->(:Assert:Method)
RETURN
  t AS TestWithoutAssertion

License

jQAssistant is contributed under GNU General Public License, v3.

Quickstart

Command Line

Installation

  • Download and unpack the distribution, a directory ${project.artifactId}-${project.version} will be created.

Scan

Windows
bin/jqassistant.cmd scan -f lib
Linux
bin/jqassistant.sh scan -f lib
  • The JAR files contained in the lib/ folder will be scanned.

Explore
Windows
bin/jqassistant.cmd server
Linux
bin/jqassistant.sh server
  • Open a browser and navigate to http://localhost:7474

  • Enter the follwoing query in the top level area and hit Ctrl-Enter:

MATCH
  (t:Type)-[:DECLARES]->(m:Method)
RETURN
  t.fqn AS Type, count(t) AS DeclaredMethods
ORDER BY
  DeclaredMethods DESC
LIMIT 20

Maven

Add the plugin

Add the following lines to the parent pom.xml file of your project:

<build>
    <plugins>
        <plugin>
            <groupId>com.buschmais.jqassistant.scm</groupId>
            <artifactId>jqassistant-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>scan</id>
                    <goals>
                        <goal>scan</goal>
                    </goals>
                </execution>
                <execution>
                    <id>analyze</id>
                    <goals>
                        <goal>analyze</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
<build>

<reporting>
    <plugins>
        <plugin>
            <groupId>com.buschmais.jqassistant.scm</groupId>
            <artifactId>jqassistant-maven-plugin</artifactId>
            <reportSets>
                <reportSet>
                    <reports>
                        <report>report</report>
                    </reports>
                </reportSet>
            </reportSets>
        </plugin>
    </plugins>
</reporting>

Add a rule

Within your parent module create a directory "jqassistant" and a rules file in it:

<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <group id="default">
        <includeConstraint refId="my-rules:TestClassName" />
    </group>

    <constraint id="my-rules:TestClassName">
        <requiresConcept refId="junit4:TestClass" />
        <description>All JUnit test classes must have a name with suffix "Test".</description>
        <cypher><![CDATA[
            MATCH
                (t:Junit4:Test:Class)
            WHERE NOT
                t.name =~ ".*Test"
            RETURN
                t AS InvalidTestClass
        ]]></cypher>
    </constraint>

</jqa:jqassistant-rules>

Run the build

Execute the following command from your parent module:

mvn install

The build will fail with the message specified by your rule if it is violated. If everything is fine you can also create a report as part of your Maven site:

mvn site

Explore your application

jQAssistant comes with an integrated Neo4j server, you can run it using

mvn jqassistant:server
  • Open a browser and navigate to http://localhost:7474

  • Enter the follwoing query in the top level area and hit Ctrl-Enter:

MATCH
  (t:Type)-[:DECLARES]->(m:Method)
RETURN
  t.fqn AS Type, count(t) AS DeclaredMethods
ORDER BY
  DeclaredMethods DESC
LIMIT 20

Introduction

This chapter provides an introduction to the concepts of jQAssistant.

How it works

The basic idea behind jQAssistant is to integrate the following steps into the build process of a software system:

  1. Scan the generated artifacts and store structural information about them into a database

  2. Analyze the structures using rules which are represented by queries

  3. Report violations

jQAssistant itself is a plugin based framework. It comes with a pre-defined set of plugins containing scanners, rules and reports but can be easily extended by custom rules or implementations.

As database an embedded instance of Neo4j Community Edition is managed and used by jQAssistant. This means that no setup or configuration of a dedicated server is required. Neo4j has been chosen because:

  • it is a mature open source graph database

  • it allows easy modelling of structural elements of a software and their relations

  • it comes with a very expressive and easy to learn query language (Cypher)

Rules

Rules are expressed as Cypher queries and are specified in XML-files:

<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <group id="default">
        <includeConstraint refId="my-rules:MyConstraint"/>
    </group>

    <constraint id="my-rules:MyConcept">
        <description>A human readable description of the concept.</description>
        <cypher><![CDATA[
            MATCH
              ...
            WHERE
              ...
            SET
              ...
            CREATE
              ...
            RETURN
              ...
        ]]></cypher>
    </constraint>

    <constraint id="my-rules:MyConstraint">
        <requiresConcept refId="my-rules:MyConcept" />
        <description>A human readable description of the constraint.</description>
        <cypher><![CDATA[
            MATCH
                ...
            WHERE
                ...
            RETURN
                ...
        ]]></cypher>
    </constraint>

</jqa:jqassistant-rules>

Each rule comes with an unique id (e.g. "my-rules:MyConstraint") which can be referenced by other rules. jQAssistant will take care about executing the rules in the correct order. Furthermore a human readable description shall help developers to understand the rationale behind them.

A rule can optionally define the severity level. jQAssistant allows to break the build if there are violations in the configured severity level (or higher). For example, if the severity is set to critical, and if there are violated constraints with blocker and/or critical severity; the build will break. This feature allows projects to pay down their technical debt in an iterative manner.

Following severity levels are supported:

  • info

  • minor

  • major

  • critical

  • blocker

Groups

A group is a set of rules that shall be executed together. This allows to adjust analysis depth for different types of builds, e.g. a Continuous Integration build (CI) can be configured to only execute rules with low costs (i.e. execution times) whereas a report build is allowed to run for a longer time with more expensive checks.

Concepts

The information created by the scanner represents the structure of a software project on a raw level. Concept rules allow enriching the database with higher level information to ease the process of writing queries that check for violations (i.e. constraints) . This typically means adding labels, properties or relations.

jQAssistant comes with language and framework plugins which include general technical concepts, e.g.

  • "java:Entity" provided by the JPA2 plugin adds a label "Entity" to a node if it represents a class which is annotated by "@javax.persistence.Entity".

  • "java:MethodOverrides" provided by the Java plugin adds a relation "OVERRIDES" between a method of a sub class to the super class methods it overrides.

It is recommended to use concepts to enrich the database with information which is specific for the concrete project, e.g. labels can be added to

  • package nodes representing modules of the application ("Module")

  • package nodes that represent technical layers ("UI", "EJB")

  • class nodes representing elements with a specific role ("Controller", "Model")

NOTE Even if the primary intention of a concept is to enrich data it still must provide a return clause. If a concept returns an empty result a warning will be generated by jQAssistant. The rationale is that in such case the concept does not match the structure of the application and other rules which depend on it will probably not work as expected. The return clause of the concept shall preferably return a node/relation itself instead of an attribute of it. Similarly, return clauses with only count of matching nodes shall be avoided. With this, XML and HTML reports can provide additional information about the concept.

Constraints

A Constraint is a query which detects violations, e.g.

  • classes with specific roles (e.g. entity, controller, etc.) that are either located in the wrong packages or have names that do not fit defined conventions

  • invocations of methods which are deprecated and/or forbidden (e.g. constructors of java.util.Date)

  • dependencies to other modules which are not allowed

A constraint can depend on one or more concepts and usually is referenced by one or more groups.

NOTE If a constraint returns a result jQAssistant will report an error including the provided description and information about the returned elements. This information shall help the developer to understand and fix the problem.

Command Line

Shell scripts are provided for executing jQAssistant from the command line of Microsoft Windows® or Unix compatible systems. They are located in the bin/ directory of the distribution:

  • jqassistant.cmd

  • jqassistant.sh

The command line accepts tasks and their specific options:

jqassistant.sh <task1> <task2> <task3> -<option1> -<option2>

The following example will scan the content of the directories classes/ and test-classes/:

jqassistant.sh scan -f classes,test-classe

Tasks

scan

Description

Scans files or directories and stores the gathered information in database.

Options
  • -s, --storeDirectory <directory>

  • -f <file1>,<file2> or --files <file1>,<file2>

    • specifies a list of files or directories to scan

  • -u <url1>,<url2> or --urls <url1>,<url2>

    • specifies a list of URLs to scan

  • -reset

    • reset the database before scanning

analyze

Description

Executes an analysis.

Options

available-rules

Description

List all available rules.

effective-rules

Description

List the rules which would be executed for an analysis and the given concepts, constraints or groups.

report

Description

Transforms an XML report into HTML.

effective-rules

Description

Starts the integrated Neo4j web server (http://localhost:7474).

Common options

-s, --storeDirectory <directory>
  • specifies the location of the database to use

  • default: './jqassistant/store'

-concepts <concept1>,<concept2>
  • specifies the ids of the concepts to be applied

-constraints <constraint1>,<constraint2>
  • specifies the ids of the constraints to be validated

-groups <group1>,<group2>
  • specifies the ids of the groups to be executed

  • default: 'default'

-r, --ruleDirectory <directory>
  • specifies the directory where reports (XML, HTML) will be stored

  • default: './jqassistant/rules'

-reportDirectory
  • specifies the directory where reports (XML, HTML) will be stored

  • default: './jqassistant/report'

Maven Plugin

jQAssistant provides a plugin for Apache Maven which can be used to provide either fully automated scanning and analysis during the build process or manual execution from a command line.

Setup

Project Scope

Software projects often consist of several modules which are assembled to executable or deployable artifacts. In a Maven project these modules are usually organized hierarchically with a common parent module which is referenced directly or indirectly by all sub-modules. For each project jQAssistant uses a separate database with its own set of rules. Thus if a goal is executed within a Maven structure jQAssistant first determines the project scope, i.e. the root module, by searching within the tree starting from the current module following the parent relation until either a module is found where a directory "jqassistant/" exists or no parent is defined. The determined root module defines the location of

  • the set of rules to apply (from the directory "jqassistant/")

  • the database, default "{project.build.directory}/jqassistant/store"

  • the generated native report, default: "{project.build.directory}/jqassistant/jqassistant-report.xml")

  • and the generated HTML report, default "{project.build.directory}/site/jqassistant.html")

The following examples demonstrate different scenarios, the root modules as detected by jQAssistant are marked using asterisks.

Single project consisting of two modules
root*
   |-pom.xml
   |
   |-jqassistant
   |           |-rules.xml
   |
   |-module1
   |       |-pom.xml
   |
   |-module2
           |-pom.xml
Multiple projects, each consisting of two modules
root
   |-pom.xml
   |
   |-project1*
   |        |-jqassistant
   |        |           |-rules1.xml
   |        |
   |        |-pom.xml
   |        |-module1
   |        |       |-pom.xml
   |        |
   |        |-module2
   |                |-pom.xml
   |
   |-project2*
            |-jqassistant
            |           |-rules2.xml
            |-pom.xml
            |-module1
            |       |-pom.xml
            |
            |-module2
                    |-pom.xml

Plugin Configuration

The jQAssistant Maven plugin must be configured in the pom.xml of the root module, it should not be overwritten by sub-modules.

Setup of the jQAssistant Maven plugin
<project ...>
    ...
    <build>
        <plugins>
            <plugin>
            <groupId>com.buschmais.jqassistant.scm</groupId>
                <artifactId>jqassistant-maven-plugin</artifactId>
                <version>1.0.0-M4</version>
                <executions>
                    <execution>
                        <id>scan</id>
                        <goals>
                            <goal>scan</goal>
                        </goals>
                        <!--
                        <configuration>
                            <scanIncludes>
                                <scanInclude>
                                    <path>config</path>
                                </scanInclude>
                            </scanIncludes>
                            <scanProperties>
                                <customScanner.property>value</customScanner.property>
                            </scanProperties>
                        </configuration>
                        -->
                    </execution>
                    <execution>
                        <id>analyze</id>
                        <goals>
                            <goal>analyze</goal>
                        </goals>
                        <!--
                        <configuration>
                            <concepts>
                                <concept>junit4:TestClass</concept>
                            </concepts>
                            <constraints>
                                <constraint>junit4:TestMethodWithoutAssertion</constraints>
                            </constraints>
                            <groups>
                                <group>default</group>
                            </groups>
                            <failOnViolations>true</failOnViolations>
                            <severity>critical</severity>
                        </configuration>
                         -->
                    </execution>
                </executions>
                <!--
                <configuration>
                    <skip>false</skip>
                    <storeLifecycle>REACTOR</storeLifecycle>
                    <rulesDirectory>jqassistant</rulesDirectory>
                    <rulesDirectories>
                        <rulesDirectory>target/generated-rules</rulesDirectory>
                    </rulesDirectories>
                    <xmlReportFile>target/jqassistant/jqassistant-report.xml</xmlReportFile>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.buschmais.jqassistant.plugin</groupId>
                        <artifactId>jqassistant.plugin.jpa2</artifactId>
                        <version>1.0.0-M4</version>
                    </dependency>
                </dependencies>
                -->
            </plugin>
        </plugins>
    </build>

    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.buschmais.jqassistant.scm</groupId>
                <artifactId>jqassistant-maven-plugin</artifactId>
                <version>1.0.0-M4</version>
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>report</report>
                        </reports>
                    </reportSet>
                </reportSets>
                <!--
                <configuration>
                    <reportProperties>
                        <customReport.fileName>target/customReport.txt</customReport.fileName>
                    </reportProperties>
                </configuration>
                -->
            </plugin>
        </plugins>
    </reporting>
    ...
</project>

Command Line

Goals may also be executed from the command line:

mvn com.buschmais.jqassistant.scm:jqassistant-maven-plugin:available-rules

Adding the following lines to the file settings.xml (usually located in the $HOME/.m2) eases execution of jQAssistant goals from the command line:

<pluginGroups>
    <pluginGroup>com.buschmais.jqassistant.scm</pluginGroup>
</pluginGroups>

The same goal can now be executed using the following command line statement:

mvn jqassistant:available-rules

Goals

jqassistant:scan

Description

Scans the directories of compiled classes and test classes and stores the gathered information in database.

Configuration

jqassistant:reset

Description

Resets the database by deleting all nodes and relationships.

jqassistant:server

Description

Starts the integrated Neo4j web server (http://localhost:7474).

jqassistant:analyze

Description

Executes an analysis.

Configuration

jqassistant:effective-rules

Description

List the rules which would be executed for an analysis and the given concepts, constraints or groups.

jqassistant:available-rules

Description

List all available rules.

jqassistant:report

Description

Transforms the XML report into HTML (i.e. for generating a Maven site).

Common Configuration Properties

Execution

skip
  • skip execution of the plugin

  • default: 'false'

Store

storeDirectory (-Djqassistant.store.directory)
  • specifies the location of the database

  • default: '{rootModule}/target/jqassistant/store'

storeLifecycle (-Djqassistant.store.lifecycle)
  • specifies the lifecycle of the data store

    • 'REACTOR': cache the store for the execution time of the reactor for fast operations

    • 'MODULE': open and close the store for each module

  • default: 'REACTOR'

Analysis And Report

concepts (-Djqassistant.concepts)
  • specifies the ids of the concepts to be applied

constraints (-Djqassistant.constraints)
  • specifies the ids of the constraints to be validated

groups (-Djqassistant.groups)
  • specifies the ids of the groups to be executed

  • default: 'default'

rulesDirectory (-Djqassistant.rules.directory)
  • specifies the name of the directory which contains rules

  • this directory is also used to identify the root module of a project, see Project Scope

  • default: 'jqassistant'

rulesDirectories (-Djqassistant.rules.directories)
  • specifies a list of directory names relative to the root module containing additional rules

xmlReportFile (-Djqassistant.report.xml)
  • specifes the target file for writing the XML report

  • default: '{rootModule}/target/jqassistant/jqassistant-report.xml'

Examples

Rules

Forbidden Method

Demonstrates a constraint to define methods which must not be invoked by the application. The example denies usage of all constructors of java.util.Date.

jqassistant/default.xml
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <constraint id="example:ConstructorOfDateMustNotBeUsed">
        <description>Constructors of java.util.Date must not be used.</description>
        <cypher><![CDATA[
            MATCH
                (dateType:Type)-[:DECLARES]->(forbiddenMethod:Method),
                (type:Type)-[:DECLARES]->(method:Method)-[:INVOKES]->(forbiddenMethod)
            WHERE
                dateType.fqn = 'java.util.Date'
                AND forbiddenMethod:Constructor
            RETURN
                method AS Method
        ]]></cypher>
    </constraint>

    <group id="default">
        <includeConstraint refId="example:ConstructorOfDateMustNotBeUsed"/>
    </group>

</jqa:jqassistant-rules>

Naming

The following rules demonstrate concepts and constraints to enforce names and locations of classes with dedicated roles. These are identified either by an implemented interface or an annotation.

Interface
jqassistant/controller.xml
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <concept id="controller:ControllerClass">
        <description>Labels all classes implementing the Controller interface as "Controller".</description>
        <cypher><![CDATA[
            MATCH
                (controllerClass:Type:Class)-[:IMPLEMENTS*]->(controllerType:Type)
            WHERE
                controllerType.fqn = "com.buschmais.jqassistant.examples.rules.naming.Controller"
            SET
                controllerClass:Controller
            RETURN
                controllerClass
        ]]></cypher>
    </concept>

    <constraint id="controller:ClassNameMustHaveControllerSuffix">
        <requiresConcept refId="controller:ControllerClass"/>
        <description>All controller implementations must have a name suffix "Controller".</description>
        <cypher><![CDATA[
            MATCH
                (controllerClass:Class:Controller)
            WHERE
                NOT controllerClass.name =~ ".*Controller"
            RETURN
                controllerClass AS ControllerClass
        ]]></cypher>
    </constraint>

    <constraint id="controller:ClassesMustBeLocatedInControllerPackage">
        <requiresConcept refId="controller:ControllerClass"/>
        <description>All controller implementations must be located in the package "model".</description>
        <cypher><![CDATA[
            MATCH
                (package:PACKAGE)-[:CONTAINS]->(controllerClass:Class:Controller)
            WHERE
                NOT package.name = "controller"
            RETURN
                controllerClass AS ControllerClass, package AS InvalidPackage
        ]]></cypher>
    </constraint>

</jqa:jqassistant-rules>
Annotation
jqassistant/model.xml
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <concept id="model:ModelClass">
        <description>Labels all classes annotated with @Model as "Model".</description>
        <cypher><![CDATA[
            MATCH
                (modelClass:Type:Class)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(modelAnnotationType:Type)
            WHERE
                modelAnnotationType.fqn = "com.buschmais.jqassistant.examples.rules.naming.Model"
            SET
                modelClass:Model
            RETURN
                modelClass AS ModelClass
        ]]></cypher>
    </concept>

    <constraint id="model:ClassNameMustHaveModelSuffix">
        <requiresConcept refId="model:ModelClass"/>
        <description>All model classes must have a name suffix "Model".</description>
        <cypher><![CDATA[
            MATCH
                (modelClass:Class:Model)
            WHERE
                NOT modelClass.name =~ ".*Model"
            RETURN
                modelClass AS ModelClass
        ]]></cypher>
    </constraint>

    <constraint id="model:ClassesMustBeLocatedInModelPackage">
        <requiresConcept refId="model:ModelClass"/>
        <description>All model classes must be located in the package "model".</description>
        <cypher><![CDATA[
            MATCH
                (package:PACKAGE)-[:CONTAINS]->(modelClass:Class:Model)
            WHERE
                NOT package.name = "model"
            RETURN
                modelClass AS ModelClass, package AS InvalidPackage
        ]]></cypher>
    </constraint>

</jqa:jqassistant-rules>

Plugins

Custom Scanner Plugin

A custom scanner plugin allows extending the functionality of jQAssistant to read arbitrary structures (e.g. from files) and create the corresponding information in the database. The following example demonstrates this for CSV files.

The plugin is an artifact which contains the scanner implementation, model classes and a plugin descriptor. The project needs to declare the following dependencies:

pom.xml
        <dependency>
            <groupId>com.buschmais.jqassistant.core</groupId>
            <artifactId>jqassistant.core.scanner</artifactId>
            <version>1.0.0-M4</version>
        </dependency>
        <dependency>
            <groupId>com.buschmais.jqassistant.plugin</groupId>
            <artifactId>jqassistant.plugin.common</artifactId>
            <version>1.0.0-M4</version>
        </dependency>
        <dependency>
            <groupId>au.com.bytecode</groupId>
            <artifactId>opencsv</artifactId>
            <version>2.4</version>
        </dependency>
    </dependencies>

The artifact 'jqassistant.core.scanner' provides the scanner plugin API, 'jqassistant.plugin.common' contains common functionality shared by plugins, the library 'opencsv' is used to read CSV files.

The model to be stored in the database is defined using the approach provided by eXtended Objects. It is based on annotated interfaces declaring methods representing properties and relations.

First a label "CSV" is defined which is going to be used for all nodes created by the scanner:

com.buschmais.jqassistant.examples.plugins.scanner.model.CSVDescriptor
@Label("CSV")
public interface CSVDescriptor extends Descriptor {
}

A CSV file is represented by CSVFileDescriptor which inherits from CSVDescriptor and FileDescriptor (part of the scanner API). Thus a node of this type will carry the labels "CSV" and "File". Furthermore a list of "HAS_ROW" relations is defined by the property "rows".

com.buschmais.jqassistant.examples.plugins.scanner.model.CSVFileDescriptor
public interface CSVFileDescriptor extends CSVDescriptor, FileDescriptor {

    @Relation("HAS_ROW")
    List<CSVRowDescriptor> getRows();

}

A single row is a node defined by the type CSVRowDescriptor which inherits the label "CSV" from CSVDescriptor and provides its own label "Row", a property "lineNumber" and a list of "HAS_COLUMN" relations.

com.buschmais.jqassistant.examples.plugins.scanner.model.CSVRowDescriptor
@Label("Row")
public interface CSVRowDescriptor extends CSVDescriptor {

    int getLineNumber();

    void setLineNumber(int lineNumber);

    @Relation("HAS_COLUMN")
    List<CSVColumnDescriptor> getColumns();
}

CSVColumnDescriptor finally defines a column of a row following the principles explained above:

com.buschmais.jqassistant.examples.plugins.scanner.model.CSVColumnDescriptor
@Label("Column")
public interface CSVColumnDescriptor extends CSVDescriptor {

    String getValue();

    void setValue(String value);

    int getIndex();

    void setIndex(int index);

}

The implementation of the plugin itself inherits from AbstractScannerPlugin which requires generic type parameters for the item type it handles and the descriptor type it creates. In the example FileResource is used which represents a file contained in a directory or archive. This allows plugins to be independent of the source where files or directories are picked up by the scanner.

The method accepts is called by the scanner to determine if the plugin can handle the given item. The example matches the value of the parameter path against the file extension ".csv". The scope parameter may be used to further restrict executions of the plugin, e.g. by checking equality against JavaScope.CLASSPATH.

The scan method actually reads the CSV file and stores the gathered data into the database using the interface Store provided by the scanner context.

com.buschmais.jqassistant.examples.plugins.scanner.CSVFileScannerPlugin
public class CSVFileScannerPlugin extends AbstractScannerPlugin<FileResource, CSVFileDescriptor> {

    @Override
    public boolean accepts(FileResource item, String path, Scope scope) throws IOException {
        return path.toLowerCase().endsWith(".csv");
    }

    @Override
    public CSVFileDescriptor scan(FileResource item, String path, Scope scope, Scanner scanner) throws IOException {
        // Open the input stream for reading the file.
        try (InputStream stream = item.createStream()) {
            // Create the node for a CSV file.
            final Store store = scanner.getContext().getStore();
            final CSVFileDescriptor fileDescriptor = store.create(CSVFileDescriptor.class);
            // Parse the stream using OpenCSV.
            CSV csv = CSV.create();
            csv.read(stream, new CSVReadProc() {

                @Override
                public void procRow(int rowIndex, String... values) {
                    // Create the node for a row
                    CSVRowDescriptor rowDescriptor = store.create(CSVRowDescriptor.class);
                    fileDescriptor.getRows().add(rowDescriptor);
                    rowDescriptor.setLineNumber(rowIndex);
                    for (int i = 0; i < values.length; i++) {
                        // Create the node for a column
                        CSVColumnDescriptor columnDescriptor = store.create(CSVColumnDescriptor.class);
                        rowDescriptor.getColumns().add(columnDescriptor);
                        columnDescriptor.setIndex(i);
                        columnDescriptor.setValue(values[i]);
                    }
                }

            });
            return fileDescriptor;
        }
    }
}

Finally the model and the plugin implementation must be declared in the jQAssistant plugin descriptor:

/META-INF/jqassistant-plugin.xml
<jqa-plugin:jqassistant-plugin
        xmlns:jqa-plugin="http://www.buschmais.com/jqassistant/core/plugin/schema/v1.0">
    <description>Provides a scanner for CSV file.</description>
    <model>
        <class>com.buschmais.jqassistant.examples.plugins.scanner.model.CSVFileDescriptor</class>
        <class>com.buschmais.jqassistant.examples.plugins.scanner.model.CSVRowDescriptor</class>
        <class>com.buschmais.jqassistant.examples.plugins.scanner.model.CSVColumnDescriptor</class>
    </model>
    <scanner>
        <class>com.buschmais.jqassistant.examples.plugins.scanner.CSVFileScannerPlugin</class>
    </scanner>
</jqa-plugin:jqassistant-plugin>

The plugin is automatically loaded by the scanner if it can be found on the classpath, e.g. by adding it as dependency to the maven plugin.

Custom Rule Plugin

Rules (i.e. concepts, constraints or groups) can be shared between projects by packaging their rule descriptors in JAR artifacts as a classpath resources under /META-INF/jqassistant-rules/, e.g.

/META-INF/jqassistant-rules/common.xml
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <constraint id="common:TopLevelPackage">
        <description>All types defined in a project must use "com.buschmais" as top level package.</description>
        <cypher><![CDATA[
            MATCH
              (:Artifact)-[:CONTAINS]->(type:Type)
            WHERE
              NOT type.fqn =~ "com\\.buschmais\\..*"
            RETURN
              type AS TypeInWrongPackage
        ]]></cypher>
    </constraint>

</jqa:jqassistant-rules>

Furthermore these resources must be declared in the jQAssistant plugin descriptor /META-INF/jqassistant-plugin.xml:

/META-INF/jqassistant-plugin.xml
<jqa-plugin:jqassistant-plugin xmlns:jqa-plugin="http://www.buschmais.com/jqassistant/core/plugin/schema/v1.0">
    <description>Example plugin defining a set of custom rules.</description>
    <rules>
        <resource>common.xml</resource>
    </rules>
</jqa-plugin:jqassistant-plugin>

The plugin and its rules are automatically loaded by the analyzer if it can be found on the classpath, e.g. by adding it as dependency to the maven plugin.

Custom Report Plugin

A custom report plugin may be implemented to create custom representations of analysis results. Such a plugin must be provided as an artifact which contains a Java class and a plugin descriptor. Therefore a project is required which declares the required dependencies:

pom.xml
        <dependency>
            <groupId>com.buschmais.jqassistant.core</groupId>
            <artifactId>jqassistant.core.report</artifactId>
            <version>1.0.0-M4</version>
        </dependency>
        <dependency>
            <groupId>com.buschmais.jqassistant.plugin</groupId>
            <artifactId>jqassistant.plugin.java</artifactId>
            <version>1.0.0-M4</version>
        </dependency>
    </dependencies>

The artifact 'jqassistant.core.report' provides the report plugin API, 'jqassistant.plugin.java' provides interfaces representing Java types which might be used by the plugin implementation:

com.buschmais.jqassistant.examples.plugins.report.CustomReportPlugin
import com.buschmais.jqassistant.core.report.api.ReportPlugin;
import com.buschmais.jqassistant.plugin.java.api.model.TypeDescriptor;

public class CustomReportPlugin implements ReportPlugin {

    private static final String PROPERTY_FILENAME = "customReport.fileName";

    private String fileName;

    @Override
    public void initialize(Map<String, Object> properties) throws ReportException {
        this.fileName = (String) properties.get(PROPERTY_FILENAME);
        if (this.fileName == null) {
            throw new ReportException("Property " + PROPERTY_FILENAME + " is not specified.");
        }
    }

    @Override
    public void begin() throws ReportException {
    }

    @Override
    public void end() throws ReportException {
    }

    @Override
    public void beginConcept(Concept concept) throws ReportException {
    }

    @Override
    public void endConcept() throws ReportException {
    }

    @Override
    public void beginGroup(Group group) throws ReportException {
    }

    @Override
    public void endGroup() throws ReportException {
    }

    @Override
    public void beginConstraint(Constraint constraint) throws ReportException {
    }

    @Override
    public void endConstraint() throws ReportException {
    }

    @Override
    public void setResult(Result<? extends Rule> result) throws ReportException {
        Rule rule = result.getRule();
        if (rule instanceof Concept && "example:MethodsPerType".equals(rule.getId())) {
            try {
                PrintWriter writer = new PrintWriter(new FileWriter(fileName));
                writer.println("Methods per Type");
                writer.println("================");
                for (Map<String, Object> row : result.getRows()) {
                    TypeDescriptor type = (TypeDescriptor) row.get("Type");
                    Long methodCount = (Long) row.get("MethodCount");
                    writer.println(type.getFullQualifiedName() + ":" + methodCount);
                }
                writer.close();
            } catch (IOException e) {
                throw new ReportException("Cannot write custom report.", e);
            }
        }
    }
}

The plugin implementation must be declared in the jQAssistant plugin descriptor:

/META-INF/jqassistant-plugin.xml
<jqa-plugin:jqassistant-plugin xmlns:jqa-plugin="http://www.buschmais.com/jqassistant/core/plugin/schema/v1.0">
    <description>Example plugin defining a custom report.</description>
    <report>
        <class>com.buschmais.jqassistant.examples.plugins.report.CustomReportPlugin</class>
    </report>
</jqa-plugin:jqassistant-plugin>

The plugin is automatically loaded by the analyzer if it can be found on the classpath, e.g. by adding it as dependency to the maven plugin.

Maven Project Using Custom Plugins

Custom plugins containing rules, scanners or reporters must be specified as dependency of the Maven plugin. It is also possible to specify plugin specific properties:

pom.xml
        <plugins>
            <plugin>
                <groupId>com.buschmais.jqassistant.scm</groupId>
                <artifactId>jqassistant-maven-plugin</artifactId>
                <version>1.0.0-M4</version>
                <configuration>
                    <reportProperties>
                        <customReport.fileName>target/customReport.txt</customReport.fileName>
                    </reportProperties>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.buschmais.jqassistant.examples</groupId>
                        <artifactId>jqassistant.examples.plugins.scanner</artifactId>
                        <version>1.0.0-M4</version>
                    </dependency>
                    <dependency>
                        <groupId>com.buschmais.jqassistant.examples</groupId>
                        <artifactId>jqassistant.examples.plugins.rule</artifactId>
                        <version>1.0.0-M4</version>
                    </dependency>
                    <dependency>
                        <groupId>com.buschmais.jqassistant.examples</groupId>
                        <artifactId>jqassistant.examples.plugins.report</artifactId>
                        <version>1.0.0-M4</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

SonarQube

jQAssistant Plugin

Projects may retrieve jQAssistant rules from a SonarQube instance using a permalink. Furthermore a sensor is provided which will create issues on violated constraints.

The plugin is available from the standalone distribution in the folder "sonar/" and must be copied to the directory "extensions/plugins" of the SonarQube server. After startup a repository 'jQAssistant' is available in the view 'Coding rules' providing all rules which are distributed with jQAssistant.

New rules can be added or created in the following ways:

  • by creating new concepts and constraints from the pre-defined templates (i.e. "Concept Template" and "Constraint Template")

  • by deploying a custom rule extension (demonstrated by the example "Custom Rule Extension")

Next the following settings must be applied to the jQAssistant Maven plugin in the pom.xml of the project to be analyzed:

pom.xml
                <plugins>
                    <plugin>
                        <groupId>com.buschmais.jqassistant.scm</groupId>
                        <artifactId>jqassistant-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>scan</id>
                                <goals>
                                    <goal>scan</goal>
                                </goals>
                            </execution>
                            <execution>
                                <id>analyze</id>
                                <goals>
                                    <goal>analyze</goal>
                                </goals>
                                <configuration>
                                    <rulesUrl>http://localhost:9000/profiles/export?format=jqassistant&amp;language=java&amp;name=MyQualityProfile</rulesUrl>
                                    <groups>
                                        <group>MyQualityProfile</group>
                                    </groups>
                                    <failOnViolations>true</failOnViolations>
                                    <severity>critical</severity>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
  • Rules are retrieved from the SonarQube server using the URL specified in 'rulesUrl'. The value is the jQAssistant permalink available within the quality profile from the SonarQube UI. Note that special characters must be replaced by XML entities.

  • The specified 'group' must be the name of the quality profile used to analyze the project.

  • The build shall break if a violation is detected, therefore 'failOnViolations' is set to 'true'.

  • The build shall break only if violation is detected with severity equal to or higher than critical, therefore severity is set to critical

Custom Rule Extension

A custom rule extension can be implemented to make user defined jQAssistant rules available for SonarQube. It is a Maven project which declares packaging 'sonar-plugin':

pom.xml
<project ...>
...


    <properties>
        <org.codehaus.sonar_version>3.7.4</org.codehaus.sonar_version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>com.buschmais.jqassistant.scm</groupId>
                <artifactId>jqassistant-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.codehaus.sonar</groupId>
                <artifactId>sonar-packaging-maven-plugin</artifactId>
                <version>1.9</version>
                <extensions>true</extensions>
                <configuration>
                    <basePlugin>jqassistant</basePlugin>
                    <pluginClass>com.buschmais.jqassistant.examples.sonar.ruleextension.MyJQAssistantExtension</pluginClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>native2ascii-maven-plugin</artifactId>
                <version>1.0-beta-1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>native2ascii</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>com.buschmais.jqassistant.sonar</groupId>
            <artifactId>jqassistant.sonar.plugin</artifactId>
            <version>1.0.0-M4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.sonar</groupId>
            <artifactId>sonar-plugin-api</artifactId>
            <version>3.7.4</version>
        </dependency>
    </dependencies>


...
</project>

Notes on the sonar plugin configuration:

  • 'basePlugin' must be set to 'jQAssistant' to indicate which plugin shall be extended.

  • The implementation of 'pluginClass' (i.e. 'MyJQAssistantExtension') returns an empty list of extensions. It must be specified as this is required by SonarQube.

MyJQAssistantExtension.java
package com.buschmais.jqassistant.examples.sonar.ruleextension;

import java.util.Collections;
import java.util.List;

import org.sonar.api.SonarPlugin;

/**
 * Defines a sonar extension.
 */
public class MyJQAssistantExtension extends SonarPlugin {

    @SuppressWarnings("rawtypes")
    @Override
    public List getExtensions() {
        return Collections.emptyList();
    }

}

The rules itself are packaged according to the following structure:

/META-INF/jqassistant-plugin.xml
<v1:jqassistant-plugin xmlns:v1="http://www.buschmais.com/jqassistant/core/plugin/schema/v1.0">
    <description>An example plugin.</description>
    <rules>
        <resource>example.xml</resource>
    </rules>
</v1:jqassistant-plugin>
  • example.xml specifies the path to a jQAssistant rule file relative to /META-INF/jqassistant-rules

/META-INF/jqassistant-rules/example.xml
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

    <!-- Don't take these constraints serious - they are just here to illustrate integration with SonarQube -->

    <constraint id="example:AllClassesMustBeNamedFoo">
        <description>All classes must be named 'Foo'.</description>
        <cypher><![CDATA[
            MATCH
              (type:Class)
            WHERE NOT
              type.name = 'Foo'
            RETURN
              type AS InvalidClass
        ]]></cypher>
    </constraint>

    <constraint id="example:FieldsMustBeReadOnly" severity="critical">
        <description>All declared fields must be read-only (i.e. not be written).</description>
        <cypher><![CDATA[
            MATCH
              (m:Method)-[w:WRITES]->(f:Field)
            RETURN
              w AS WriteAccess
        ]]></cypher>
    </constraint>
</jqa:jqassistant-rules>

The created JAR archive and the jQAssistant must be copied to the folder 'extensions/plugins' of the SonarQube installation. The defined rules will be visible in the repository "jQAssistant".

Plugins

This section provides detailed descriptions of all distributed plugins.

jqassistant.plugin.cdi

Provides rules for CDI.

Scanner for beans.xml files

Imports beans descriptors from META-INF/beans.xml or WEB-INF/beans.xml files.

:File:Cdi:Beans

Represents a beans.xml file.

Table 1. Properties of :File:Cdi:Beans
Name Description

fileName

The file name

version

The version of the CDI specification this descriptor represents, e.g. '1.0'

beanDiscoveryMode

The bean discovery mode, i.e. "all", "annotated" or "none"

Table 2. Relations of :File:Cdi:Beans
Name Target label(s) Cardinality Description

HAS_INTERCEPTOR

:Type

0..n

References an interceptor type which is activated

HAS_DECORATOR

:Type

0..n

References a decorator type which is activated

HAS_ALTERNATIVE

:Type

0..n

References an alternative type (class or stereotype annotation) which is activated

cdi.xml

Concepts
cdi:Alternative

Labels all types annotated by @javax.enterprise.inject.Alternative with "Cdi" and "Alternative".

            MATCH
              (alternative:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(alternativeType:Type)
            WHERE
              alternativeType.fqn="javax.enterprise.inject.Alternative"
            SET
              alternative:Cdi:Alternative
            RETURN
              alternative AS Alternative
cdi:Any

Labels all elements annotated by "javax.enterprise.inject.Any" with "Cdi" and "Any".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(anyType:Type)
            WHERE
              anyType.fqn = "javax.enterprise.inject.Any"
            SET
              e:Cdi:Any
            RETURN
              e AS Any
cdi:ApplicationScoped

Labels all beans, fields or methods annotated by @javax.enterprise.context.ApplicationScoped with "Cdi" and "ApplicationScoped".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(scopeType:Type)
            WHERE
              (e:Type or e:Method or e:Field)
              and scopeType.fqn="javax.enterprise.context.ApplicationScoped"
            SET
              e:Cdi:ApplicationScoped
            RETURN
              e AS ApplicationScopedElement
cdi:ConversationScoped

Labels all beans, fields or methods annotated by @javax.enterprise.context.ConversationScoped with "Cdi" and "ConversationScoped".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(scopeType:Type)
            WHERE
              (e:Type or e:Method or e:Field)
              and scopeType.fqn="javax.enterprise.context.ConversationScoped"
            SET
              e:Cdi:ConversationScoped
            RETURN
              e AS ConversationScopedElement
cdi:Decorator

Requires concepts:

Labels all types annotated by @javax.decorator.Decorator with "Cdi" and "Decorator".

            MATCH
              (decorator:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(decoratorType:Type)
            WHERE
              decoratorType.fqn="javax.decorator.Decorator"
            SET
              decorator:Cdi:Decorator
            RETURN
              decorator AS Decorator
cdi:Default

Labels all elements annotated by "javax.enterprise.inject.Default" with "Cdi" and "Default".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(defaultType:Type)
            WHERE
              defaultType.fqn = "javax.enterprise.inject.Default"
            SET
              e:Cdi:Default
            RETURN
              e AS Default
cdi:Delegate

Labels all fields annotated annotated by @javax.decorator.Delegate with "Cdi" and "Delegate".

            MATCH
              (delegate:Field)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(delegateType:Type)
            WHERE
              delegateType.fqn="javax.decorator.Delegate"
            SET
              delegate:Cdi:Delegate
            RETURN
              delegate AS Delegate
cdi:Dependent

Labels all beans, fields or methods annotated by @javax.enterprise.context.Dependent with "Cdi" and "Dependent".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(scopeType:Type)
            WHERE
              (e:Type or e:Method or e:Field)
              and scopeType.fqn="javax.enterprise.context.Dependent"
            SET
              e:Cdi:Dependent
            RETURN
              e AS DependentElement
cdi:Disposes

Creates a relation DISPOSES between a parameter and its type if the parameter is annotated by @javax.enterprise.inject.Disposes.

            MATCH
              (:Type)-[:DECLARES]->(disposeMethod:Method)-[:HAS]->(parameter:Parameter),
              (parameter)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(disposesType:Type),
              (parameter)-[:OF_TYPE]->(type)
            WHERE
              disposesType.fqn="javax.enterprise.inject.Disposes"
            CREATE UNIQUE
              (parameter)-[:DISPOSES]->(type)
            RETURN
              disposeMethod AS DisposeMethod
cdi:EventConsumer

Requires concepts:

Labels all beans declaring method that has parameter of type "javax.enterprise.event.Observes" with "Cdi" and "EventConsumer".

            MATCH
              (a:Type)-[:DECLARES]->(member:Method)-[:HAS]->(param:Parameter),
              (param)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(injectType:Type)
            WHERE
              injectType.fqn = "javax.enterprise.event.Observes"
            SET
              a:Cdi:EventConsumer
            RETURN
              DISTINCT a.fqn AS cdiEventConsumer
cdi:EventProducer

Requires concepts:

Labels all beans declaring "InjectionPoint" of type "javax.enterprise.event.Event" with "Cdi" and "EventProducer".

            MATCH
              (a:Type)-[:DECLARES]->(member:Field:Cdi:InjectionPoint),
              (member)-[:OF_TYPE]->(injectType:Type)
            WHERE
              injectType.fqn = "javax.enterprise.event.Event"
            SET
              a:Cdi:EventProducer
            RETURN
              DISTINCT a.fqn AS cdiEventProducers
cdi:InjectionPoint

Labels all fields or methods annotated by @javax.inject.Inject with "Cdi" and "InjectionPoint".

            MATCH
              (:Type)-[:DECLARES]->(member),
              (member)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(injectType:Type)
            WHERE
              (member:Field or member:Method)
              and injectType.fqn="javax.inject.Inject"
            SET
              member:Cdi:InjectionPoint
            RETURN
              member AS InjectionPoint
cdi:Named

Labels all types or methods annotated by "javax.inject.Named" with "Cdi" and "Named".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(namedType:Type)
            WHERE
              namedType.fqn = "javax.inject.Named"
            SET
              e:Cdi:Named
            RETURN
              e AS Named
cdi:New

Labels all elements annotated by "javax.enterprise.inject.New" with "Cdi" and "New".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(newType:Type)
            WHERE
              newType.fqn = "javax.enterprise.inject.New"
            SET
              e:Cdi:New
            RETURN
              e AS New
cdi:Produces

Creates a relation PRODUCES between a field and its type or a method and its return type if the parameter is annotated by @javax.enterprise.inject.Disposes.

            MATCH
              (:Type)-[:DECLARES]->(member),
              (member)-[:OF_TYPE|RETURNS]->(type),
              (member)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(producesType:Type)
            WHERE
              (member:Field or member:Method)
              and producesType.fqn="javax.enterprise.inject.Produces"
            CREATE UNIQUE
              (member)-[:PRODUCES]->(type)
            RETURN
              member AS Producer
cdi:Qualifier

Labels all annotation types annotated by @javax.inject.Qualifier with "Cdi" and "Qualifier" and adds the labels "Cdi" and "Nonbinding" to all non-binding annotation values (i.e. which are annotated by @javax.enterprise.util.Nonbinding).

            MATCH
              (qualifier:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(qualifierType:Type)
            WHERE
              qualifierType.fqn = "javax.inject.Qualifier"
            SET
              qualifier:Cdi:Qualifier
            WITH
              qualifier
            MATCH
              (qualifier)-[:DECLARES]->(attribute:Method),
              (attribute)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(nonbindingType:Type)
            WHERE
              nonbindingType.fqn = "javax.enterprise.util.Nonbinding"
            SET
              attribute:Cdi:Nonbinding
            RETURN
              distinct qualifier AS Qualifier
cdi:RequestScoped

Labels all beans, fields or methods annotated by @javax.enterprise.context.RequestScoped with "Cdi" and "RequestScoped".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(scopeType:Type)
            WHERE
              (e:Type or e:Method or e:Field)
              and scopeType.fqn="javax.enterprise.context.RequestScoped"
            SET
              e:Cdi:RequestScoped
            RETURN
              e AS RequestScopedElement
cdi:SessionScoped

Labels all beans, fields or methods annotated by @javax.enterprise.context.SessionScoped with "Cdi" and "SessionScoped".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(scopeType:Type)
            WHERE
              (e:Type or e:Method or e:Field)
              and scopeType.fqn="javax.enterprise.context.SessionScoped"
            SET
              e:Cdi:SessionScoped
            RETURN
              e AS SessionScopedElement
cdi:SingletonScoped

Labels all beans annotated by @javax.inject.Singleton with "Cdi" and "SingletonScoped".

            MATCH
              (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn = "javax.inject.Singleton"
            SET
              t:Cdi:SingletonScoped
            RETURN
              t AS cdiSingleton
cdi:Specializes

Labels all types and methods annotated by @javax.enterprise.inject.Specializes with "Cdi" and "Specializes".

            MATCH
              (specializes)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(specializesType:Type)
            WHERE
              (specializes:Type or specializes:Method)
              and specializesType.fqn="javax.enterprise.inject.Specializes"
            SET
              specializes:Cdi:Specializes
            RETURN
              specializes AS Specialization
cdi:Stereotype

Labels all annotation types annotated by @javax.enterprise.inject.Stereotype with "Cdi" and "Stereotype".

            MATCH
              (stereotype:Type:Annotation)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(stereotypeType:Type)
            WHERE
              stereotypeType.fqn="javax.enterprise.inject.Stereotype"
            SET
              stereotype:Cdi:Stereotype
            RETURN
              stereotype AS Stereotype
Constraints
cdi:BeansMustNotUseFieldInjection

Requires concepts:

CDI beans shall not use field injection (constructor and setter injections are fine.).

            MATCH
              (a:Type)-[:DECLARES]->(member:Field:Cdi:InjectionPoint)
            RETURN
              DISTINCT a.fqn AS invalidBean
cdi:BeansMustUseConstructorInjection

Requires concepts:

All CDI beans must use constructor injection.

            MATCH
              (a:Type)-[:DECLARES]->(member:Cdi:InjectionPoint)
            WHERE
              NOT member:Constructor
            RETURN
              DISTINCT a.fqn AS invalidBean

decorator.xml

Concepts
decorator:Decorator

Requires concepts:

Labels all types annotated by @javax.decorator.Decorator with "Decorator".

            MATCH
              (decorator:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(decoratorType:Type)
            WHERE
              decoratorType.fqn="javax.decorator.Decorator"
            SET
              decorator:Decorator
            RETURN
              decorator AS Decorator
decorator:Delegate

Labels all fields annotated annotated by @javax.decorator.Delegate with "Decorator" and "Delegate".

            MATCH
              (delegate:Field)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(delegateType:Type)
            WHERE
              delegateType.fqn="javax.decorator.Delegate"
            SET
              delegate:Decorator:Delegate
            RETURN
              delegate AS Delegate

interceptor.xml

Concepts
interceptor:Binding

Labels all annotations annotated by "javax.interceptor.InterceptorBinding" with "Interceptor" and "Binding".

            MATCH
              (binding:Type:Annotation)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(bindingType:Type)
            WHERE
              bindingType.fqn = "javax.interceptor.InterceptorBinding"
            SET
              binding:Interceptor:Binding
            RETURN
              binding AS InterceptorBinding
interceptor:Interceptor

Labels all classes annotated by "javax.interceptor.Interceptor" with "Interceptor".

            MATCH
              (interceptor:Type:Class)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(interceptorType:Type)
            WHERE
              interceptorType.fqn = "javax.interceptor.Interceptor"
            SET
              interceptor:Interceptor
            RETURN
              interceptor AS Interceptor

jqassistant.plugin.ejb3

Provides rules for EJB3.

ejb3.xml

Concepts
ejb3:Local

Labels all types annotated with @javax.ejb.Local with "Ejb" and "Local".

            MATCH (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE a.fqn="javax.ejb.Local"
            SET t:Ejb:Local
            RETURN t AS LocalBean
ejb3:MessageDrivenBean

Labels all types annotated with @javax.ejb.MessageDriven with "Ejb" and "MessageDriven".

            MATCH (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE a.fqn="javax.ejb.MessageDriven"
            SET t:Ejb:MessageDriven
            RETURN t AS MessageDrivenBean
ejb3:Remote

Labels all types annotated with @javax.ejb.Remote with "Ejb" and "Remote".

            MATCH (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE a.fqn="javax.ejb.Remote"
            SET t:Ejb:Remote
            RETURN t AS RemoteBean
ejb3:SingletonBean

Labels all classes annotated with @javax.ejb.Singleton with "Ejb" and "Singleton".

            MATCH (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE a.fqn="javax.ejb.Singleton"
            SET t:Ejb:Singleton
            RETURN t AS SingletonEjb
ejb3:StatefulSessionBean

Labels all types annotated with @javax.ejb.Stateful with "Ejb" and "Stateful".

            MATCH (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE a.fqn="javax.ejb.Stateful"
            SET t:Ejb:Stateful
            RETURN t AS StatefulEjb
ejb3:StatelessSessionBean

Labels all types annotated with @javax.ejb.Stateless with "Ejb" and "Stateless".

            MATCH (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE a.fqn="javax.ejb.Stateless"
            SET t:Ejb:Stateless
            RETURN t AS StatelessEjb

jqassistant.plugin.jaxrs

Provides rules for JAX-RS.

jaxrs-resource.xml

Concepts
jaxrs:DeleteResourceMethod

Requires concepts:

Finds resource methods annotated with "@javax.ws.rs.DELETE" and labels them with 'DeleteResourceMethod'.

            MATCH
              (m:JaxRS:ResourceMethod)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            WHERE
              a.fqn = "javax.ws.rs.DELETE"
            SET
              m:DeleteResourceMethod
            RETURN
              m AS DeleteMethod
jaxrs:GetResourceMethod

Requires concepts:

Finds resource methods annotated with "@javax.ws.rs.GET" and labels them with 'GetResourceMethod'.

            MATCH
              (m:JaxRS:ResourceMethod)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            WHERE
              a.fqn = "javax.ws.rs.GET"
            SET
              m:GetResourceMethod
            RETURN
              m AS GetMethod
jaxrs:HeadResourceMethod

Requires concepts:

Finds resource methods annotated with "@javax.ws.rs.HEAD" and labels them with 'HeadResourceMethod'.

            MATCH
              (m:JaxRS:ResourceMethod)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            WHERE
              a.fqn = "javax.ws.rs.HEAD"
            SET
              m:HeadResourceMethod
            RETURN
              m AS HeadMethod
jaxrs:OptionsResourceMethod

Requires concepts:

Finds resource methods annotated with "@javax.ws.rs.OPTIONS" and labels them with 'OptionsResourceMethod'.

            MATCH
              (m:JaxRS:ResourceMethod)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            WHERE
              a.fqn = "javax.ws.rs.OPTIONS"
            SET
              m:OptionsResourceMethod
            RETURN
              m AS OptionsMethod
jaxrs:PostResourceMethod

Requires concepts:

Finds resource methods annotated with "@javax.ws.rs.POST" and labels them with 'PostResourceMethod'.

            MATCH
              (m:JaxRS:ResourceMethod)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            WHERE
              a.fqn = "javax.ws.rs.POST"
            SET
              m:PostResourceMethod
            RETURN
              m AS PostMethod
jaxrs:PutResourceMethod

Requires concepts:

Finds resource methods annotated with "@javax.ws.rs.PUT" and labels them with 'PutResourceMethod'.

            MATCH
              (m:JaxRS:ResourceMethod)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            WHERE
              a.fqn = "javax.ws.rs.PUT"
            SET
              m:PutResourceMethod
            RETURN
              m AS PutMethod
jaxrs:RequestMethodDesignator

Finds request method designator annotations (i.e. @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS) and labels them with 'JaxRS' and 'RequestMethodDesignator'

            MATCH
              (a:Type)
            WHERE
              a.fqn IN ["javax.ws.rs.GET", "javax.ws.rs.POST", "javax.ws.rs.PUT", "javax.ws.rs.DELETE", "javax.ws.rs.HEAD", "javax.ws.rs.OPTIONS"]
            SET
              a:JaxRS:RequestMethodDesignator
            RETURN
              a AS RequestMethodDesignator
jaxrs:Resource

Requires concepts:

Finds classes or interfaces with atleast one method annotated with "@javax.ws.rs.Path" or with request method designator (i.e. @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS) and labels them with 'JaxRS' and 'Resource'.

            MATCH
              (t:Type)-[:DECLARES]->(m:Method)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:Type)
            WHERE
              (a:JaxRS:RequestMethodDesignator OR a.fqn="javax.ws.rs.Path")
            AND
              (t:Class OR t:Interface)
            SET
              t:JaxRS:Resource
            RETURN
              DISTINCT t AS RestResource
jaxrs:ResourceMethod

Requires concepts:

Finds methods that belong to resource class or interface and annotated with request method designator (i.e. @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS) and labels them with 'JaxRS' and 'ResourceMethod'.

            MATCH
              (c:JaxRS:Resource)-[:DECLARES]->(m:Method)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:JaxRS:RequestMethodDesignator)
            SET
              m:JaxRS:ResourceMethod
            RETURN
              m AS ResourceMethod
jaxrs:SubResourceLocator

Requires concepts:

Finds classes or interfaces with method annotated with "@javax.ws.rs.Path" but no request method designator and no entity parameters and labels the methods with 'JaxRS' and 'SubResourceLocator'.

            MATCH
              (t:Type)-[:DECLARES]->(m:Method)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn = "javax.ws.rs.Path"
            AND
              (t:Class OR t:Interface)
            AND
              NOT (m)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(:JaxRS:RequestMethodDesignator)
            AND
              (m)-[:HAS]->(:Parameter)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(:Type)
            SET
              m:JaxRS:SubResourceLocator
            RETURN
              DISTINCT m AS RestSubResourceLocator

jaxrs-provider.xml

Concepts
jaxrs:ContextProvider

Finds classes that implement "javax.ws.rs.ext.ContextResolver" and labels them with 'JaxRS' and 'ContextProvider'

            MATCH
              (c:Type:Class)-[:IMPLEMENTS]->(a:Type)
            WHERE
              a.fqn = "javax.ws.rs.ext.ContextResolver"
            SET
              c:JaxRS:ContextProvider
            RETURN
              DISTINCT c AS ContextProvider
jaxrs:EntityProvider

Finds classes that implement "javax.ws.rs.ext.MessageBodyReader" or "javax.ws.rs.ext.MessageBodyWriter" and labels them with 'JaxRS' and 'EntityProvider'

            MATCH
              (c:Type:Class)-[:IMPLEMENTS]->(a:Type)
            WHERE
              a.fqn IN ["javax.ws.rs.ext.MessageBodyReader", "javax.ws.rs.ext.MessageBodyWriter"]
            SET
              c:JaxRS:EntityProvider
            RETURN
              DISTINCT c AS EntityProvider
jaxrs:ExceptionMappingProvider

Finds classes that implement "javax.ws.rs.ext.ExceptionMapper" and labels them with 'JaxRS' and 'ExceptionMappingProvider'

            MATCH
              (c:Type:Class)-[:IMPLEMENTS]->(a:Type)
            WHERE
              a.fqn = "javax.ws.rs.ext.ExceptionMapper"
            SET
              c:JaxRS:ExceptionMappingProvider
            RETURN
              DISTINCT c AS ExceptionMappingProvider

jqassistant.plugin.java

Provides scanner for Java elements (e.g. packages, classes, manifest and property files) and rules for common language concepts (e.g. Deprecation), dependencies and metrics.

Package Scanner

Imports Java packages.

:Package

A Java package, i.e. a directory containing '.class' files or other directories.

Table 3. Properties of :Package
Name Description

fqn

Fully qualified name, e.g. 'java.lang'

name

The local name, e.g. 'lang'

Table 4. Relations of :Package
Name Target label(s) Cardinality Description

CONTAINS

:Type

0..n

References a type located in the package

CONTAINS

:Package

0..n

References a package located in the package

Class Scanner

Imports Java classes, i.e. all scanned files having a '.class' suffix. Nodes with following labels will be created:

NOTE Some of these labels may be further qualified with other labels, see the description below.

NOTE The full set of information is only available for class files which have actually been scanned. Types which are only referenced (i.e. from external libraries not included in the scan) are represented by ':TYPE' nodes with a property 'fqn' and 'DECLARES' relations to their members. These are ':FIELD' or ':METHOD' labeled nodes which only provide the property 'signature'.

:Type

A Java type. Can be qualified by either ':Class', ':Interface', ':Enum' or ':Annotation'

Table 5. Properties of :Type
Name Description

fqn

Fully qualified name, e.g. 'java.lang.Object'

name

The local name, e.g. 'Object'

visibility

optional, the visibility of the type, can be either 'public', 'protected', 'default' or 'private'

abstract

optional, 'true' indicates that the type is abstract, e.g. 'public abstract class …​'

static

optional, 'true' indicates that the type has the static modifier, e.g. 'private static class …​'

final

optional, 'true' indicates that the type is final, e.g. 'public final class…​'

synthetic

optional, 'true' indicates that the type is synthetic, i.e. it has been generated

Table 6. Relations of :Type
Name Target label(s) Cardinality Description

DECLARES

:Type

0..n

Declares an inner type of the type

DECLARES

:Method

0..n

Declares a method of the type

DECLARES

:Field

0..n

Declares a field of the type

EXTENDS

:Type

0..1

References a type this type extends from

IMPLEMENTS

:Type

0..1

References an "Interface" type this type implements

ANNOTATED_BY

:Value:Annotation

0..n

References an annotation which is present on the type

DEPENDS_ON

:Type

0..n

References a type which this type depends on according to its signature (e.g. due to generic type parameters)

NOTE Types which are referenced by scanned classes but have not been scanned themselves will only provide the property "fqn" and the relation "DECLARES".

NOTE Inheritance between interfaces (i.e. "public interface A extends B { …​ }") is represented using IMPLEMENTS relations, i.e. queries must use (a:Type:Interface)-[:IMPLEMENTS]→(b:Type:Interface) for pattern matching.

:Type:Class

Qualifies a Java type as class.

:Type:Interface

Qualifies a Java type node as interface.

:Type:Enum

Qualifies a Java type as enumeration.

:Type:Annotation

Qualifies a Java type as annotation.

:Field

A field declared in a Java type.

Table 7. Properties of :Field
Name Description

name

The field name, e.g. 'id'

signature

The raw signature of the field, e.g. 'int id', 'java.lang.String toString()'

visibility

optional, The visibility of the field, can be either 'public', 'protected', 'default' or 'private'

static

optional, 'true' indicates that the field has the static modifier, e.g. 'static int id;'

final

optional, 'true' indicates that the field is final, e.g. 'final int id;'

transient

optional, 'true' indicates that the field is transient, e.g. 'transient int id;'

volatile

optional, 'true' indicates that the field is volatile, e.g. 'volatile int id;'

synthetic

optional, 'true' indicates that the field is synthetic, i.e. it has been generated

Table 8. Relations of :Field
Name Target label(s) Cardinality Description

OF_TYPE

:Type

1

References the type of the field

ANNOTATED_BY

:Value:Annotation

0..n

References an annotation which is present on the field

DEPENDS_ON

:Type

0..n

References a type which this field depends on according to its signature (e.g. generic type parameters)

NOTE Fields which are referenced by scanned classes but have not been scanned themselves will only provide the property "signature".

:Method

A method declared in a Java type.

Table 9. Properties of :Method
Name Description

name

The method name, e.g. 'getId'

signature

The raw signature of the method, e.g. 'int getId()', 'java.lang.String concat(java.lang.String,java.lang.String)'

visibility

optional, The visibility of the method, can be either 'public', 'protected', 'default' or 'private'

static

optional, 'true' indicates that the method has the static modifier, e.g. 'static int getId();'

final

optional, 'true' indicates that the method is final, e.g. 'final int getId();'

native

optional, 'true' indicates that the method is native, e.g. 'native int getId();'

synthetic

optional, 'true' indicates that the method is synthetic, i.e. it has been generated

Table 10. Relations of :Method
Name Target label(s) Cardinality Description

HAS

:Parameter

0..n

References a parameter of the method

THROWS

:Type

0..n

References the exception type thrown by the method

RETURNS

:Type

0..n

References the return type of the method

ANNOTATED_BY

:Value:Annotation

0..n

References an annotation which is present on the method declaration

READS

:Field

0..n

References a field which is read by the method

WRITES

:Field

0..n

References a field which is written by the method

INVOKES

:Method

0..n

References a method which is invoked by the method

DEPENDS_ON

:Type

0..n

References a type which this method depends on (e.g. generic type parameters, dependencies from the method body)

NOTE Methods which are referenced by scanned classes but have not been scanned themselves will only provide the property "signature".

:Method:Constructor

Qualifies a method as constructor.

:Parameter

A method parameter.

Table 11. Properties of :Parameter
Name Description

index

The index of the parameter according to the method signature (starting with 0)

Table 12. Properties of :Parameter
Name Target label(s) Cardinality Description

OF_TYPE

:Type

1

References the type of the parameter

ANNOTATED_BY

:Value:Annotation

0..n

References an annotation which is present on the parameter

DEPENDS_ON

:Type

0..n

References a type which this parameter depends on according to its signature (e.g. generic type parameters)

:Value

A value, can be qualified by either ':Primitive', ':Annotation', ':Class', ':Enum' or ':Array'.

Table 13. Properties of :Value
Name Description

name

The method name, e.g. 'value'

:Value:Primitive

A primitive value.

Table 14. Properties of :Value:Primitive
Name Description

value

The value

:Value:Annotation

Represents a annotation on a Java element, e.g. '@Entity public class …​'

Table 15. Relations of :Value:Annotation:
Name Target label(s) Cardinality Description

OF_TYPE

:Type

1

References the type of the annotation

HAS

:Value

0..n

References an attribute of the annotation, e.g. '@Entity(name="MyEntity")'

:Value:Class

Represents a class instance, e.g. as specified by annotation attribute.

Table 16. Relations of :Value:Class:
Name Target label(s) Cardinality Description

IS

:Type

1

References the type

:Value:Enum

Represents an enum value.

Table 17. Relations of :Value:Enum:
Name Target label(s) Cardinality Description

IS

:Field

1

References the field representing the enumeration value

:Value:Array

Represents an array value, i.e. a node referencing value nodes.

Table 18. Relations of :Value:Array:
Name Target label(s) Cardinality Description

CONTAINS

:Value

0..n

References a value contained in the array

Manifest File Scanner

Imports manifest descriptors from META-INF/MANIFEST.MF files.

:File:Manifest

A MANIFEST.MF file containing sections.

Table 19. Properties of :File:Manifest
Name Description

fileName

The file name

Table 20. Relations of :Manifest
Name Target label(s) Cardinality Description

DECLARES

:ManifestSection

0..n

References a manifest section

:ManifestSection

A manifest section.

Table 21. Relations of :ManifestSection
Name Target label(s) Cardinality Description

HAS

:Value:ManifestEntry

0..n

References a manifest entry in the section

:Value:ManifestEntry

A manifest entry.

Table 22. Properties of :Value:ManifestEntry
Name Description

name

The name of the entry, e.g. 'Main-Class'

value

The value of the entry, e.g. 'com.buschmais.jqassistant.scm.cli.Main'

Property File Scanner

Imports property files, i.e. all files having a suffix '.properties'.

:File:Properties

A property file containing key/value pairs.

Table 23. Properties of :File:Properties
Name Description

fileName

The file name

Table 24. Relations of :File:Properties
Name Target label(s) Cardinality Description

HAS

:Value:Property

0..n

References a property value

:Value:Property

A key value/pair.

Table 25. Properties of :Value:Property
Name Description

name

The name of the property

value

The value of the property

Service Loader File Scanner

Imports service loader descriptors from "META-INF/services" directories.

:File:ServiceLoader

A file containing the implementation class names for a service interface

Table 26. Properties of :File:ServiceLoader
Name Description

fileName

The file name

Table 27. Relations of :File:ServiceLoader
Name Target label(s) Cardinality Description

OF_TYPE

:Type

1

The type representing the service interface

CONTAINS

:Type

0..n

References a type which implements the service interface

dependency.xml

Concepts
dependency:Annotation

Creates a DEPENDS_ON relationship between an annotated element and the types of the annotation and its values.

            MATCH
                (e)-[:ANNOTATED_BY]->(a:Annotation)-[:OF_TYPE]->(at:Type)
            WHERE
                e:Type
                or e:Method
                or e:Parameter
                or e:Field
            CREATE UNIQUE
                (e)-[:DEPENDS_ON]->(at)
            WITH
                e, a
            MATCH
                (a)-[:HAS|IS|CONTAINS|OF_TYPE*0..]->(vt:Type)
            CREATE UNIQUE
                (e)-[:DEPENDS_ON]->(vt)
            RETURN
                e AS AnnotatedType, COUNT(DISTINCT vt) as Elements
dependency:Artifact

Requires concepts:

Creates a DEPENDS_ON relationship between artifacts if there are type dependencies between them.

            MATCH
                (a1:Artifact)-[:CONTAINS]->(t1:Type)-[:DEPENDS_ON]->(t2:Type)<-[:CONTAINS]-(a2:Artifact)
            WHERE
                a1<>a2
            CREATE UNIQUE
                (a1)-[:DEPENDS_ON]->(a2)
            RETURN
                a1 AS Artifact, COLLECT(DISTINCT a2.name) AS Dependencies
dependency:FieldAccess

Creates a DEPENDS_ON relationship between a method and all fields types it reads and writes.

            MATCH
                (m:Method)-[:READS|WRITES]->(target:Field),
                (dt:Type)-[:DECLARES]->(target)
            CREATE UNIQUE
                (m)-[:DEPENDS_ON]->(dt)
            WITH
                m, target
            MATCH
                  (target)-[:OF_TYPE|DEPENDS_ON]->(ft)
            CREATE UNIQUE
                (m)-[:DEPENDS_ON]->(ft)
            RETURN
                m AS method, COUNT(DISTINCT ft) AS FieldAccesseCount
dependency:MethodInvocation

Requires concepts:

Creates a DEPENDS_ON relationship between a method and the dependencies of the method signature it invokes.

            MATCH
                (m:Method)-[:INVOKES]->(target:Method),
                (dt:Type)-[:DECLARES]->(target)
            CREATE UNIQUE
                (m)-[:DEPENDS_ON]->(dt)
            WITH
                m, target
            MATCH
                (target)-[:HAS]->(:Parameter)-[:DEPENDS_ON]->(pt)
            CREATE UNIQUE
                (m)-[:DEPENDS_ON]->(pt)
            WITH
                m, target
            MATCH
                (target)-[:RETURNS]->(rt)
            CREATE UNIQUE
                (m)-[:DEPENDS_ON]->(rt)
            RETURN
                m AS Method, COUNT(DISTINCT rt) AS MethodInvocations
dependency:MethodParameter

Requires concepts:

Creates a DEPENDS_ON relationship between a method and the types of its parameters.

            MATCH
                (m:Method)-[:HAS]->(p:Parameter)-[:OF_TYPE|DEPENDS_ON]->(t:Type)
            CREATE UNIQUE
                (m)-[:DEPENDS_ON]->(t)
            RETURN
                m AS Method, COUNT(DISTINCT t) as Parameters
dependency:Package

Requires concepts:

Creates a DEPENDS_ON relationship between a packages if there are type dependencies between them.

            MATCH
                (p1:Package)-[:CONTAINS]->(t1:Type)-[:DEPENDS_ON]->(t2:Type)<-[:CONTAINS]-(p2:Package)
            WHERE
                p1<>p2
            CREATE UNIQUE
                (p1)-[:DEPENDS_ON]->(p2)
            RETURN
                p1 AS package, COUNT(DISTINCT p2) AS PackageDependencyCount
dependency:Type

Requires concepts:

Creates a DEPENDS_ON relationship between a type and all types its signature and body depends on.

            MATCH
                (t1:Type)-[:EXTENDS|IMPLEMENTS]->(t2:Type)
            WHERE
                t1<>t2
            CREATE UNIQUE
                (t1)-[:DEPENDS_ON]->(t2)
            RETURN
                t1 AS Type, COUNT(DISTINCT t2) AS DependencyCount
dependency:TypeBody

Requires concepts:

Creates a DEPENDS_ON relationship between a type and all types its body (i.e. methods and fields) depends on.

            MATCH
                (t1:Type)-[:DECLARES*0..1]->(fieldOrMethod)-[:OF_TYPE|RETURNS|DEPENDS_ON|THROWS]->(t2:Type)
            WHERE
                t1<>t2
                and (fieldOrMethod:Field OR fieldOrMethod:Method)
            CREATE UNIQUE
                (t1)-[:DEPENDS_ON]->(t2)
            RETURN
                t1 AS Type, COUNT(DISTINCT t2) AS DependencyCount
Constraints
dependency:ArtifactCycles

Requires concepts:

There must be no cyclic artifact dependencies.

            MATCH
                (a1:Artifact)-[:DEPENDS_ON]->(a2:Artifact),
                path=shortestPath((a2)-[:DEPENDS_ON*]->(a1))
            WHERE
                a1<>a2
            RETURN
                a1 AS Artifact, EXTRACT(a IN nodes(path) | a.fqn) AS Cycle
            ORDER BY
                Artifact.fqn
dependency:PackageCycles

Requires concepts:

There must be no cyclic package dependencies.

            MATCH
                (p1:Package)-[:DEPENDS_ON]->(p2:Package),
                path=shortestPath((p2)-[:DEPENDS_ON*]->(p1))
            WHERE
                p1<>p2
            RETURN
                p1 AS Package, EXTRACT(p IN nodes(path) | p.fqn) AS Cycle
            ORDER BY
                Package.fqn
dependency:TypeCycles

Requires concepts:

There must be no cyclic type dependencies.

            MATCH
                (p1:Package)-[:DEPENDS_ON*0..1]->(p2:Package),
                shortestPath((p2)-[:DEPENDS_ON*]->(p1))
            WITH
                p1, p2
            MATCH
                (p1)-[:CONTAINS]->(t1:Type),
                (p2)-[:CONTAINS]->(t2:Type),
                (t1)-[:DEPENDS_ON]->(t2),
                path=shortestPath((t2)-[:DEPENDS_ON*]->(t1))
            WHERE
                NOT (
                  t1 = t2 OR (t1)-[:DECLARES]-(t2)
                )
            RETURN
                LENGTH(path) AS Length, EXTRACT(p IN nodes (path) | p.fqn) AS Cycle

java.xml

Concepts
java:AnonymousInnerType

Requires concepts:

Sets a label "Anonymous" on anonymous inner types, i.e. types without a name.

            MATCH
              (innerType:Inner:Type)
            WHERE
              innerType.name =~ ".*\\$[0-9]*"
            SET
              innerType:Anonymous
            RETURN
              innerType AS AnonymousInnerType
java:Deprecated

Labels all nodes representing deprecated elements (types, fields, methods, packages or parameters) with "Deprecated".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(dt:Type)
            WHERE
              dt.fqn='java.lang.Deprecated'
            SET
              e:Deprecated
            RETURN
              e AS DeprecatedElement
java:Error

Labels types deriving from java.lang.Error as "Error".

            MATCH
              (throwable)-[:EXTENDS*]->(t:Type)
            WHERE
              t.fqn = 'java.lang.Error'
            SET
              throwable:Error
            RETURN
              throwable AS Error
java:Exception

Labels types deriving from java.lang.Exception as "Exception".

            MATCH
              (exception)-[:EXTENDS*]->(t:Type)
            WHERE
              t.fqn = 'java.lang.Exception'
            SET
              exception:Exception
            RETURN
              exception AS Exception
java:InnerType

Sets a label "Inner" on inner types.

            MATCH
              (:Type)-[:DECLARES]->(innerType:Type)
            SET
              innerType:Inner
            RETURN
              innerType AS InnerType
java:InvokesOverriddenMethod

Requires concepts:

Propagates INVOKES relationships to methods which implement or override the invoked method.

            MATCH
              (method:Method)-[:INVOKES]->(invokedMethod:Method),
              (overridingMethod:Method)-[:OVERRIDES]->(invokedMethod)
            CREATE UNIQUE
              (method)-[r:INVOKES]->(overridingMethod)
            RETURN r AS OverridingInvocation
java:MethodOverloads

Creates a relationship OVERLOADS between two "Method" labeled nodes if one method overloads another one from the same type (i.e. the methods have the same name but not the same signature).

            MATCH
              (type:Type)-[:DECLARES]->(method:Method),
              (type)-[:DECLARES]->(otherMethod:Method)
            WHERE
              method <> otherMethod
              AND method.name = otherMethod.name
              AND method.signature <> otherMethod.signature
            CREATE UNIQUE
                (method)-[:OVERLOADS]->(otherMethod)
            RETURN method AS OverloadedMethod, type AS DeclaringType
java:MethodOverrides

Requires concepts:

Creates a relationship OVERRIDES between two "Method" labeled nodes if one method overrides another one from a super type (i.e. the methods have the same signature).

            MATCH
              (type:Type)-[:DECLARES]->(method:Method),
              (superType:Type)-[:DECLARES]->(otherMethod:Method),
              (superType)-[:ASSIGNABLE_FROM]->(type)
            WHERE
              method.name = otherMethod.name
              AND method.signature = otherMethod.signature
              AND method.visibility <> 'private'
            CREATE UNIQUE
              (method)-[:OVERRIDES]->(otherMethod)
            RETURN method AS OverriddenMethod, type AS DeclaringType, superType AS SuperType
java:RuntimeException

Labels types deriving from java.lang.RuntimeException as "RuntimeException".

            MATCH
              (runtimeException)-[:EXTENDS*]->(t:Type)
            WHERE
              t.fqn = 'java.lang.RuntimeException'
            SET
              runtimeException:RuntimeException
            RETURN
              runtimeException AS RuntimeException
java:Throwable

Labels types deriving from java.lang.Throwable as "Throwable".

            MATCH
              (throwable)-[:EXTENDS*]->(t:Type)
            WHERE
              t.fqn = 'java.lang.Throwable'
            SET
              throwable:Throwable
            RETURN
              throwable AS Throwable
java:TypeAssignableFrom

Creates a relationship ASSIGNABLE_FROM between two "Type" labeled nodes if one type is assignable from the other (i.e. a super class or interface).

            MATCH
              (type:Type)
            CREATE UNIQUE
              (type)-[:ASSIGNABLE_FROM]->(type)
            WITH
              type
            MATCH
              (type)-[:IMPLEMENTS|EXTENDS*]->(superType:Type)
            CREATE UNIQUE
              (superType)-[:ASSIGNABLE_FROM]->(type)
            RETURN type AS AssignableType, superType AS AssignableFrom

metric.xml

Concepts
metric:Top10FieldsPerType

Requires concepts:

Returns the top 10 types regarding to the number of declared fields.

            MATCH
                (t:Type:File)-[:DECLARES]->(f:Field)
            RETURN
                t.fqn as Type, COUNT(f) as FieldCount
            ORDER BY
                FieldCount DESC
            LIMIT 10
metric:Top10MethodsPerType

Requires concepts:

Returns the top 10 types regarding to the number of declared methods (including constructors).

            MATCH
                (t:Type:File)-[:DECLARES]->(m:Method)
            RETURN
                t.fqn as Type, COUNT(m) as MethodCount
            ORDER BY
                MethodCount DESC
            LIMIT 10
metric:Top10TypeFanIn

Requires concepts:

Returns the top 10 types regarding the number of other types depending on them.

            MATCH
                (t:Type:File)<-[:DEPENDS_ON]-(dependent:Type)
            RETURN
                t.fqn as Type, COUNT(dependent) as Dependents
            ORDER BY
                Dependents DESC
            LIMIT 10
metric:Top10TypeFanOut

Requires concepts:

Returns the top 10 types regarding the number of other types they depend on.

            MATCH
                (t:Type:File)-[:DEPENDS_ON]->(dependency:Type)
            RETURN
                t.fqn as Type, COUNT(dependency) as Dependencies
            ORDER BY
                Dependencies DESC
            LIMIT 10
metric:Top10TypesPerArtifact

Returns the top 10 artifacts regarding the number of contained types.

            MATCH
                (a:Artifact:File)-[:CONTAINS]->(t:Type:File)
            RETURN
                a.fqn as Artifact, COUNT(t) as Types
            ORDER BY
                Types DESC
            LIMIT 10
metric:Top10TypesPerPackage

Returns the top 10 packages regarding the number of contained types.

            MATCH
                (p:Package:File)-[:CONTAINS]->(t:Type:File)
            RETURN
                p.fqn as Package, COUNT(t) as Types
            ORDER BY
                Types DESC
            LIMIT 10

jqassistant.plugin.java8

Provides Java 8 specific rules, e.g. concepts for functional interfaces and default methods.

java8.xml

Concepts
java8:DefaultMethod

Labels default methods of interfaces with "Default".

            MATCH
              (type:Type:Interface)-[:DECLARES]->(defaultMethod:Method)
            WHERE NOT
              has(defaultMethod.abstract)
            SET
              defaultMethod:Default
            RETURN
              defaultMethod AS DefaultMethod, type AS Interface
java8:FunctionalInterface

Labels functional interfaces (i.e. to be used as lambda expressions) with "FunctionalInterface".

            MATCH
              (functionalInterface:Interface)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(functionalInterfaceType)
            WHERE
              functionalInterfaceType.fqn = 'java.lang.FunctionalInterface'
            SET
              functionalInterface:FunctionalInterface
            RETURN
              functionalInterface AS FunctionalInterface

jqassistant.plugin.jpa2

Provides a scanner for persistence.xml descriptors and rules for JPA2 specific elements (e.g. entities).

Scanner for persistence.xml files

Imports persistence descriptors from META-INF/persistence.xml or WEB-INF/persistence.xml files.

:File:Jpa:Persistence

A persistence.xml file containing persistence units.

Table 28. Properties of :File:Jpa:Persistence
Name Description

fileName

The file name

version

The version of the JPA specification this descriptor represents, e.g. '2.0'

Table 29. Relations of :File:Jpa:Persistence
Name Target label(s) Cardinality Description

CONTAINS

:Jpa:PersistenceUnit

0..n

References a contained persistence unit

:Jpa:PersistenceUnit

A persistence unit.

Table 30. Properties of :Jpa:PersistenceUnit
Name Description

description

The description of the persistence unit.

transactionType

The transaction type, can be 'RESOURCE_LOCAL' or 'JTA'

provider

The class name of the JPA provider.

jtaDatasource

The JNDI name of the transactional datasource

nonJtaDatasource

The JNDI name of the non-transactional datasource

validationMode

The validation mode, can be 'NONE', 'CALLBACK' or 'AUTO'

sharedCacheMode

The shared cache mode, e.g. 'NONE'

Table 31. Relations of :Jpa:PersistenceUnit
Name Target label(s) Cardinality Description

CONTAINS

:Type

0..n

References a persistent type (entity) contained in the persistence unit

HAS

:Value:Property

0..n

References a property of the persistence unit

jpa2.xml

Concepts
jpa2:Embeddable

Labels all types annotated with @javax.persistence.Embeddable with "Jpa" and "Embeddable".

            MATCH
              (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="javax.persistence.Embeddable"
            SET
              t:Jpa:Embeddable
            RETURN
              t AS JpaEmbeddable
jpa2:Embedded

Labels all fields or methods annotated with @javax.persistence.Embedded with "Jpa" and "Embedded".

            MATCH
              (t:Type)-[:DECLARES]->(member),
              (member)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              (member:Field or member:Method)
              and a.fqn="javax.persistence.Embedded"
            SET
              member:Jpa:Embedded
            RETURN
              member AS JpaEmbedded
jpa2:EmbeddedId

Labels all fields or methods annotated with @javax.persistence.EmbeddedId with "Jpa" and "Embedded".

            MATCH
              (t:Type)-[:DECLARES]->(member),
              (member)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              (member:Field or member:Method)
              and a.fqn="javax.persistence.EmbeddedId"
            SET
              member:Jpa:EmbeddedId
            RETURN
              member AS EmbeddedId
jpa2:Entity

Labels all types annotated with @javax.persistence.Entity with "Jpa" and "Entity".

            MATCH
              (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="javax.persistence.Entity"
            SET
              t:Jpa:Entity
            RETURN
              t AS JpaEntity
jpa2:NamedQuery

Requires concepts:

Creates a node labeled with "Jpa" and "NamedQuery" for each @NamedQuery defined for an entity. Furthermore a relation DEFINES is created between the entity and the named query.

            MATCH
              (entity:Jpa:Entity)-[:ANNOTATED_BY]->(namedQueries)-[:OF_TYPE]->(namedQueriesType:Type),
              (namedQueries)-[:HAS]->(:Value:Array)-[:CONTAINS]->(namedQuery:Value:Annotation)-[:OF_TYPE]->(namedQueryType:Type),
              (namedQuery)-[:HAS]->(nameAttribute:Value),
              (namedQuery)-[:HAS]->(queryAttribute:Value)
            WHERE
              namedQueriesType.fqn = 'javax.persistence.NamedQueries'
              and namedQueryType.fqn = 'javax.persistence.NamedQuery'
              and nameAttribute.name = 'name'
              and queryAttribute.name = 'query'
            CREATE UNIQUE
              (entity)-[:DEFINES]->(n:Jpa:NamedQuery {name:nameAttribute.value, query:queryAttribute.value})
            RETURN
              entity AS Entity, n.name AS Name, n.query AS Query
Constraints
jpa2:ValidationModeMustBeExplicitlySpecified

The validation mode of all persistence units must be explicitly specified and either set to CALLBACK or NONE.

            MATCH
              (pu:PersistenceUnit)
            WHERE
              not has(pu.validationMode)
              OR NOT (
                pu.validationMode='CALLBACK'
                OR pu.validationMode='NONE'
              )
            RETURN
              pu AS PersistenceUnit

jqassistant.plugin.junit4

Provides scanner for JUnit test reports and rules (e.g. for test classes/methods and ignored tests).

Scanner for JUnit4 test suite results

Imports results from JUnit4 test suites matching the file name 'TEST-.xml'.

:File:TestSuite

A file containing results of a JUnit4 test suite.

Table 32. Properties of :File:TestSuite
Name Description

fileName

The file name

name

The name of the test suite, e.g. the name of the class defining the suite

tests

The number of executed tests

failures

The number of failed tests

errors

The number of tests in error

skipped

The number of skipped tests

time

The time it took to run the suite

Table 33. Relations of :File:TestSuite
Name Target label(s) Cardinality Description

CONTAINS

:TestCase

0..n

References a test case

:TestCase

A test case.

Table 34. Properties of :TestCase
Name Description

name

The name of the test, e.g. the name of the test method

className

The name of the class containing the test

time

The time it took to run the test

result

The result of the test, either 'SUCCESS', 'FAILURE', 'ERROR' or 'SKIPPED'

junit3.xml

Concepts
junit3:SetUpMethod

Requires concepts:

Labels all setUp methods declared by a test class with "SetUp" and "Junit3".

            MATCH
              (c:Class:Test:Junit3)-[:DECLARES]->(m:Method)
            WHERE
              m.signature = "void setUp()"
            SET
              m:SetUp:Junit3
            RETURN
              m AS SetUpMethod, c AS TestClass
junit3:TearDownMethod

Requires concepts:

Labels all tearDown methods declared by a test class with "TearDown" and "Junit3".

            MATCH
              (c:Class:Test:Junit3)-[:DECLARES]->(m:Method)
            WHERE
              m.signature = "void tearDown()"
            SET
              m:TearDown:Junit3
            RETURN
              m AS TearDownMethod, c AS TestClass
junit3:TestClass

Labels all non-abstract classes extending from junit.framework.TestCase "Test" and "Junit3".

            MATCH
              (c:Type:Class)-[:EXTENDS*]->(testCaseType:Type)
            WHERE
              testCaseType.fqn = "junit.framework.TestCase"
            SET
              c:Test:Junit3
            RETURN
              c AS TestClass
junit3:TestMethod

Requires concepts:

Labels all test methods declared by a test class with "Test" and "Junit3".

            MATCH
              (c:Class:Test:Junit3)-[:DECLARES]->(m:Method)
            WHERE
              m.signature =~ "void test.*\\(.*\\)"
            SET
              m:Test:Junit3
            RETURN
              m AS Test, c AS TestClass

junit4.xml

Concepts
junit4:AfterClassMethod

Labels all methods annotated by "@org.junit.AfterClass" with "Junit4" and "AfterClass".

            MATCH
              (c:Type:Class)-[:DECLARES]->(m:Method),
              (m:Method)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="org.junit.AfterClass"
            SET
              m:Junit4:AfterClass
            RETURN
              m AS AfterClassMethod, c AS TestClass
junit4:AfterMethod

Labels all methods annotated by "@org.junit.After" with "Junit4" and "After".

            MATCH
              (c:Type:Class)-[:DECLARES]->(m:Method),
              (m:Method)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="org.junit.After"
            SET
              m:Junit4:After
            RETURN
              m AS AfterMethod, c AS TestClass
junit4:AssertMethod

Labels all assertion methods declared by org.junit.Assert with "Junit4" and "Assert".

            MATCH
              (assertType:Type)-[:DECLARES]->(assertMethod)
            WHERE
              assertType.fqn = 'org.junit.Assert'
              and assertMethod.signature =~ 'void assert.*'
            SET
              assertMethod:Junit4:Assert
            RETURN
              assertMethod
junit4:BeforeClassMethod

Labels all methods annotated by "@org.junit.BeforeClass" with "Junit4" and "BeforeClass".

            MATCH
              (c:Type:Class)-[:DECLARES]->(m:Method),
              (m:Method)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="org.junit.BeforeClass"
            SET
              m:Junit4:BeforeClass
            RETURN
              m AS BeforeClassMethod, c AS TestClass
junit4:BeforeMethod

Labels all methods annotated by "@org.junit.Before" with "Junit4" and "Before".

            MATCH
              (c:Type:Class)-[:DECLARES]->(m:Method),
              (m:Method)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="org.junit.Before"
            SET
              m:Junit4:Before
            RETURN
              m AS BeforeMethod, c AS TestClass
junit4:IgnoreTestClassOrMethod

Labels all classes or methods annotated with "@org.junit.Ignore" with "Junit4" and "Ignore".

            MATCH
              (e)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="org.junit.Ignore"
            SET
              e:Junit4:Ignore
            RETURN
              e AS IgnoredElement
junit4:TestCaseImplementedByMethod

Creates a relation IMPLEMENTED_BY between all test cases from test reports and their implementing methods.

            MATCH
              (testcase:TestCase)
            WITH
              testcase
            MATCH
              (testclass:Type)-[:DECLARES]->(testmethod:Method)
            WHERE
              testclass.fqn = testcase.className
              and testmethod.name = testcase.name
            CREATE UNIQUE
              (testcase)-[:IMPLEMENTED_BY]->(testmethod)
            RETURN
              testcase AS TestCase
junit4:TestClass

Requires concepts:

Labels all classes containing test methods with "Test" and "Junit4".

            MATCH
              (c:Type:Class)-[:DECLARES]->(m:Method:Junit4:Test)
            SET
              c:Test:Junit4
            RETURN
              c AS TestClass, COLLECT(m) AS TestMethods
junit4:TestClassOrMethod
_The rule is deprecated: This concept has been replaced by "junit4:TestMethod" and "junit4:TestClass"._

Requires concepts:

Finds test methods (i.e. annotated with "@org.junit.Test") and labels them and their containing classes with "Test" and "Junit4".

            MATCH
              (c:Type:Class)-[:DECLARES]->(m:Method:Junit4:Test)
            RETURN
              c AS TestClass, COLLECT(m) AS TestMethods
junit4:TestMethod

Finds all test methods (i.e. annotated with "@org.junit.Test") and labels them with "Test" and "Junit4".

            MATCH
              (m:Method)-[:ANNOTATED_BY]-()-[:OF_TYPE]->(a:Type)
            WHERE
              a.fqn="org.junit.Test"
            SET
              m:Test:Junit4
            RETURN
              m AS Test
Constraints
junit4:AssertionMustProvideMessage

Requires concepts:

All assertions must provide a message.

            MATCH
              (testType:Type)-[:DECLARES]->(testMethod:Method),
              (testMethod)-[invocation:INVOKES]->(assertMethod:Assert:Method)
            WHERE
              NOT assertMethod.signature =~ 'void assert.*\\(java.lang.String,.*\\)'
            RETURN
              invocation AS Invocation,
              testType AS DeclaringType,
              testMethod AS Method
junit4:IgnoreWithoutMessage

All @Ignore annotations must provide a message.

            MATCH
              (e)-[:ANNOTATED_BY]->(ignore:Annotation)-[:OF_TYPE]->(ignoreType:Type)
            WHERE
              ignoreType.fqn= "org.junit.Ignore"
              AND NOT (ignore)-[:HAS]->(:Value{name:"value"})
            RETURN
              e AS IgnoreWithoutMessage
junit4:TestMethodWithoutAssertion

Requires concepts:

All test methods must perform assertions (within a call hierarchy of max. 3 steps).

            MATCH
              (testType:Type)-[:DECLARES]->(testMethod:Test:Method)
            WHERE
              NOT (testMethod)-[:INVOKES*..3]->(:Method:Assert)
            RETURN
              testType AS DeclaringType,
              testMethod AS Method

jqassistant.plugin.maven3

Provides a scanner for Maven 3 modules.

Scanner for Maven projects

Imports information from Maven projects.

:Maven:Project:File

A pom.xml file describing a single Maven project.

Table 35. Properties of :Maven:Project:File
Name Description

fileName

The directory of the project.

name

The name

groupId

The group id

artifactId

The artifact id

packaging

The packaging type, e.g. "jar"

version

The version

Table 36. Relations of :Maven:Project:File
Name Target label(s) Cardinality Description

CREATES

:Artifact

0..n

References an artifact created by the project

HAS_PARENT

:Maven:Project:File

0..1

References the parent project (optional)

HAS_MODULE

:Maven:Project:File

0..n

References modules of this project (optional)

:Artifact

Represents an artifact, e.g. a JAR-File.

Table 37. Properties of :Artifact
Name Description

groupId

The group id

artifactId

The artifact id

type

The type, e.g. "jar"

classifier

The classifiert

version

The version

Table 38. Relations of :Artifact
Name Target label(s) Cardinality Description

CONTAINS

:File

0..n

References a file contained in the artifact

DEPENDS_ON

:Artifact

0..n

References a artifact which is a declared dependency

Table 39. Properties of :DEPENDS_ON
Name Description

scope

The declared scope, e.g. "compile"

optional

"true" indicates that this dependency is optional.

maven3.xml

Constraints
maven3:HierarchicalParentModuleRelation

If a parent Maven project declares a module then the parent project must also be declared as the parent of the module (i.e. to keep the project hierarchy consistent).

            match
              (parent:Maven:Project)-[:HAS_MODULE]->(module:Maven:Project)
            where
              not (module)-[:HAS_PARENT]->(parent)
            return
              module as InvalidModule

jqassistant.plugin.osgi

Provides rules for OSGi bundles (e.g. Bundles, Activator).

osgi-bundle.xml

Concepts
osgi-bundle:Activator

Requires concepts:

Creates an ACTIVATES relation between a class and the bundle artifact if the class is declared as "Bundle-Activator" in the bundle manifest.

            match
              (bundle:Osgi:Bundle)-[:CONTAINS]->(manifest:File:Manifest),
              (manifest)-[:DECLARES]->(section:ManifestSection),
              (section)-[:HAS]->(entry:ManifestEntry),
              (bundle)-[:CONTAINS]->(activator:Class)
            where
              entry.name = "Bundle-Activator"
              and entry.value = activator.fqn
            create unique
              (activator)-[:ACTIVATES]->(bundle)
            return
              activator as Activator, bundle as Bundle
osgi-bundle:Bundle

Labels all artifacts with a manifest declaring a bundle name as "Osgi" and "Bundle" and adds the properties "bundleSymbolicName" and "bundleVersion".

            MATCH
              (bundle:Artifact)-[:CONTAINS]->(manifest:Manifest:File),
              (manifest)-[DECLARES]->(section:ManifestSection),
              (section)-[:HAS]->(nameEntry:ManifestEntry),
              (section)-[:HAS]->(versionEntry:ManifestEntry)
            WHERE
              nameEntry.name = "Bundle-SymbolicName"
              AND versionEntry.name = "Bundle-Version"
            SET
              bundle:Osgi:Bundle,
              bundle.bundleSymbolicName = nameEntry.value,
              bundle.bundleVersion = versionEntry.value
            RETURN
              bundle as Bundle, bundle.bundleSymbolicName as BundleSymbolicName, bundle.bundleVersion as BundleVersion
osgi-bundle:ExportPackage

Requires concepts:

Creates an EXPORTS relation from a bundle artifact to all packages which are declared as "Export-Package" in the bundle manifest.

            match
             (bundle:Osgi:Bundle)-[:CONTAINS]->(package:Package)
            with
              bundle, package, "(^|.*,)\\s*"+ replace(package.fqn, ".", "\\.")+"\\s*((;|,).*|$)" as pattern
            match
              (bundle)-[:CONTAINS]->(manifest:File:Manifest),
              (manifest)-[:DECLARES]->(section:ManifestSection),
              (section)-[:HAS]->(entry:ManifestEntry)
            where
              entry.name = "Export-Package"
              AND entry.value=~ pattern
            create unique
              (bundle)-[:EXPORTS]->(package)
            return
              bundle as Bundle, collect(package) as ExportedPackages
osgi-bundle:ImportPackage

Requires concepts:

Creates an IMPORTS relation from a bundle artifact to all packages which are declared as "Import-Package" in the bundle manifest.

            match
             (package:Package)
            with
              package, "(^|.*,)\\s*"+ replace(package.fqn, ".", "\\.")+"\\s*((;|,).*|$)" as pattern
            match
              (bundle:Osgi:Bundle)-[:CONTAINS]->(manifest:File:Manifest),
              (manifest)-[:DECLARES]->(section:ManifestSection),
              (section)-[:HAS]->(entry:ManifestEntry)
            where
              entry.name = "Import-Package"
              and entry.value =~ pattern
            create unique
              (bundle)-[:IMPORTS]->(package)
            return
              bundle as Bundle, collect(package) as ImportedPackages
osgi-bundle:InternalType

Requires concepts:

Labels all internal types (i.e. which are not located in an exported package) as "Internal".

            match
              (bundle:Bundle:Osgi)-[:CONTAINS]->(internalPackage:Package),
              (bundle)-[:CONTAINS]->(internalType:Type),
              (internalPackage)-[:CONTAINS]->(internalType:Type)
            where not
                (bundle)-[:EXPORTS]->(internalPackage)
            set
              internalType:Internal
            return bundle as Bundle, collect(internalType) as InternalTypes
Constraints
osgi-bundle:InternalTypeMustNotBePublic

Requires concepts:

Internal types must not be public if no depending types exist in other packages of the bundle.

            match
              (bundle:Osgi:Bundle)-[:CONTAINS]->(internalType:Type:Internal),
              (internalPackage:Package)-[:CONTAINS]->(internalType)
            optional match
              (bundle)-[:CONTAINS]->(otherPackage:Package),
              (otherPackage)-[:CONTAINS]->()-[:DEPENDS_ON]->(internalType)
            where
              internalType.visibility='public'
            with
              bundle, internalPackage, internalType, collect(otherPackage) as otherPackages
            where
              all(otherPackage in otherPackages where internalPackage = otherPackage)
              and not
                (internalType)-[:ACTIVATES]->(bundle)
            return
              bundle as Bundle, internalType as InternalType
osgi-bundle:UnusedInternalType

Requires concepts:

A bundle must not contain internal types which are not used by other types in the bundle.

            match
              (bundle:Osgi:Bundle)-[:CONTAINS]->(internalType:Type:Internal)
            where not (
                (internalType)-[:ACTIVATES]->(bundle)
              or
                (bundle)-[:CONTAINS]->(:Type)-[:DEPENDS_ON]->(internalType)
            )
            return
              bundle as Bundle, internalType as InternalType

jqassistant.plugin.tycho

Provides a scanner for tycho modules.

Scanner for Tycho projects

Adds Tycho specific resources to be scanned to the file system scanner.