Package org.apache.wicket.core.util.watch

Source Code of org.apache.wicket.core.util.watch.Nio2ModificationWatcher

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.core.util.watch;

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

import org.apache.wicket.Application;
import org.apache.wicket.ThreadContext;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.io.IOUtils;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.thread.ICode;
import org.apache.wicket.util.thread.Task;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.util.watch.ModificationWatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An extension of ModificationWatcher that removes the NotFound entries from
* the MarkupCache for newly created files.
*
* By default MarkupCache registers Markup.NO_MARKUP value for each requested but
* not found markup file. Later when the user creates the markup file the MarkupCache
* should be notified.
*
* @since 7.0.0
*/
public class Nio2ModificationWatcher extends ModificationWatcher
{
  private static final Logger LOG = LoggerFactory.getLogger(Nio2ModificationWatcher.class);

  private final WatchService watchService;
  private final Application application;

  /** the <code>Task</code> to run */
  private Task task;

  /**
   * Constructor.
   *
   * @param application
   *              The application that manages the caches
   * @param pollFrequency
   *              How often to check on <code>IModifiable</code>s
   */
  public Nio2ModificationWatcher(final Application application, Duration pollFrequency)
  {
    try
    {
      this.application = application;

      this.watchService = FileSystems.getDefault().newWatchService();
      registerWatchables(watchService);

      start(pollFrequency);

    } catch (IOException iox)
    {
      throw new WicketRuntimeException("Cannot get the watch service", iox);
    }
  }

  @Override
  public void start(final Duration pollFrequency)
  {
    // Construct task with the given polling frequency
    task = new Task("Wicket-ModificationWatcher-NIO2");

    task.run(pollFrequency, new ICode() {
      @Override
      public void run(final Logger log)
      {
        checkCreated(log);
        checkModified();
      }
    });
  }

  /**
   * Checks for newly created files and folders.
   * New folders are registered to be watched.
   * New files are removed from the MarkupCache because there could be
   * {@link org.apache.wicket.markup.Markup#NO_MARKUP} (Not Found) entries for them already.
   * @param log
   *              a logger that can be used to log the events
   */
  protected void checkCreated(Logger log)
  {
    WatchKey watchKey = watchService.poll();
    if (watchKey != null)
    {
      List<WatchEvent<?>> events = watchKey.pollEvents();
      for (WatchEvent<?> event : events)
      {
        WatchEvent.Kind<?> eventKind = event.kind();
        Path eventPath = (Path) event.context();

        if (eventKind == ENTRY_CREATE)
        {
          entryCreated(eventPath, log);
        }
        else if (eventKind == ENTRY_DELETE)
        {
          entryDeleted(eventPath, log);
        }
        else if (eventKind == ENTRY_MODIFY)
        {
          entryModified(eventPath, log);
        }
      }

      watchKey.reset();
    }
  }

  /**
   * A callback method called when a new Path entry is modified
   *
   * @param path
   *              the modified path
   * @param log
   *              a logger that can be used to log the events
   */
  protected void entryModified(Path path, Logger log)
  {
  }

  /**
   * A callback method called when a new Path entry is deleted
   *
   * @param path
   *              the deleted path
   * @param log
   *              a logger that can be used to log the events
   */
  protected void entryDeleted(Path path, Logger log)
  {
  }

  /**
   * A callback method called when a new Path entry is created
   *
   * @param path
   *              the new path entry
   * @param log
   *              a logger that can be used to log the events
   */
  protected void entryCreated(Path path, Logger log)
  {
    if (Files.isDirectory(path))
    {
      try
      {
        // a directory is created. register it for notifications
        register(path, watchService);
      } catch (IOException iox)
      {
        log.warn("Cannot register folder '" + path + "' to be watched.", iox);
      }
    }
    else
    {
      // A new file is created. We need to clear the NOT_FOUND entry that may have been added earlier.
      // MarkupCache keys are fully qualified URIs
      String absolutePath = path.toAbsolutePath().toFile().toURI().toString();

      try
      {
        ThreadContext.setApplication(application);
        application.getMarkupSettings()
            .getMarkupFactory().getMarkupCache().removeMarkup(absolutePath);
      } finally {
        ThreadContext.setApplication(null);
      }
    }
  }

  @Override
  public void destroy()
  {
    try
    {
      super.destroy();

      if (task != null)
      {
        task.interrupt();
      }
    } finally
    {
      IOUtils.closeQuietly(watchService);
    }
  }

  /**
   * Registers all classpath folder entries and their subfolders in the {@code #watchService}.
   *
   * @param watchService
   *      the watch service that will send the notifications
   * @throws IOException
   */
  private void registerWatchables(final WatchService watchService) throws IOException
  {
    String classpath = System.getProperty("java.class.path");

    String[] classPathEntries = Strings.split(classpath, File.pathSeparatorChar);
    for (String classPathEntry : classPathEntries)
    {
      if (classPathEntry.endsWith(".jar") == false)
      {
        Path folder = Paths.get(classPathEntry);
        if (Files.isDirectory(folder))
        {
          register(folder, watchService);

          Files.walkFileTree(folder, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
            {
              register(dir, watchService);
              return FileVisitResult.CONTINUE;
            }
          });
        }
      }
    }
  }
 
  private void register(final Path folder, final WatchService watchService) throws IOException
  {
    WatchEvent.Kind[] watchedKinds = getWatchedKinds(folder);
    LOG.debug("Registering folder '{}' to the watching service with kinds: {}", folder, watchedKinds);
    folder.register(watchService, watchedKinds);
  }

  /**
   * @param folder
   *          the folder that will be watched
   * @return an array of watch event kinds to use for the watching of the given folder
   */
  protected WatchEvent.Kind[] getWatchedKinds(Path folder)
  {
    return new WatchEvent.Kind[] {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY};
  }
}
TOP

Related Classes of org.apache.wicket.core.util.watch.Nio2ModificationWatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.