2 * This is a common dao with basic CRUD operations and is not limited to any
3 * persistent layer implementation
5 * Copyright (C) 2008 Imran M Yousuf (imyousuf@smartitengineering.com)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 package com
.smartitengineering
.exim
.impl
;
21 import com
.smartitengineering
.domain
.annotations
.Eager
;
22 import com
.smartitengineering
.domain
.annotations
.Export
;
23 import com
.smartitengineering
.domain
.annotations
.Id
;
24 import com
.smartitengineering
.domain
.annotations
.Name
;
25 import com
.smartitengineering
.domain
.annotations
.ResourceDomain
;
26 import com
.smartitengineering
.domain
.exim
.DomainSelfExporter
;
27 import com
.smartitengineering
.domain
.exim
.DomainSelfImporter
;
28 import com
.smartitengineering
.domain
.exim
.IdentityCustomizer
;
29 import com
.smartitengineering
.domain
.exim
.StringValueProvider
;
30 import com
.smartitengineering
.exim
.AssociationConfig
;
31 import com
.smartitengineering
.exim
.ClassConfigScanner
;
32 import com
.smartitengineering
.exim
.ConfigRegistrar
;
33 import com
.smartitengineering
.exim
.EximResourceConfig
;
34 import com
.smartitengineering
.exim
.PackageConfigScanner
;
35 import com
.smartitengineering
.util
.simple
.IOFactory
;
36 import com
.smartitengineering
.util
.simple
.reflection
.AnnotationConfig
;
37 import com
.smartitengineering
.util
.simple
.reflection
.ClassAnnotationVisitorImpl
;
38 import com
.smartitengineering
.util
.simple
.reflection
.ClassScanner
;
39 import com
.smartitengineering
.util
.simple
.reflection
.VisitCallback
;
40 import java
.lang
.annotation
.Annotation
;
41 import java
.lang
.reflect
.AnnotatedElement
;
42 import java
.lang
.reflect
.Field
;
43 import java
.lang
.reflect
.Method
;
44 import java
.util
.Collection
;
45 import java
.util
.HashMap
;
46 import java
.util
.HashSet
;
51 * This registrar is responsible for scanning and containing configuration of
52 * all resources. Whenever a class is looked up whose package has not been
53 * scanned yet, registrar will scanClassForConfig its package to generate cofiguration and
54 * while generating it will keep scanning until it reaches the leaves, i.e. in
55 * this case association to an object that isn't {@link ResourceDomain}
59 public class DefaultAnnotationConfigScanner
60 implements ClassConfigScanner
,
61 PackageConfigScanner
{
63 private static final String GETTER_PREFIX
= "get";
64 private static DefaultAnnotationConfigScanner registrar
;
67 ConfigRegistrar
.registerClassScanner(
68 DefaultAnnotationConfigScanner
.class, 25);
69 ConfigRegistrar
.registerPackageScanner(
70 DefaultAnnotationConfigScanner
.class, 25);
73 public static DefaultAnnotationConfigScanner
getInstance() {
74 if (registrar
== null) {
75 registrar
= new DefaultAnnotationConfigScanner();
79 protected final ClassScanner classScanner
;
80 protected final Map
<Class
, EximResourceConfig
> configuraitons
;
81 protected final Collection
<Class
> scannedClasses
;
82 protected final Collection
<String
> scannedPackages
;
83 protected final ResourceVisitCallback resourceVisitCallback
;
84 protected final VisitCallback
<AnnotationConfig
> callbackHandler
;
86 protected DefaultAnnotationConfigScanner() {
87 classScanner
= IOFactory
.getDefaultClassScanner();
88 configuraitons
= new HashMap
<Class
, EximResourceConfig
>();
89 scannedClasses
= new HashSet
<Class
>();
90 scannedPackages
= new HashSet
<String
>();
91 resourceVisitCallback
= new ResourceVisitCallback();
92 callbackHandler
= resourceVisitCallback
;
95 public synchronized Map
<Class
, EximResourceConfig
> getConfigurations() {
96 return configuraitons
;
99 public synchronized Collection
<Class
> getConfiguredResourceClasses() {
100 return configuraitons
.keySet();
104 * Retrieves the configuration for the resource class, if its not already
105 * generated then it will search the package of the class and generate its
107 * @param resourceClass The class to scanClassForConfig retrieve configuration for
108 * @return Configuration for the resource; NULL if no resource is available
110 * @throws IllegalArgumentException If resource class is null!
112 public synchronized EximResourceConfig
getResourceConfigForClass(
113 final Class resourceClass
) {
114 if (resourceClass
== null) {
115 throw new IllegalArgumentException("Resource class can't be null!");
117 if (configuraitons
.containsKey(resourceClass
)) {
118 return configuraitons
.get(resourceClass
);
121 String packageName
= resourceClass
.getPackage().getName();
122 EximResourceConfig resourceConfig
= null;
123 if (!scannedPackages
.contains(packageName
)) {
124 resourceConfig
= scanPackage(Package
.getPackage(packageName
),
127 return resourceConfig
;
132 * Scans and prepares all configurations in the package and makes it
133 * available for future use.
134 * @param resourcePackage Package to scan and gather configuration
135 * @throws IllegalArgumentException If package is null
137 public synchronized void scanPackageForResourceConfigs(
138 final Package resourcePackage
) {
139 if (resourcePackage
== null) {
140 throw new IllegalArgumentException("Resource class can't be null!");
142 scanPackage(resourcePackage
, null);
145 protected String
getPropertyNameFromMethodName(final String methodName
) {
146 StringBuilder propertyNameBuilder
=
147 new StringBuilder(methodName
);
148 propertyNameBuilder
.delete(0, GETTER_PREFIX
.length());
149 char firstChar
= propertyNameBuilder
.charAt(0);
150 propertyNameBuilder
.delete(0, 1);
151 propertyNameBuilder
.insert(0, Character
.toLowerCase(firstChar
));
152 return propertyNameBuilder
.toString();
156 * Scan among the class's annotations to find the required configuration for
157 * exporting and importing resources.
158 * @param probableResourceClass The probable resource domain class.
159 * @return The configuration of the class, null if not annotated with
160 * {@link ResourceDomain}
162 protected EximResourceConfig
scanClassForConfig(
163 final Class probableResourceClass
) {
164 if(probableResourceClass
== null) {
167 if (scannedClasses
.contains(probableResourceClass
)) {
168 return getConfigurations().get(probableResourceClass
);
170 Annotation annotation
= probableResourceClass
.getAnnotation(
171 ResourceDomain
.class);
172 if (annotation
== null) {
175 EximResourceConfigImpl resourceConfig
= new EximResourceConfigImpl();
176 resourceConfig
.setDomainClass(probableResourceClass
);
177 Name nameAnnotation
= (Name
) probableResourceClass
.getAnnotation(
179 if (nameAnnotation
!= null) {
180 resourceConfig
.setName(nameAnnotation
.value());
183 resourceConfig
.setName(probableResourceClass
.getName());
185 ResourceDomain domainAnnotation
= (ResourceDomain
) annotation
;
186 resourceConfig
.setAccessByPropertyEnabled(domainAnnotation
.
188 resourceConfig
.setAssociateExportPolicyAsUri(domainAnnotation
.
189 exportAsURIByDefault());
190 resourceConfig
.setPathToResource(domainAnnotation
.path());
191 resourceConfig
.setExporterImplemented(DomainSelfExporter
.class.
192 isAssignableFrom(probableResourceClass
));
193 resourceConfig
.setImporterImplemented(DomainSelfImporter
.class.
194 isAssignableFrom(probableResourceClass
));
195 resourceConfig
.setIdentityCustomizerImplemented(
196 IdentityCustomizer
.class.isAssignableFrom(probableResourceClass
));
197 scanMembers(resourceConfig
, probableResourceClass
);
198 scannedClasses
.add(probableResourceClass
);
199 configuraitons
.put(probableResourceClass
, resourceConfig
);
200 return resourceConfig
;
204 * Scan a package for extracting configurations of resource domains.
205 * @param packageName Package to scanClassForConfig.
206 * @param resourceClass Main class scanClassForConfig requested for
207 * @return The configuration of the resource class, Null if the class is not
208 * a domain class or resourceClass is null
209 * @throws java.lang.IllegalArgumentException If package is null
211 protected EximResourceConfig
scanPackage(final Package packageToScan
,
212 final Class resourceClass
)
213 throws IllegalArgumentException
{
214 if (packageToScan
== null) {
215 throw new IllegalArgumentException();
217 EximResourceConfig resourceConfig
= null;
218 classScanner
.scan(new String
[]{packageToScan
.getName()},
219 new ClassAnnotationVisitorImpl(callbackHandler
,
220 IOFactory
.getAnnotationNameForVisitor(ResourceDomain
.class)));
221 Set
<String
> classPaths
= resourceVisitCallback
.getProbableResources();
222 if (!classPaths
.isEmpty()) {
223 for (String classPath
: classPaths
) {
225 Class probableResourceClass
=
226 IOFactory
.getClassFromVisitorName(classPath
);
227 EximResourceConfig config
= scanClassForConfig(
228 probableResourceClass
);
229 if (config
!= null && resourceClass
!= null &&
230 probableResourceClass
.equals(resourceClass
)) {
231 resourceConfig
= config
;
234 catch (Exception ex
) {
238 scannedPackages
.add(packageToScan
.getName());
239 return resourceConfig
;
243 * It will scan all member attributes and behavior based on configuration on
244 * the class. It will also scan all inherited attributes and behavior.
245 * @param resourceConfig The config representing the domain class
246 * @param resourceClass The domain class
248 protected void scanMembers(final EximResourceConfigImpl resourceConfig
,
249 final Class resourceClass
) {
250 if (resourceConfig
.isAccessByPropertyEnabled()) {
251 scanMethods(resourceConfig
, resourceClass
);
254 scanFields(resourceConfig
, resourceClass
);
259 * Scans getter methods for discovering associations of the domain and thier
260 * respective configurations. It will only scan public getter methods.
261 * @param resourceConfig The config of the domain resource
262 * @param resourceClass The domain class
264 protected void scanMethods(final EximResourceConfigImpl resourceConfig
,
265 final Class resourceClass
) {
266 Method
[] methods
= resourceClass
.getMethods();
267 if (methods
== null || methods
.length
<= 0) {
270 for (Method method
: methods
) {
271 String methodName
= method
.getName();
272 //Only scan getter methods as of bean spec, that getter methods with
273 //no paratmeters and non-void return types
274 if (methodName
.startsWith(GETTER_PREFIX
) && methodName
.length() >
275 GETTER_PREFIX
.length() && method
.getReturnType() != null &&
276 !method
.getReturnType().equals(Void
.class) &&
277 (method
.getParameterTypes() == null ||
278 method
.getParameterTypes().length
<= 0)) {
279 scanGetterMethod(resourceConfig
, method
);
285 * Scans a getter method for annotations which is used to cofigure the
286 * nature of the export
287 * @param resourceConfig The config to populate with configurations
288 * @param method The getter method to scan
289 * @throws java.lang.IllegalArgumentException If its not a getter method
290 * with non-void return type and
291 * with a non-zero length bean
292 * name length and with no
295 protected void scanGetterMethod(final EximResourceConfigImpl resourceConfig
,
297 throws IllegalArgumentException
{
298 String methodName
= method
.getName();
299 if (!(methodName
.startsWith(GETTER_PREFIX
) && methodName
.length() >
300 GETTER_PREFIX
.length() && method
.getReturnType() != null &&
301 !method
.getReturnType().equals(Void
.class) &&
302 (method
.getParameterTypes() == null ||
303 method
.getParameterTypes().length
<= 0))) {
304 throw new IllegalArgumentException();
306 String propertyName
= getPropertyNameFromMethodName(methodName
);
307 Class returnType
= method
.getReturnType();
308 scanAnnotatedElement(method
, propertyName
, returnType
, resourceConfig
);
312 * Scans fields for discovering associations of the domain and thier
313 * respective configurations
314 * @param resourceConfig The config of the domain resource
315 * @param resourceClass The domain class
317 protected void scanFields(EximResourceConfigImpl resourceConfig
,
318 Class resourceClass
) {
319 Field
[] fields
= resourceClass
.getDeclaredFields();
320 for (Field field
: fields
) {
321 scanField(resourceConfig
, field
);
323 Class parentClass
= resourceClass
.getSuperclass();
324 if (!parentClass
.equals(Object
.class)) {
325 scanFields(resourceConfig
, parentClass
);
330 * A field is scanned for gathering configurations for export-import.
331 * @param resourceConfig Configuraton for the field association
332 * @param field The field to scan
334 protected void scanField(final EximResourceConfigImpl resourceConfig
,
336 String propertyName
= field
.getName();
337 Class propertyType
= field
.getType();
338 scanAnnotatedElement(field
, propertyName
, propertyType
, resourceConfig
);
342 * Scan an ennotated element to extract configuration information
343 * @param element Element to scan
344 * @param propertyName The name of the property scanning
345 * @param propertyType The type of the property
346 * @param resourceConfig The configuration for the domain class
347 * @throws java.lang.IllegalArgumentException If any argument is null
349 protected void scanAnnotatedElement(final AnnotatedElement element
,
350 final String propertyName
,
351 final Class propertyType
,
352 final EximResourceConfigImpl resourceConfig
)
353 throws IllegalArgumentException
{
354 if (element
== null || propertyName
== null || propertyType
== null ||
355 resourceConfig
== null) {
356 throw new IllegalArgumentException();
359 element
.getAnnotation(Export
.class);
360 AssociationConfigImpl configImpl
=
361 new AssociationConfigImpl();
362 configImpl
.setName(propertyName
);
363 configImpl
.setAssociationType(AssociationConfig
.AssociationType
.
364 getAssociationType(propertyType
));
366 element
.getAnnotation(Eager
.class);
367 configImpl
.setStringProviderImplemented(StringValueProvider
.class.
368 isAssignableFrom(propertyType
));
369 configImpl
.setEagerSet(eager
!= null);
370 if (annotation
!= null) {
371 configImpl
.setItToBeExportedAsUri(!annotation
.asObject());
372 configImpl
.setTransient(annotation
.isTransient());
375 configImpl
.setItToBeExportedAsUri(false);
376 configImpl
.setTransient(false);
378 resourceConfig
.getAssociationConfigs().put(propertyName
, configImpl
);
380 element
.getAnnotation(Id
.class);
382 resourceConfig
.setIdPropertyName(propertyName
);
387 * The visitor callback to be notified for dmain classes.
389 protected static class ResourceVisitCallback
390 implements VisitCallback
<AnnotationConfig
> {
392 private Set
<String
> probableResources
= new HashSet
<String
>();
394 public void handle(AnnotationConfig config
) {
395 probableResources
.add(config
.getClassName());
398 public Set
<String
> getProbableResources() {
399 return probableResources
;