Changeset 676


Ignore:
Timestamp:
2008-10-07 09:58:24 (5 years ago)
Author:
bruno
Message:

Factored out the error-prone rebuild-on-change logic employed by various routing components to a class ReloadableResource? (this was already done for the new directory router, now also applied it to the JAX-RS router and the global router).

Location:
trunk/modules/kauri-routing/kauri-routing-impl/src/main/java/org/kauriproject/routing/impl
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/modules/kauri-routing/kauri-routing-impl/src/main/java/org/kauriproject/routing/impl/components/DirectoryRouter.java

    r674 r676  
    210210 
    211211    private class DirectoryRouterBuilder implements ReloadableResource.ResourceBuilder { 
    212         public Object build() { 
    213             return DirectoryRouter.this.build(); 
     212        public ReloadableResource.Result build() { 
     213            return new ReloadableResource.Result(DirectoryRouter.this.build()); 
     214        } 
     215 
     216        public void dispose(Object object) { 
     217            // we don't need disposing 
    214218        } 
    215219    } 
  • trunk/modules/kauri-routing/kauri-routing-impl/src/main/java/org/kauriproject/routing/impl/components/KauriJaxRsRestlet.java

    r637 r676  
    1414import org.kauriproject.util.ArgumentValidator; 
    1515import org.kauriproject.routing.impl.routing.DisposableRoutingComponent; 
    16 import org.apache.commons.jci.monitor.FilesystemAlterationListener; 
    17 import org.apache.commons.jci.monitor.FilesystemAlterationObserver; 
    1816import org.apache.commons.logging.Log; 
    1917import org.apache.commons.logging.LogFactory; 
     
    5856    private Set<String> providerClassNames = new HashSet<String>(); 
    5957 
    60     private boolean initialized; 
     58    private ReloadableResource reloadableResource; 
    6159    private Context context; 
    6260    private KauriModule module; 
    6361    private ModuleSource moduleSource; 
    6462    private AutowireCapableBeanFactory beanFactory; 
     63    private final Log log = LogFactory.getLog(getClass().getName()); 
     64 
    6565    private static final String GROOVY_EXT = ".groovy"; 
    6666    private static final String CLASS_EXT = ".class"; 
    67     private Restlet router; 
    68     private boolean needsBuilding = true; 
    69     private final Object needsBuildingLock = new Object(); 
    70     private FAMListener famListener; 
    71     private final Log log = LogFactory.getLog(getClass().getName()); 
    7267 
    7368    public KauriJaxRsRestlet(Context context, KauriModule module) { 
     
    7671        this.beanFactory = ((Module)module.getImplementation()).getApplicationContext().getAutowireCapableBeanFactory(); 
    7772        moduleSource = module.getSource(); 
     73        reloadableResource = new ReloadableResource(moduleSource, groovyPath, new JaxRsRestletBuilder(), new FAMListener(), "JAX-RS routing"); 
    7874 
    7975        // Make sure RuntimeDelegate is searched for now, while the context classloader is appropriately set 
     
    8177    } 
    8278 
    83     /** 
    84      * Some initialization which should be done only once but can't be done in 
    85      * the constructor. 
    86      */ 
    87     private void init() { 
    88         if (initialized) 
    89             return; 
    90         initialized = true; 
    91  
    92         if (moduleSource.getFAM() != null && groovyPath != null) { 
    93             famListener = new FAMListener(); 
    94             moduleSource.addResourceListener(groovyPath, famListener); 
    95         } 
     79    public void dispose() { 
     80        reloadableResource.disposeReloading(); 
    9681    } 
    9782 
     
    9984     * Builds or rebuilds the JAX-RS router, rescanning classes etc. 
    10085     */ 
    101     private synchronized void build() { 
    102         synchronized(needsBuildingLock) { 
    103             if (!needsBuilding) 
    104                 return; 
    105             needsBuilding = false; 
    106         } 
    107  
     86    private ReloadableResource.Result build() { 
    10887        log.debug("Rebuilding routing setup."); 
    109         init(); 
    11088        try { 
    11189            final Set<Class<?>> resourceClasses = new HashSet<Class<?>>(); 
     
    132110            } 
    133111 
    134             router = jrr; 
     112            return new ReloadableResource.Result(jrr); 
    135113        } catch (Throwable t) { 
    136             router = new Restlet() { 
     114            Restlet restlet = new Restlet() { 
    137115                public void handle(Request request, Response response) { 
    138116                    throw new RuntimeException("The JAX-RS request routing was not succesfully initialized."); 
    139117                } 
    140118            }; 
    141             throw new RuntimeException("Could not initialize JAX-RS router.", t); 
     119            return new ReloadableResource.Result(restlet, t); 
    142120        } 
    143121    } 
     
    145123    @Override 
    146124    public void handle(Request request, Response response) { 
    147         build(); 
    148         router.handle(request, response); 
     125        ((Restlet)reloadableResource.build()).handle(request, response); 
    149126    } 
    150127 
     
    423400    } 
    424401 
    425     private class FAMListener implements FilesystemAlterationListener { 
    426         private boolean changes; 
    427  
    428         public void onStart(FilesystemAlterationObserver filesystemAlterationObserver) { 
    429             changes = false; 
    430         } 
    431  
    432         public void onFileCreate(File file) { 
    433             if (file.getName().endsWith(GROOVY_EXT) || file.getName().endsWith(CLASS_EXT)) 
    434                 changes = true; 
    435         } 
    436  
    437         public void onFileChange(File file) { 
    438             if (file.getName().endsWith(GROOVY_EXT) || file.getName().endsWith(CLASS_EXT)) 
    439                 changes = true; 
    440         } 
    441  
    442         public void onFileDelete(File file) { 
    443             if (file.getName().endsWith(GROOVY_EXT) || file.getName().endsWith(CLASS_EXT)) 
    444                 changes = true; 
    445         } 
    446  
    447         public void onDirectoryCreate(File file) { 
    448         } 
    449  
    450         public void onDirectoryChange(File file) { 
    451         } 
    452  
    453         public void onDirectoryDelete(File file) { 
    454             changes = true; 
    455         } 
    456  
    457         public void onStop(FilesystemAlterationObserver filesystemAlterationObserver) { 
    458             if (changes) { 
    459                 synchronized (needsBuildingLock) { 
    460                     needsBuilding = true; 
    461                 } 
    462             } 
    463         } 
    464     } 
    465  
    466     public void dispose() { 
    467         if (famListener != null) 
    468             moduleSource.getFAM().removeListener(famListener); 
    469     } 
     402    private class FAMListener implements ReloadableResource.FAMListener { 
     403        public boolean onFileCreate(File file) { 
     404            return file.getName().endsWith(GROOVY_EXT) || file.getName().endsWith(CLASS_EXT); 
     405        } 
     406 
     407        public boolean onFileChange(File file) { 
     408            return file.getName().endsWith(GROOVY_EXT) || file.getName().endsWith(CLASS_EXT); 
     409        } 
     410 
     411        public boolean onFileDelete(File file) { 
     412            return file.getName().endsWith(GROOVY_EXT) || file.getName().endsWith(CLASS_EXT); 
     413        } 
     414 
     415        public boolean onDirectoryCreate(File file) { 
     416            return false; 
     417        } 
     418 
     419        public boolean onDirectoryChange(File file) { 
     420            return false; 
     421        } 
     422 
     423        public boolean onDirectoryDelete(File file) { 
     424            return true; 
     425        } 
     426    } 
     427 
     428    private class JaxRsRestletBuilder implements ReloadableResource.ResourceBuilder { 
     429        public ReloadableResource.Result build() { 
     430            return KauriJaxRsRestlet.this.build(); 
     431        } 
     432 
     433        public void dispose(Object object) { 
     434            // we don't need disposing 
     435        } 
     436    } 
     437 
    470438 
    471439    public void addResourceClass(String className) { 
  • trunk/modules/kauri-routing/kauri-routing-impl/src/main/java/org/kauriproject/routing/impl/components/ReloadableResource.java

    r674 r676  
    99import java.io.File; 
    1010 
     11/** 
     12 * This class encapsulates the rebuild-when-files-change pattern employed 
     13 * by multiple routing components. 
     14 * 
     15 * <p>Besides the normal rebuild-on-change, it also supports disposing 
     16 * the previously built object (if any) and putting in action a stub 
     17 * object in case an exception occurs. 
     18 */ 
    1119public  class ReloadableResource { 
    1220    private boolean needsBuilding = true; 
    1321    private final Object needsBuildingLock = new Object(); 
     22    private FAMListener famListenerDelegate; 
    1423    private FAMListenerWrapper famListener; 
    1524    private ModuleSource moduleSource; 
     
    2534        this.resourceBuilder = resourceBuilder; 
    2635        this.path = path; 
     36        this.famListenerDelegate = famListener; 
    2737        this.name = name; 
     38    } 
     39 
     40    private void init() { 
    2841        if (moduleSource.getFAM() != null && path != null) { 
    29             this.famListener = new FAMListenerWrapper(famListener); 
     42            this.famListener = new FAMListenerWrapper(famListenerDelegate); 
    3043            moduleSource.addResourceListener(path, this.famListener); 
    3144        } 
    3245    } 
    3346 
    34     public Object build() { 
     47    public synchronized Object build() { 
    3548        synchronized(needsBuildingLock) { 
    3649            if (!needsBuilding) 
     
    3952        } 
    4053 
     54        init(); 
     55 
    4156        if (log.isDebugEnabled()) { 
    4257            log.debug("Will rebuild " + name + " due to changes on path " + path); 
    4358        } 
    4459 
    45         buildObject = resourceBuilder.build(); 
    46         return buildObject; 
     60        Object oldBuildObject = buildObject; 
     61 
     62        try { 
     63            Result result; 
     64            try { 
     65                result = resourceBuilder.build(); 
     66            } catch (Throwable t) { 
     67                throw new RuntimeException("Could not initialize " + name, t); 
     68            } 
     69 
     70            this.buildObject = result.object; 
     71 
     72            if (result.throwable != null) 
     73                throw new RuntimeException("Could not initialize " + name, result.throwable); 
     74 
     75            return buildObject; 
     76        } finally { 
     77            if (oldBuildObject != null) 
     78                resourceBuilder.dispose(oldBuildObject); 
     79        } 
    4780    } 
    4881 
    4982    public void disposeReloading() { 
    5083        if (famListener != null) 
    51             moduleSource.getFAM().removeListener(famListener);         
     84            moduleSource.getFAM().removeListener(famListener); 
    5285    } 
    5386 
    5487    public static interface ResourceBuilder { 
    55         Object build(); 
     88        Result build(); 
     89 
     90        void dispose(Object object); 
     91    } 
     92 
     93    public static class Result { 
     94        private Object object; 
     95        private Throwable throwable; 
     96 
     97        public Result(Object object) { 
     98            this.object = object; 
     99        } 
     100 
     101        /** 
     102         * For the current call, the throwable will be thrown, but for next calls 
     103         * (until a new rebuild happens), the supplied object will be used. 
     104         */ 
     105        public Result(Object object, Throwable throwable) { 
     106            this.object = object; 
     107            this.throwable = throwable; 
     108        } 
    56109    } 
    57110 
  • trunk/modules/kauri-routing/kauri-routing-impl/src/main/java/org/kauriproject/routing/impl/routing/KauriRouter.java

    r651 r676  
    99import org.kauriproject.runtime.module.Module; 
    1010import org.kauriproject.routing.impl.groovybuild.GroovyKauriRouterBuilder; 
     11import org.kauriproject.routing.impl.components.ReloadableResource; 
    1112import org.kauriproject.routing.RoutingConfig; 
    1213import org.apache.commons.logging.Log; 
    1314import org.apache.commons.logging.LogFactory; 
    14 import org.apache.commons.jci.monitor.FilesystemAlterationListener; 
    15 import org.apache.commons.jci.monitor.FilesystemAlterationObserver; 
    1615import org.springframework.context.ApplicationContext; 
    1716 
     
    2120 
    2221public class KauriRouter extends Restlet { 
    23     private final Log log = LogFactory.getLog(getClass().getName()); 
    24     private RouterData routerData; 
     22    private final Log log = LogFactory.getLog(getClass()); 
    2523    private Context context; 
    2624    private RoutingConfig routingConfig; 
     
    2826    private ModuleSource moduleSource; 
    2927    private ApplicationContext applicationContext; 
    30     private FAMListener famListener; 
    31     private boolean needsBuilding = true; 
    32     private final Object needsBuildingLock = new Object(); 
     28    private ReloadableResource reloadableResource; 
    3329 
    3430    public KauriRouter(Context context, KauriModule module, RoutingConfig routingConfig) { 
     
    4339        moduleSource = module.getSource(); 
    4440 
    45         if (moduleSource.getFAM() != null) { 
    46             famListener = new FAMListener(); 
    47             // TODO only listen to needed things 
    48             // TODO think about removing listeners 
    49             moduleSource.addResourceListener(routingConfig.getConfigPath(), famListener); 
    50         } 
     41        this.reloadableResource = new ReloadableResource(moduleSource, routingConfig.getConfigPath(), new RouterBuilder(), new FAMListener(), "router"); 
    5142    } 
    5243 
    5344    public void handle(Request request, Response response) { 
    54         build(); 
    55         routerData.router.handle(request, response); 
     45        ((RouterData)reloadableResource.build()).router.handle(request, response); 
    5646    } 
    5747 
    58     private synchronized void build() { 
    59         synchronized(needsBuildingLock) { 
    60             if (!needsBuilding) 
    61                 return; 
    62             needsBuilding = false; 
    63         } 
    64  
    65         RouterData currentRouterData = routerData; 
     48    private ReloadableResource.Result build() { 
    6649        try { 
    6750            log.debug("Rebuilding routing setup."); 
     
    8164                    buildContext, module, applicationContext, log); 
    8265 
    83             routerData = new RouterData(routingRestlet, disposables); 
     66            return new ReloadableResource.Result(new RouterData(routingRestlet, disposables)); 
    8467        } catch (Throwable t) { 
    8568            // Set the router to a fixed config, so that we don't try 
    8669            // to consistantly rebuild it 
    87             routerData = new RouterData(new Restlet() { 
     70            RouterData routerData = new RouterData(new Restlet() { 
    8871                public void handle(Request request, Response response) { 
    8972                    throw new RuntimeException("The request routing was not succesfully initialized."); 
    9073                } 
    9174            }, Collections.<DisposableRoutingComponent>emptyList()); 
    92             throw new RuntimeException("Could not initialize router.", t); 
    93         } finally { 
    94             if (currentRouterData != null) 
    95                 currentRouterData.dispose(); 
     75            return new ReloadableResource.Result(routerData, t); 
    9676        } 
    9777    } 
    9878 
    99     private class FAMListener implements FilesystemAlterationListener { 
    100         private boolean changes; 
    101  
    102         public void onStart(FilesystemAlterationObserver filesystemAlterationObserver) { 
    103             changes = false; 
     79    private class RouterBuilder implements ReloadableResource.ResourceBuilder { 
     80        public ReloadableResource.Result build() { 
     81            return KauriRouter.this.build(); 
    10482        } 
    10583 
    106         public void onFileCreate(File file) { 
    107             changes = true; 
     84        public void dispose(Object object) { 
     85            ((RouterData)object).dispose(); 
     86        } 
     87    } 
     88 
     89    private class FAMListener implements ReloadableResource.FAMListener { 
     90        public boolean onFileCreate(File file) { 
     91            return true; 
    10892        } 
    10993 
    110         public void onFileChange(File file) { 
    111             changes = true; 
     94        public boolean onFileChange(File file) { 
     95            return true; 
    11296        } 
    11397 
    114         public void onFileDelete(File file) { 
    115             changes = true; 
     98        public boolean onFileDelete(File file) { 
     99            return true; 
    116100        } 
    117101 
    118         public void onDirectoryCreate(File file) { 
     102        public boolean onDirectoryCreate(File file) { 
     103            return false; 
    119104        } 
    120105 
    121         public void onDirectoryChange(File file) { 
     106        public boolean onDirectoryChange(File file) { 
     107            return false; 
    122108        } 
    123109 
    124         public void onDirectoryDelete(File file) { 
    125             changes = true; 
    126         } 
    127  
    128         public void onStop(FilesystemAlterationObserver filesystemAlterationObserver) { 
    129             if (changes) { 
    130                 synchronized (needsBuildingLock) { 
    131                     needsBuilding = true; 
    132                 } 
    133             } 
     110        public boolean onDirectoryDelete(File file) { 
     111            return true; 
    134112        } 
    135113    } 
     
    157135    @PreDestroy 
    158136    public void destroy() { 
    159         if (famListener != null) 
    160             moduleSource.getFAM().removeListener(famListener); 
     137        reloadableResource.disposeReloading(); 
    161138    } 
    162139} 
Note: See TracChangeset for help on using the changeset viewer.