synchronized void load()

in core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java [122:259]


    synchronized void load() {
        if (classpath == null || isLoaded) return;

        if (classpath.getEntries() == null) {
            urls = new URL[0];
        } else {
            urls = new URL[classpath.getEntries().size()];
            for (int i=0; i<urls.length; i++) {
                try {
                    String u = classpath.getEntries().get(i);
                    if (u.startsWith("classpath:")) {
                        // special support for classpath: url's
                        // TODO put convenience in ResourceUtils for extracting to a normal url
                        // (or see below)
                        InputStream uin = ResourceUtils.create(this).getResourceFromUrl(u);
                        File f = Os.newTempFile("brooklyn-catalog-"+u, null);
                        FileOutputStream fout = new FileOutputStream(f);
                        try {
                            Streams.copy(uin, fout);
                        } finally {
                            Streams.closeQuietly(fout);
                            Streams.closeQuietly(uin);
                        }
                        u = f.toURI().toString();
                    }
                    urls[i] = new URL(u);
                    
                    // NOTE - item above potentially deleted earlier as written to a temp file (which some OS's will wipe, though usually not if in use);
                    //      - and potential disk leak above as we have no way to know when the temp file can be removed earlier than server shutdown;
                    // (but not a big deal as this is legacy code, superseded by bundle installation in almost all cases)
                    // a better way to handle this is to supply a stream handler (but URLConnection is a little bit hard to work with):
//                    urls[i] = new URL(null, classpath.getEntries().get(i)   // (handy construtor for reparsing urls, without splitting into uri first)
//                        , new URLStreamHandler() {
//                            @Override
//                            protected URLConnection openConnection(URL u) throws IOException {
//                                new ResourceUtils(null). ???
//                            }
//                        });
                } catch (Exception e) {
                    Exceptions.propagateIfFatal(e);
                    log.error("Error loading URL "+classpath.getEntries().get(i)+" in definition of catalog "+catalog+"; skipping definition");
                    throw Exceptions.propagate(e);
                }
            }
        }
        
        // prefix is supported (but not really used yet) --
        // seems to have _better_ URL-discovery with prefixes 
        // (might also offer regex ? but that is post-load filter as opposed to native optimisation)
        String prefix = null;

        if (scanMode==null || scanMode==CatalogScanningModes.NONE)
            return;
        
        Stopwatch timer = Stopwatch.createStarted();
        ReflectionScanner scanner = null;
        
        if (classpath.getEntries() == null || classpath.getEntries().isEmpty()) {
            // scan default classpath:
            ClassLoader baseCL = null;
            Iterable<URL> baseCP = null;
            if (catalog.mgmt instanceof ManagementContextInternal) {
                baseCL = ((ManagementContextInternal)catalog.mgmt).getBaseClassLoader();
                baseCP = ((ManagementContextInternal)catalog.mgmt).getBaseClassPathForScanning();
            }
            scanner = new ReflectionScanner(baseCP, prefix, baseCL, catalog.getRootClassLoader());
            if (scanner.getSubTypesOf(Entity.class).isEmpty()) {
                try {
                    ((ManagementContextInternal)catalog.mgmt).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
                    log.debug("Catalog scan of default classloader returned nothing; reverting to java.class.path");
                    baseCP = sanitizeCP(((ManagementContextInternal) catalog.mgmt).getBaseClassPathForScanning());
                    scanner = new ReflectionScanner(baseCP, prefix, baseCL, catalog.getRootClassLoader());
                } catch (Exception e) {
                    log.info("Catalog scan is empty, and unable to use java.class.path (base classpath is "+baseCP+"): "+e);
                    Exceptions.propagateIfFatal(e);
                }
            }
        } else {
            // scan specified jars:
            scanner = new ReflectionScanner(urls==null || urls.length==0 ? null : Arrays.asList(urls), prefix, getLocalClassLoader());
        }
        
        if (scanner!=null) {
            int count = 0, countApps = 0;
            if (scanMode==CatalogScanningModes.ANNOTATIONS) {
                log.warn("Deprecated scanning of @Catalog annotations; instead use catalog.bom");
                Set<Class<?>> catalogClasses = scanner.getTypesAnnotatedWith(Catalog.class);
                for (Class<?> c: catalogClasses) {
                    try {
                        CatalogItem<?,?> item = addCatalogEntry(c);
                        count++;
                        if (CatalogTemplateItemDto.class.isInstance(item)) countApps++;
                    } catch (Exception e) {
                        log.warn("Failed to add catalog entry for "+c+"; continuing scan...", e);
                    }
                }
            } else if (scanMode==CatalogScanningModes.TYPES) {
                Iterable<Class<?>> entities = this.excludeInvalidClasses(
                        Iterables.concat(scanner.getSubTypesOf(Entity.class),
                                // not sure why we have to look for sub-types of Application, 
                                // they should be picked up as sub-types of Entity, but in maven builds (only!)
                                // they are not -- i presume a bug in scanner
                                scanner.getSubTypesOf(Application.class)));
                for (Class<?> c: entities) {
                    if (Application.class.isAssignableFrom(c)) {
                        addCatalogEntry(new CatalogTemplateItemDto(), c);
                        countApps++;
                    } else {
                        addCatalogEntry(new CatalogEntityItemDto(), c);
                    }
                    count++;
                }
                Iterable<Class<? extends Policy>> policies = this.excludeInvalidClasses(scanner.getSubTypesOf(Policy.class));
                for (Class<?> c: policies) {
                    addCatalogEntry(new CatalogPolicyItemDto(), c);
                    count++;
                }

                Iterable<Class<? extends Enricher>> enrichers = this.excludeInvalidClasses(scanner.getSubTypesOf(Enricher.class));
                for (Class<?> c: enrichers) {
                    addCatalogEntry(new CatalogEnricherItemDto(), c);
                    count++;
                }
                
                Iterable<Class<? extends Location>> locations = this.excludeInvalidClasses(scanner.getSubTypesOf(Location.class));
                for (Class<?> c: locations) {
                    addCatalogEntry(new CatalogLocationItemDto(), c);
                    count++;
                }
            } else {
                throw new IllegalStateException("Unsupported catalog scan mode "+scanMode+" for "+this);
            }
            log.debug("Catalog '"+catalog.dto.name+"' classpath scan completed: loaded "+
                    count+" item"+Strings.s(count)+" ("+countApps+" app"+Strings.s(countApps)+") in "+Time.makeTimeStringRounded(timer));
        }
        
        isLoaded = true;
    }