java - Assert package call directions with AspectJ -
let's assume have following package structure
some.package |-aaa.private |-aaa.public |-bbb.private |-bbb.public
my architecture demand, make calls some.package.aaa..*
some.package.bbb.public..*
, vice versa, calls some.package.bbb..*
some.package.aaa.public..*
. in other words, if traverse "major" package border (e.g. aaa bbb), want allow calls public package in root of other major package.
is possible define aspectj pointcut, selects joinpoints violate rule? i.e. if want write
declare error: insomemajorpackage() && callingnonpublicpackageofothermajorpackage() : "please make calls public interfaces of other major packages";
is there way define 2 pointcuts, such enforce rule?
attention: lengthy answer because of code samples.
i created sample project can downloaded scrum-master.de. package structure follows:
as can see, below main application package de.scrum_master there 3 "major" packages common, feature1, feature2, each containing sub-packages pub (public) , prv (private). additionally there aop package containing aspects. each pub/prv sub-package contains dummy class.
the java classes follows:
package de.scrum_master.common.pub; import de.scrum_master.common.prv.commonprivate; import de.scrum_master.feature1.prv.feature1private; import de.scrum_master.feature1.pub.feature1public; import de.scrum_master.feature2.prv.feature2private; import de.scrum_master.feature2.pub.feature2public; public class application { private int id; private string name; public application(int id, string name) { super(); this.id = id; this.name = name; } public static void main(string[] args) { system.out.println(new application (1, "application")); system.out.println(new commonprivate (2, "common (private)")); system.out.println(new feature1public (3, "feature 1 (public)")); system.out.println(new feature1private(4, "feature 1 (private)")); system.out.println(new feature2public (5, "feature 2 (public)")); system.out.println(new feature2private(6, "feature 2 (private)")); } @override public string tostring() { return "application [id=" + id + ", name=" + name + "]"; } }
package de.scrum_master.common.prv; public class commonprivate { private int id; private string name; public commonprivate(int id, string name) { super(); this.id = id; this.name = name; } @override public string tostring() { return "commonprivate [id=" + id + ", name=" + name + "]"; } }
package de.scrum_master.feature1.pub; public class feature1public { private int id; private string name; public feature1public(int id, string name) { super(); this.id = id; this.name = name; } @override public string tostring() { return "feature1public [id=" + id + ", name=" + name + "]"; } }
package de.scrum_master.feature1.prv; import de.scrum_master.feature2.prv.feature2private; import de.scrum_master.feature2.pub.feature2public; public class feature1private { private int id; private string name; public feature1private(int id, string name) { super(); this.id = id; this.name = name; } @override public string tostring() { new feature2private(11111, "this should illegal"); new feature2public(22222, "this should ok"); return "feature1private [id=" + id + ", name=" + name + "]"; } }
package de.scrum_master.feature2.pub; public class feature2public { private int id; private string name; public feature2public(int id, string name) { super(); this.id = id; this.name = name; } @override public string tostring() { return "feature2public [id=" + id + ", name=" + name + "]"; } }
package de.scrum_master.feature2.prv; import de.scrum_master.feature1.prv.feature1private; import de.scrum_master.feature1.pub.feature1public; public class feature2private { private int id; private string name; public feature2private(int id, string name) { super(); this.id = id; this.name = name; } @override public string tostring() { new feature1private(33333, "this should illegal"); new feature1public(44444, "this should ok"); return "feature2private [id=" + id + ", name=" + name + "]"; } }
now need our aspect. more exactly, need abstract base aspect , 1 package-specific concrete sub-aspect each "major" package. not nice, works.
the abstract base aspect looks this:
package de.scrum_master.aop; public abstract aspect accesscontroller { // method/constructor calls base package pointcut basepackagecall(): call(* de.scrum_master..*.*(..)) || call(de.scrum_master..*.new(..)); // method/constructor calls public packages pointcut publicpackagecall() : call(* de.scrum_master..*.pub..*(..)) || call(de.scrum_master..*.pub..new(..)); // own "major" package. please override in concrete sub-aspect this: // within(de.scrum_master.mymajor..*) pointcut ownpackage(); // method/constructor calls within own "major" package. please override in concrete sub-aspect this: // call(* de.scrum_master.mymajor..*(..)) || call(de.scrum_master.mymajor..new(..)) pointcut ownpackagecall(); pointcut forbiddencall() : ownpackage() && basepackagecall() && !(publicpackagecall() || ownpackagecall()); declare error: forbiddencall() : "illegal call non-public foreign major package"; }
as can see there 2 pointcuts must concretised sub-aspects this:
package de.scrum_master.aop; public aspect accesscontroller_common extends accesscontroller { pointcut ownpackage() : within(de.scrum_master.common..*); pointcut ownpackagecall() : call(* de.scrum_master.common..*(..)) || call(de.scrum_master.common..new(..)); }
package de.scrum_master.aop; public aspect accesscontroller_feature1 extends accesscontroller { pointcut ownpackage() : within(de.scrum_master.feature1..*); pointcut ownpackagecall() : call(* de.scrum_master.feature1..*(..)) || call(de.scrum_master.feature1..new(..)); }
package de.scrum_master.aop; public aspect accesscontroller_feature2 extends accesscontroller { pointcut ownpackage() : within(de.scrum_master.feature2..*); pointcut ownpackagecall() : call(* de.scrum_master.feature2..*(..)) || call(de.scrum_master.feature2..new(..)); }
creating sub-aspects new "major" packages simple copy & paste plus minor editing corresponding package name.
if inspect application.main
, feature1private.tostring
, feature2private.tostring
see built in illegal calls non-public foreign sub-packages there, 4 in all. looks in eclipse's problem view:
a few more words base/sub aspects: while in advice possible dynamically determine package names , more magic via reflection, declare error
based on pointcuts can determined statically during compile time. thus, must more specific , explicit here requires have 1 sub-aspect each "major" package in scenario. alternative 1 big concrete aspect containing pointcuts each single package. thought ugly though.
now enjoy solution, think adequately addresses problem. :-)
Comments
Post a Comment