|
Page 4 of 4
Spring configuration files
.QL is truly an object-oriented language. Not only all classes such as XMLFile are not fixed in SemmleCode (you can find definition of all SemmleCode classes in default.ql file), but you can also easily write new class definitions of your own.
As we are going to query only this Spring configuration files in the remainder of the tutorial, it makes sense at this stage to use the object-riented nature of the .QL language and define a special class SpringConfigFile based on the previous query:
class SpringConfigFile extends XMLFile {
SpringConfigFile() {
this.getADTD().getSystemId() =
"http://www.springframework.org/dtd/spring-beans.dtd"
}
}
This class definition states that a SpringConfigFile is a special kind of XMLFile. The constructor actually makes the distinguishing property of the new class precise: this file has a specific document type definition.
Lets this time select all Spring configuration files as well as all XML elements that are defined in them. Since elements can reside inside other elements, in order to find all elements we define our query recursively to search for children of the file:
from SpringConfigFile f
select f.getPath(),
f,
f.getAChild*()
A little * next to the getAChild method call says that we are interested in all direct and indirect (transitively) children of a file.
Spring Beans
As we have seen from the previous query each Spring configuration file contains many different XML elements. Each such element carries a certain semantic meaning, which could also be encapsulated in a .QL class and conveniently reused in subsequent framework specific queries.
To illustrate we are going to define a notion of a special Sping configuration file element - bean. Bean is an XML element that has name "bean" and is defined in a spring configuration file. We override the definition of the getName() method that is inherited from the XMLElement class and set it to be the "id" attribute of the bean XML element. Beans also have several other special properties, such as "scope" of the bean, "class" that it refers to, etc. For each of such properties we define methods in our Bean class for convenience in future uses of the class.
class Bean extends XMLElement {
Bean() { super.getName() = "bean" and
super.getFile() instanceof SpringConfigFile
}
string getName() {
(this.hasIdAttribute() and result = this.getId()) or (not this.hasIdAttribute() and result = "")
}
string getId() { result = this.getAttributeValue("id") }
predicate hasIdAttribute() { exists(this.getAttributeValue("id")) }
string getScope() {
(this.hasAttribute("scope") and result = this.getId()) or (not this.hasAttribute("scope") and result = "singleton")
}
string getClassName() { result = this.getAttributeValue("class") }
}
There are many more properties that a Spring bean may have. If you are a seasoned Spring developer, you will immediately think of some. Do you already know how to add them to your Bean class?
Querying different software artefacts
As a final excercise, let us now illustrate how queries that involve different software artefacts (in this case Java and XML) can be easily expressed in .QL all in the same manner. We are going to write a query that finds all Beans that refer to non-existent Java classes in the source code of the JPetStore project:
from Bean b
where b.getClassName().matches("org.springframework.samples.jpetstore%") and
not exists(Class c| c.getQualifiedName() = b.getClassName())
select b, b.getClassName() + " is refered from bean " +
b.getName() + ", but cannot be found!"
This query says that we are looking for all those beans, such that they refer to a class, whose qualified name starts with a specific name and there does not exist a class that has the same qualified name.
Click on the
( ) icon to visualize the results as Eclipse warnings. One problem, in this case planted by us, will be reported.
The referred class should be PetStoreAnnotationImpl in the same package. Try changing the XML configuration file and press save. The database is incrementally updated and you do not need to repopulate the project again. Rerun the query. The problem will no longer be reported.
Before rerunning the query, make sure you clean problems previously reported by Semmle. To do that go to the Eclipse errors view and select "Clear SemmleCode results" in its context menu.
|