    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.jci.listeners;
    019
    020    import java.io.File;
    021    import java.util.ArrayList;
    022    import java.util.Collection;
    023
    024    import org.apache.commons.jci.compilers.CompilationResult;
    025    import org.apache.commons.jci.compilers.JavaCompiler;
    026    import org.apache.commons.jci.compilers.JavaCompilerFactory;
    027    import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
    028    import org.apache.commons.jci.problems.CompilationProblem;
    029    import org.apache.commons.jci.readers.FileResourceReader;
    030    import org.apache.commons.jci.readers.ResourceReader;
    031    import org.apache.commons.jci.stores.MemoryResourceStore;
    032    import org.apache.commons.jci.stores.ResourceStore;
    033    import org.apache.commons.jci.stores.TransactionalResourceStore;
    034    import org.apache.commons.jci.utils.ConversionUtils;
    035    import org.apache.commons.logging.Log;
    036    import org.apache.commons.logging.LogFactory;
    037
    038    /**
    039     * A CompilingListener is an improved version of the ReloadingListener.
    040     * It even compiles the classes from source before doing the reloading.
    041     *
    042     * @author tcurdt
    043     */
    044    public class CompilingListener extends ReloadingListener {
    045
    046        private final Log log = LogFactory.getLog(CompilingListener.class);
    047
    048        private final JavaCompiler compiler;
    049        private final TransactionalResourceStore transactionalStore;
    050        private ResourceReader reader;
    051        private CompilationResult lastResult;
    052
    053        public CompilingListener() {
    054            this(new JavaCompilerFactory().createCompiler("eclipse"));
    055        }
    056
    057        public CompilingListener( final JavaCompiler pCompiler ) {
    058            this(pCompiler, new TransactionalResourceStore(new MemoryResourceStore()));
    059        }
    060
    061        public CompilingListener( final JavaCompiler pCompiler, final TransactionalResourceStore pTransactionalStore ) {
    062            super(pTransactionalStore);
    063            compiler = pCompiler;
    064            transactionalStore = pTransactionalStore;
    065            lastResult = null;
    066        }
    067
    068        public JavaCompiler getCompiler() {
    069            return compiler;
    070        }
    071
    072        public String getSourceFileExtension() {
    073            return ".java";
    074        }
    075
    076        public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
    077            return new FileResourceReader(pObserver.getRootDirectory());
    078        }
    079
    080        public String getSourceNameFromFile( final FilesystemAlterationObserver pObserver, final File pFile ) {
    081            return ConversionUtils.stripExtension(ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), pFile))) + getSourceFileExtension();
    082        }
    083
    084        @Override
    085        public ResourceStore getStore() {
    086            return transactionalStore;
    087        }
    088
    089        public synchronized CompilationResult getCompilationResult() {
    090            return lastResult;
    091        }
    092
    093        @Override
    094        public void onStart( final FilesystemAlterationObserver pObserver ) {
    095            super.onStart(pObserver);
    096
    097            reader = getReader(pObserver);
    098
    099            transactionalStore.onStart();
    100        }
    101
    102        public String[] getResourcesToCompile( final FilesystemAlterationObserver pObserver ) {
    103            final Collection<File> created = getCreatedFiles();
    104            final Collection<File> changed = getChangedFiles();
    105
    106            final Collection<String> resourceNames = new ArrayList<String>();
    107
    108            for (File createdFile : created) {
    109                if (createdFile.getName().endsWith(getSourceFileExtension())) {
    110                    resourceNames.add(getSourceNameFromFile(pObserver, createdFile));
    111                }
    112            }
    113
    114            for (File changedFile : changed) {
    115                if (changedFile.getName().endsWith(getSourceFileExtension())) {
    116                    resourceNames.add(getSourceNameFromFile(pObserver, changedFile));
    117                }
    118            }
    119
    120            final String[] result = new String[resourceNames.size()];
    121            resourceNames.toArray(result);
    122            return result;
    123        }
    124
    125        @Override
    126        public boolean isReloadRequired( final FilesystemAlterationObserver pObserver ) {
    127            boolean reload = false;
    128
    129            final Collection<File> created = getCreatedFiles();
    130            final Collection<File> changed = getChangedFiles();
    131            final Collection<File> deleted = getDeletedFiles();
    132
    133            log.debug("created:" + created.size() + " changed:" + changed.size() + " deleted:" + deleted.size() + " resources");
    134
    135            if (deleted.size() > 0) {
    136                for (File deletedFile : deleted) {
    137                    final String resourceName = ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), deletedFile));
    138
    139                    if (resourceName.endsWith(getSourceFileExtension())) {
    140                        // if source resource got removed delete the corresponding class
    141                        transactionalStore.remove(ConversionUtils.stripExtension(resourceName) + ".class");
    142                    } else {
    143                        // ordinary resource to be removed
    144                        transactionalStore.remove(resourceName);
    145                    }
    146
    147                    // FIXME: does not remove nested classes
    148
    149                }
    150                reload = true;
    151            }
    152
    153            final String[] resourcesToCompile = getResourcesToCompile(pObserver);
    154
    155            if (resourcesToCompile.length > 0) {
    156
    157                log.debug(resourcesToCompile.length + " classes to compile");
    158
    159                final CompilationResult result = compiler.compile(resourcesToCompile, reader, transactionalStore);
    160
    161                synchronized(this) {
    162                    lastResult = result;
    163                }
    164
    165                final CompilationProblem[] errors = result.getErrors();
    166                final CompilationProblem[] warnings = result.getWarnings();
    167
    168                log.debug(errors.length + " errors, " + warnings.length + " warnings");
    169
    170                if (errors.length > 0) {
    171                    // FIXME: they need to be marked for re-compilation
    172                    // and then added as compileables again
    173                    for (int j = 0; j < resourcesToCompile.length; j++) {
    174                        transactionalStore.remove(resourcesToCompile[j]);
    175                    }
    176                }
    177
    178                reload = true;
    179            }
    180
    181            return reload;
    182        }
    183    }