# IGNITE-661: Implemented.
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/0755231c Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/0755231c Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/0755231c Branch: refs/heads/ignite-661 Commit: 0755231c19bb2416169300e4d1ad6165cf92cff4 Parents: 47c4b17 Author: vozerov-gridgain <voze...@gridgain.com> Authored: Thu Apr 9 13:27:57 2015 +0300 Committer: vozerov-gridgain <voze...@gridgain.com> Committed: Thu Apr 9 13:27:57 2015 +0300 ---------------------------------------------------------------------- .../spi/deployment/uri/UriDeploymentSpi.java | 109 ++--- .../uri/scanners/GridUriDeploymentScanner.java | 286 ----------- .../uri/scanners/UriDeploymentScanner.java | 47 ++ .../scanners/UriDeploymentScannerContext.java | 92 ++++ .../scanners/UriDeploymentScannerManager.java | 221 +++++++++ .../file/GridUriDeploymentFileScanner.java | 311 ------------ .../scanners/file/UriDeploymentFileScanner.java | 327 +++++++++++++ .../http/GridUriDeploymentHttpScanner.java | 423 ---------------- .../scanners/http/UriDeploymentHttpScanner.java | 478 +++++++++++++++++++ .../GridFileDeploymentUndeploySelfTest.java | 4 +- 10 files changed, 1222 insertions(+), 1076 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/UriDeploymentSpi.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/UriDeploymentSpi.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/UriDeploymentSpi.java index a301a39..cb089c3 100644 --- a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/UriDeploymentSpi.java +++ b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/UriDeploymentSpi.java @@ -161,12 +161,6 @@ import java.util.Map.*; * <th>Optional</th> * <th>Default</th> * </tr> - * <tr> - * <td>freq</td> - * <td>File directory scan frequency in milliseconds.</td> - * <td>Yes</td> - * <td>{@code 5000} ms specified in {@link #DFLT_DISK_SCAN_FREQUENCY DFLT_DISK_SCAN_FREQUENCY}.</td> - * </tr> * </table> * <h2 class="header">File URI Example</h2> * The following example will scan {@code 'c:/Program files/ignite/deployment'} @@ -192,12 +186,6 @@ import java.util.Map.*; * <th>Optional</th> * <th>Default</th> * </tr> - * <tr> - * <td>freq</td> - * <td>File directory scan frequency in milliseconds.</td> - * <td>Yes</td> - * <td>{@code 5000} ms specified in {@link #DFLT_DISK_SCAN_FREQUENCY DFLT_DISK_SCAN_FREQUENCY}.</td> - * </tr> * </table> * <h2 class="header">Classes URI Example</h2> * The following example will scan {@code 'c:/Program files/ignite/deployment'} @@ -280,12 +268,6 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, */ public static final String DFLT_DEPLOY_DIR = "deployment/file"; - /** Default scan frequency for {@code file://} and {@code classes://} protocols (value is {@code 5000}). */ - public static final int DFLT_DISK_SCAN_FREQUENCY = 5000; - - /** Default scan frequency for {@code http://} protocol (value is {@code 300000}). */ - public static final int DFLT_HTTP_SCAN_FREQUENCY = 300000; - /** Default task description file path and name (value is {@code META-INF/ignite.xml}). */ public static final String XML_DESCRIPTOR_PATH = "META-INF/ignite.xml"; @@ -312,15 +294,14 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, /** */ @SuppressWarnings({"CollectionDeclaredAsConcreteClass"}) - private final LinkedList<GridUriDeploymentUnitDescriptor> unitLoaders = - new LinkedList<>(); + private final LinkedList<GridUriDeploymentUnitDescriptor> unitLoaders = new LinkedList<>(); /** */ @SuppressWarnings({"TypeMayBeWeakened"}) private final LastTimeUnitDescriptorComparator unitComp = new LastTimeUnitDescriptorComparator(); - /** List of scanners. Every URI has it's own scanner. */ - private final Collection<GridUriDeploymentScanner> scanners = new ArrayList<>(); + /** List of scanner managers. Every URI has it's own manager. */ + private final Collection<UriDeploymentScannerManager> mgrs = new ArrayList<>(); /** Whether URIs should be encoded or not. */ private boolean encodeUri = true; @@ -342,6 +323,9 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, @SuppressWarnings("UnusedDeclaration") private boolean delayOnNewOrUpdatedFile; + /** Configured scanners. */ + private UriDeploymentScanner[] scanners; + /** * Sets absolute path to temporary directory which will be used by * deployment SPI to keep all deployed classes in. @@ -421,17 +405,36 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, this.lsnr = lsnr; } + /** + * Gets scanners. + * + * @return Scanners. + */ + public UriDeploymentScanner[] getScanners() { + return scanners; + } + + /** + * Sets scanners. + * + * @param scanners Scanners. + */ + @IgniteSpiConfiguration(optional = true) + public void setScanners(UriDeploymentScanner... scanners) { + this.scanners = scanners; + } + /** {@inheritDoc} */ @Override public void spiStop() throws IgniteSpiException { - for (GridUriDeploymentScanner scanner : scanners) - scanner.cancel(); + for (UriDeploymentScannerManager mgr : mgrs) + mgr.cancel(); - for (GridUriDeploymentScanner scanner : scanners) - scanner.join(); + for (UriDeploymentScannerManager mgr : mgrs) + mgr.join(); // Clear inner collections. uriEncodedList.clear(); - scanners.clear(); + mgrs.clear(); List<ClassLoader> tmpClsLdrs; @@ -545,9 +548,15 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, } }; - for (URI uri : uriEncodedList) { - String proto = uri.getScheme(); + // Set default scanners if none are configured. + if (scanners == null) { + scanners = new UriDeploymentScanner[2]; + + scanners[0] = new UriDeploymentFileScanner(); + scanners[1] = new UriDeploymentHttpScanner(); + } + for (URI uri : uriEncodedList) { File file = new File(deployTmpDirPath); long freq = -1; @@ -559,29 +568,23 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, U.error(log, "Error parsing parameter value for frequency.", e); } - assert proto != null; - - GridUriDeploymentScanner scanner; + UriDeploymentScannerManager mgr = null; - switch (proto) { - case "file": - scanner = new GridUriDeploymentFileScanner(gridName, uri, file, freq > 0 ? freq : - DFLT_DISK_SCAN_FREQUENCY, filter, lsnr, log); - break; + for (UriDeploymentScanner scanner : scanners) { + if (scanner.acceptsURI(uri)) { + mgr = new UriDeploymentScannerManager(gridName, uri, file, freq > 0 ? freq : + scanner.getDefaultScanFrequency(), filter, lsnr, log, scanner); - case "http": - case "https": - scanner = new GridUriDeploymentHttpScanner(gridName, uri, file, freq > 0 ? freq : - DFLT_HTTP_SCAN_FREQUENCY, filter, lsnr, log); break; - - default: - throw new IgniteSpiException("Unsupported protocol: " + proto); + } } - scanners.add(scanner); + if (mgr == null) + throw new IgniteSpiException("Unsupported URI (please configure appropriate scanner): " + uri); + + mgrs.add(mgr); - scanner.start(); + mgr.start(); } // Ack parameters. @@ -589,7 +592,7 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, log.debug(configInfo("tmpDirPath", tmpDirPath)); log.debug(configInfo("uriList", uriList)); log.debug(configInfo("encodeUri", encodeUri)); - log.debug(configInfo("scanners", scanners)); + log.debug(configInfo("scanners", mgrs)); } // Ack ok start. @@ -615,7 +618,7 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, if (userInfo != null) { String[] arr = userInfo.split(";"); - if (arr != null && arr.length > 0) + if (arr.length > 0) for (String el : arr) if (el.startsWith("freq=")) return Long.parseLong(el.substring(5)); @@ -629,7 +632,7 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, @Override public DeploymentResource findResource(String rsrcName) { assert rsrcName != null; - // Wait until all scanners finish their first scanning. + // Wait until all scanner managers finish their first scanning. try { synchronized (mux) { while (!isFirstScanFinished(firstScanCntr)) @@ -637,7 +640,7 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, } } catch (InterruptedException e) { - U.error(log, "Failed to wait while all scanners finish their first scanning.", e); + U.error(log, "Failed to wait while all scanner managers finish their first scanning.", e); Thread.currentThread().interrupt(); @@ -945,8 +948,6 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, for (String uri : uriList) { assertParameter(uri != null, "uriList.get(X) != null"); - assert uri != null; - String encUri = encodeUri(uri.replaceAll("\\\\", "/")); URI uriObj; @@ -969,7 +970,7 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, } /** - * Add configuration for file scanner {@link GridUriDeploymentFileScanner}. + * Add configuration for file scanner. * * @throws org.apache.ignite.spi.IgniteSpiException Thrown if default URI syntax is incorrect. */ @@ -1075,7 +1076,7 @@ public class UriDeploymentSpi extends IgniteSpiAdapter implements DeploymentSpi, GridUriDeploymentUnitDescriptor desc = iter.next(); assert !newDesc.getClassLoader().equals(desc.getClassLoader()) : - "Uri scanners always create new class loader for every GAR file: " + newDesc; + "URI scanners always create new class loader for every GAR file: " + newDesc; // Only for GAR files. Undeploy all for overwritten GAR files. if (desc.getType() == GridUriDeploymentUnitDescriptor.Type.FILE && http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/GridUriDeploymentScanner.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/GridUriDeploymentScanner.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/GridUriDeploymentScanner.java deleted file mode 100644 index e20d156..0000000 --- a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/GridUriDeploymentScanner.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * 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.ignite.spi.deployment.uri.scanners; - -import org.apache.ignite.*; -import org.apache.ignite.internal.util.tostring.*; -import org.apache.ignite.internal.util.typedef.internal.*; -import org.apache.ignite.spi.*; - -import java.io.*; -import java.net.*; - -/** - * Base deployment scanner implementation. It simplifies scanner implementation - * by providing loggers, executors and file names parsing methods. - */ -public abstract class GridUriDeploymentScanner { - /** Grid name. */ - private final String gridName; - - /** URI that scanner should looks after. */ - @GridToStringExclude - private final URI uri; - - /** Temporary deployment directory. */ - private final File deployDir; - - /** Scan frequency. */ - private final long freq; - - /** Found files filter. */ - private final FilenameFilter filter; - - /** Scanner listener which should be notified about changes. */ - private final GridUriDeploymentScannerListener lsnr; - - /** Logger. */ - private final IgniteLogger log; - - /** Scanner implementation. */ - private IgniteSpiThread scanner; - - /** Whether first scan completed or not. */ - private boolean firstScan = true; - - /** - * Scans URI for new, updated or deleted files. - */ - protected abstract void process(); - - /** - * Creates new scanner. - * - * @param gridName Grid name. - * @param uri URI which scanner should looks after. - * @param deployDir Temporary deployment directory. - * @param freq Scan frequency. - * @param filter Found files filter. - * @param lsnr Scanner listener which should be notifier about changes. - * @param log Logger. - */ - protected GridUriDeploymentScanner( - String gridName, - URI uri, - File deployDir, - long freq, - FilenameFilter filter, - GridUriDeploymentScannerListener lsnr, - IgniteLogger log) { - assert uri != null; - assert freq > 0; - assert deployDir != null; - assert filter != null; - assert log != null; - assert lsnr != null; - - this.gridName = gridName; - this.uri = uri; - this.deployDir = deployDir; - this.freq = freq; - this.filter = filter; - this.log = log.getLogger(getClass()); - this.lsnr = lsnr; - } - - /** - * Starts scanner. - */ - public void start() { - scanner = new IgniteSpiThread(gridName, "grid-uri-scanner", log) { - /** {@inheritDoc} */ - @SuppressWarnings({"BusyWait"}) - @Override protected void body() throws InterruptedException { - try { - while (!isInterrupted()) { - try { - process(); - } - finally { - // Do it in finally to avoid any hanging. - if (firstScan) { - firstScan = false; - - lsnr.onFirstScanFinished(); - } - } - - Thread.sleep(freq); - } - } - finally { - // Double check. If we were cancelled before anything has been scanned. - if (firstScan) { - firstScan = false; - - lsnr.onFirstScanFinished(); - } - } - } - }; - - scanner.start(); - - if (log.isDebugEnabled()) - log.debug("Grid URI deployment scanner started: " + this); - } - - /** - * Cancels scanner execution. - */ - public void cancel() { - U.interrupt(scanner); - } - - /** - * Joins scanner thread. - */ - public void join() { - U.join(scanner, log); - - if (log.isDebugEnabled()) - log.debug("Grid URI deployment scanner stopped: " + this); - } - - /** - * Tests whether scanner was cancelled before or not. - * - * @return {@code true} if scanner was cancelled and {@code false} - * otherwise. - */ - protected boolean isCancelled() { - assert scanner != null; - - return scanner.isInterrupted(); - } - - /** - * Creates temp file in temp directory. - * - * @param fileName File name. - * @param tmpDir dir to creating file. - * @return created file. - * @throws IOException if error occur. - */ - protected File createTempFile(String fileName, File tmpDir) throws IOException { - assert fileName != null; - - int idx = fileName.lastIndexOf('.'); - - if (idx == -1) - idx = fileName.length(); - - String prefix = fileName.substring(0, idx); - if (idx < 3) { // Prefix must be at least 3 characters long. See File.createTempFile(...). - prefix += "___"; - } - - String suffix = fileName.substring(idx); - - return File.createTempFile(prefix, suffix, tmpDir); - } - - /** - * Gets file URI for the given file name. It extends any given name with {@link #uri}. - * - * @param name File name. - * @return URI for the given file name. - */ - protected String getFileUri(String name) { - assert name != null; - - String fileUri = uri.toString(); - - fileUri = fileUri.length() > 0 && fileUri.charAt(fileUri.length() - 1) == '/' ? fileUri + name : - fileUri + '/' + name; - - return fileUri; - } - - /** - * Tests whether first scan completed or not. - * - * @return {@code true} if first scan has been already completed and - * {@code false} otherwise. - */ - protected boolean isFirstScan() { - return firstScan; - } - - /** - * Gets deployment URI. - * - * @return Deployment URI. - */ - protected final URI getUri() { - return uri; - } - - /** - * Gets deployment frequency. - * - * @return Deployment frequency. - */ - protected final long getFrequency() { - return freq; - } - - /** - * Gets temporary deployment directory. - * - * @return Temporary deployment directory. - */ - protected final File getDeployDirectory() { - return deployDir; - } - - /** - * Gets filter for found files. Before {@link #lsnr} is notified about - * changes with certain file last should be accepted by filter. - * - * @return New, updated or deleted file filter. - */ - protected final FilenameFilter getFilter() { - return filter; - } - - /** - * Gets deployment listener. - * - * @return Listener which should be notified about all deployment events - * by scanner. - */ - protected final GridUriDeploymentScannerListener getListener() { - return lsnr; - } - - /** - * Gets scanner logger. - * - * @return Logger. - */ - protected final IgniteLogger getLogger() { - return log; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(GridUriDeploymentScanner.class, this, - "uri", uri != null ? U.hidePassword(uri.toString()) : null); - } -} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScanner.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScanner.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScanner.java new file mode 100644 index 0000000..e33351c --- /dev/null +++ b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScanner.java @@ -0,0 +1,47 @@ +/* + * 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.ignite.spi.deployment.uri.scanners; + +import java.net.*; + +/** + * URI deployment scanner. + */ +public interface UriDeploymentScanner { + /** + * Check whether scanner is able to process the given URI. + * + * @param uri URI. + * @return {@code true} if scanner is able to process the URI. + */ + boolean acceptsURI(URI uri); + + /** + * Scan the given URI. + * + * @param scanCtx Scan context. + */ + void scan(UriDeploymentScannerContext scanCtx); + + /** + * Gets default scan frequency in milliseconds. + * + * @return Default scan frequency. + */ + long getDefaultScanFrequency(); +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerContext.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerContext.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerContext.java new file mode 100644 index 0000000..9867f0f --- /dev/null +++ b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerContext.java @@ -0,0 +1,92 @@ +/* + * 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.ignite.spi.deployment.uri.scanners; + +import org.apache.ignite.*; +import org.apache.ignite.spi.deployment.uri.scanners.*; + +import java.io.*; +import java.net.*; + +/** + * Deployment scanner context. + */ +public interface UriDeploymentScannerContext { + /** + * Creates temp file in temp directory. + * + * @param fileName File name. + * @param tmpDir dir to creating file. + * @return created file. + * @throws IOException if error occur. + */ + File createTempFile(String fileName, File tmpDir) throws IOException; + + /** + * Gets temporary deployment directory. + * + * @return Temporary deployment directory. + */ + File getDeployDirectory(); + + /** + * Gets filter for found files. Before listener is notified about + * changes with certain file last should be accepted by filter. + * + * @return New, updated or deleted file filter. + */ + FilenameFilter getFilter(); + + /** + * Gets deployment listener. + * + * @return Listener which should be notified about all deployment events + * by scanner. + */ + GridUriDeploymentScannerListener getListener(); + + /** + * Gets scanner logger. + * + * @return Logger. + */ + IgniteLogger getLogger(); + + /** + * Gets deployment URI. + * + * @return Deployment URI. + */ + URI getUri(); + + /** + * Tests whether scanner was cancelled before or not. + * + * @return {@code true} if scanner was cancelled and {@code false} + * otherwise. + */ + boolean isCancelled(); + + /** + * Tests whether first scan completed or not. + * + * @return {@code true} if first scan has been already completed and + * {@code false} otherwise. + */ + boolean isFirstScan(); +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerManager.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerManager.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerManager.java new file mode 100644 index 0000000..0d959d9 --- /dev/null +++ b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/UriDeploymentScannerManager.java @@ -0,0 +1,221 @@ +/* + * 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.ignite.spi.deployment.uri.scanners; + +import org.apache.ignite.*; +import org.apache.ignite.internal.util.tostring.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.spi.*; + +import java.io.*; +import java.net.*; + +/** + * URI deployment scanner manager. + */ +public class UriDeploymentScannerManager implements UriDeploymentScannerContext { + /** Grid name. */ + private final String gridName; + + /** URI that scanner should looks after. */ + @GridToStringExclude + private final URI uri; + + /** Temporary deployment directory. */ + private final File deployDir; + + /** Scan frequency. */ + private final long freq; + + /** Found files filter. */ + private final FilenameFilter filter; + + /** Scanner listener which should be notified about changes. */ + private final GridUriDeploymentScannerListener lsnr; + + /** Logger. */ + private final IgniteLogger log; + + /** Underlying scanner. */ + private final UriDeploymentScanner scanner; + + /** Scanner implementation. */ + private IgniteSpiThread scannerThread; + + /** Whether first scan completed or not. */ + private boolean firstScan = true; + + /** + * Creates new scanner. + * + * @param gridName Grid name. + * @param uri URI which scanner should looks after. + * @param deployDir Temporary deployment directory. + * @param freq Scan frequency. + * @param filter Found files filter. + * @param lsnr Scanner listener which should be notifier about changes. + * @param log Logger. + * @param scanner Scanner. + */ + public UriDeploymentScannerManager( + String gridName, + URI uri, + File deployDir, + long freq, + FilenameFilter filter, + GridUriDeploymentScannerListener lsnr, + IgniteLogger log, + UriDeploymentScanner scanner) { + assert uri != null; + assert freq > 0; + assert deployDir != null; + assert filter != null; + assert log != null; + assert lsnr != null; + assert scanner != null; + + this.gridName = gridName; + this.uri = uri; + this.deployDir = deployDir; + this.freq = freq; + this.filter = filter; + this.log = log.getLogger(getClass()); + this.lsnr = lsnr; + this.scanner = scanner; + } + + /** + * Starts scanner. + */ + public void start() { + scannerThread = new IgniteSpiThread(gridName, "grid-uri-scanner", log) { + /** {@inheritDoc} */ + @SuppressWarnings({"BusyWait"}) + @Override protected void body() throws InterruptedException { + try { + while (!isInterrupted()) { + try { + scanner.scan(UriDeploymentScannerManager.this); + } + finally { + // Do it in finally to avoid any hanging. + if (firstScan) { + firstScan = false; + + lsnr.onFirstScanFinished(); + } + } + + Thread.sleep(freq); + } + } + finally { + // Double check. If we were cancelled before anything has been scanned. + if (firstScan) { + firstScan = false; + + lsnr.onFirstScanFinished(); + } + } + } + }; + + scannerThread.start(); + + if (log.isDebugEnabled()) + log.debug("Grid URI deployment scanner started: " + this); + } + + /** + * Cancels scanner execution. + */ + public void cancel() { + U.interrupt(scannerThread); + } + + /** + * Joins scanner thread. + */ + public void join() { + U.join(scannerThread, log); + + if (log.isDebugEnabled()) + log.debug("Grid URI deployment scanner stopped: " + this); + } + + /** {@inheritDoc} */ + public boolean isCancelled() { + assert scannerThread != null; + + return scannerThread.isInterrupted(); + } + + /** {@inheritDoc} */ + public File createTempFile(String fileName, File tmpDir) throws IOException { + assert fileName != null; + + int idx = fileName.lastIndexOf('.'); + + if (idx == -1) + idx = fileName.length(); + + String prefix = fileName.substring(0, idx); + if (idx < 3) { // Prefix must be at least 3 characters long. See File.createTempFile(...). + prefix += "___"; + } + + String suffix = fileName.substring(idx); + + return File.createTempFile(prefix, suffix, tmpDir); + } + + /** {@inheritDoc} */ + public boolean isFirstScan() { + return firstScan; + } + + /** {@inheritDoc} */ + public URI getUri() { + return uri; + } + + /** {@inheritDoc} */ + public File getDeployDirectory() { + return deployDir; + } + + /** {@inheritDoc} */ + public FilenameFilter getFilter() { + return filter; + } + + /** {@inheritDoc} */ + public GridUriDeploymentScannerListener getListener() { + return lsnr; + } + + /** {@inheritDoc} */ + public IgniteLogger getLogger() { + return log; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(UriDeploymentScannerManager.class, this, "uri", U.hidePassword(uri.toString())); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/GridUriDeploymentFileScanner.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/GridUriDeploymentFileScanner.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/GridUriDeploymentFileScanner.java deleted file mode 100644 index fc81c13..0000000 --- a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/GridUriDeploymentFileScanner.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * 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.ignite.spi.deployment.uri.scanners.file; - -import org.apache.ignite.*; -import org.apache.ignite.internal.util.lang.*; -import org.apache.ignite.internal.util.typedef.*; -import org.apache.ignite.internal.util.typedef.internal.*; -import org.apache.ignite.spi.*; -import org.apache.ignite.spi.deployment.uri.scanners.*; - -import java.io.*; -import java.net.*; -import java.util.*; - -/** - * Scanner that processes all URIs with "file" scheme. Usually URI point to - * certain directory or file and scanner is in charge of watching all changes - * (file deletion, creation and so on) and sending notification to the listener - * about every change. - */ -public class GridUriDeploymentFileScanner extends GridUriDeploymentScanner { - /** Scanning directory or file. */ - private File scanDir; - - /** Cache of found GAR-files or GAR-directories to check if any of it has been updated. */ - private Map<File, Long> tstampCache = new HashMap<>(); - - /** Cache of found files in GAR-folder to check if any of it has been updated. */ - private Map<File, Map<File, Long>> garDirFilesTstampCache = new HashMap<>(); - - /** */ - private FileFilter garFilter; - - /** */ - private FileFilter garDirFilesFilter; - - /** - * Creates new instance of scanner with given name. - * - * @param gridName Grid name. - * @param uri URI which scanner should look after. - * @param deployDir Temporary deployment directory. - * @param freq Scan frequency. - * @param filter Found files filter. - * @param lsnr Scanner listener which should be notifier about changes. - * @param log Logger. - * @throws org.apache.ignite.spi.IgniteSpiException Thrown if URI is {@code null} or is not a - * directory. - */ - public GridUriDeploymentFileScanner( - String gridName, - URI uri, - File deployDir, - long freq, - FilenameFilter filter, - GridUriDeploymentScannerListener lsnr, - IgniteLogger log) throws IgniteSpiException { - super(gridName, uri, deployDir, freq, filter, lsnr, log); - - initialize(uri); - } - - /** - * Initializes scanner by parsing given URI and extracting scanning - * directory path and creating file filters. - * - * @param uri Scanning URI with "file" scheme. - * @throws org.apache.ignite.spi.IgniteSpiException Thrown if URI is {@code null} or is not a - * directory. - */ - private void initialize(URI uri) throws IgniteSpiException { - assert "file".equals(getUri().getScheme()); - - String scanDirPath = uri.getPath(); - - if (scanDirPath != null) - scanDir = new File(scanDirPath); - - if (scanDir == null || !scanDir.isDirectory()) { - scanDir = null; - - throw new IgniteSpiException("URI is either not provided or is not a directory: " + - U.hidePassword(uri.toString())); - } - - garFilter = new FileFilter() { - /** {@inheritDoc} */ - @Override public boolean accept(File pathname) { - return getFilter().accept(null, pathname.getName()); - } - }; - - garDirFilesFilter = new FileFilter() { - /** {@inheritDoc} */ - @Override public boolean accept(File pathname) { - // Allow all files in GAR-directory. - return pathname.isFile(); - } - }; - } - - /** - * Handles changes in scanning directory by tracking files modification date. - * Checks files modification date against those one that was collected before - * and notifies listener about every changed or deleted file. - */ - @Override protected void process() { - final Set<File> foundFiles = isFirstScan() ? new HashSet<File>() : U.<File>newHashSet(tstampCache.size()); - - GridDeploymentFileHandler hnd = new GridDeploymentFileHandler() { - /** {@inheritDoc} */ - @Override public void handle(File file) { - foundFiles.add(file); - - handleFile(file); - } - }; - - // Scan directory for deploy units. - GridDeploymentFolderScannerHelper.scanFolder(scanDir, garFilter, hnd); - - // Print warning if no GAR-units found first time. - if (isFirstScan() && foundFiles.isEmpty()) - U.warn(getLogger(), "No GAR-units found in: " + U.hidePassword(getUri().toString())); - - if (!isFirstScan()) { - Collection<File> deletedFiles = new HashSet<>(tstampCache.keySet()); - - deletedFiles.removeAll(foundFiles); - - if (!deletedFiles.isEmpty()) { - List<String> uris = new ArrayList<>(); - - for (File file : deletedFiles) { - uris.add(getFileUri(file.getAbsolutePath())); - } - - // Clear cache. - tstampCache.keySet().removeAll(deletedFiles); - - garDirFilesTstampCache.keySet().removeAll(deletedFiles); - - getListener().onDeletedFiles(uris); - } - } - } - - /** - * Tests whether given directory or file was changed since last check and if so - * copies all directory sub-folders and files or file itself to the deployment - * directory and than notifies listener about new or updated files. - * - * @param file Scanning directory or file. - */ - private void handleFile(File file) { - boolean changed; - - Long lastMod; - - if (file.isDirectory()) { - GridTuple<Long> dirLastModified = F.t(file.lastModified()); - - changed = checkGarDirectoryChanged(file, dirLastModified); - - lastMod = dirLastModified.get(); - } - else { - lastMod = tstampCache.get(file); - - changed = lastMod == null || lastMod != file.lastModified(); - - lastMod = file.lastModified(); - } - - // If file is new or has been modified. - if (changed) { - tstampCache.put(file, lastMod); - - if (getLogger().isDebugEnabled()) - getLogger().debug("Discovered deployment file or directory: " + file); - - String fileName = file.getName(); - - try { - File cpFile = createTempFile(fileName, getDeployDirectory()); - - // Delete file when JVM stopped. - cpFile.deleteOnExit(); - - if (file.isDirectory()) { - cpFile = new File(cpFile.getParent(), "dir_" + cpFile.getName()); - - // Delete directory when JVM stopped. - cpFile.deleteOnExit(); - } - - // Copy file to deploy directory. - U.copy(file, cpFile, true); - - String fileUri = getFileUri(file.getAbsolutePath()); - - getListener().onNewOrUpdatedFile(cpFile, fileUri, lastMod); - } - catch (IOException e) { - U.error(getLogger(), "Error saving file: " + fileName, e); - } - } - } - - /** - * Tests whether certain directory was changed since given modification date. - * It scans all directory files one by one and compares their modification - * dates with those ones that was collected before. - * <p> - * If at least one file was changed (has modification date after given one) - * whole directory is considered as modified. - * - * @param dir Scanning directory. - * @param lastModified Last calculated Directory modification date. - * @return {@code true} if directory was changed since last check and - * {@code false} otherwise. - */ - private boolean checkGarDirectoryChanged(File dir, final GridTuple<Long> lastModified) { - final Map<File, Long> clssTstampCache; - - boolean firstScan = false; - - if (!garDirFilesTstampCache.containsKey(dir)) { - firstScan = true; - - garDirFilesTstampCache.put(dir, clssTstampCache = new HashMap<>()); - } - else - clssTstampCache = garDirFilesTstampCache.get(dir); - - assert clssTstampCache != null; - - final GridTuple<Boolean> changed = F.t(false); - - final Set<File> foundFiles = firstScan ? new HashSet<File>() : U.<File>newHashSet(clssTstampCache.size()); - - GridDeploymentFileHandler hnd = new GridDeploymentFileHandler() { - @Override public void handle(File file) { - foundFiles.add(file); - - Long fileLastModified = clssTstampCache.get(file); - - if (fileLastModified == null || fileLastModified != file.lastModified()) { - clssTstampCache.put(file, fileLastModified = file.lastModified()); - - changed.set(true); - } - - // Calculate last modified file in folder. - if (fileLastModified > lastModified.get()) - lastModified.set(fileLastModified); - } - }; - - // Scan GAR-directory for changes. - GridDeploymentFolderScannerHelper.scanFolder(dir, garDirFilesFilter, hnd); - - // Clear cache for deleted files. - if (!firstScan && clssTstampCache.keySet().retainAll(foundFiles)) - changed.set(true); - - return changed.get(); - } - - /** - * Converts given file name to the URI with "file" scheme. - * - * @param name File name to be converted. - * @return File name with "file://" prefix. - */ - @Override protected String getFileUri(String name) { - assert name != null; - - name = name.replace("\\","/"); - - return "file://" + (name.charAt(0) == '/' ? "" : '/') + name; - } - - /** {@inheritDoc} */ - @Override public String toString() { - StringBuilder buf = new StringBuilder(); - - buf.append(getClass().getName()).append(" ["); - buf.append("scanDir=").append(scanDir); - buf.append(']'); - - return buf.toString(); - } -} - http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/UriDeploymentFileScanner.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/UriDeploymentFileScanner.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/UriDeploymentFileScanner.java new file mode 100644 index 0000000..2cbe38b --- /dev/null +++ b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/file/UriDeploymentFileScanner.java @@ -0,0 +1,327 @@ +/* + * 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.ignite.spi.deployment.uri.scanners.file; + +import org.apache.ignite.internal.util.lang.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.spi.*; +import org.apache.ignite.spi.deployment.uri.scanners.*; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * URI deployment file scanner. + */ +public class UriDeploymentFileScanner implements UriDeploymentScanner { + /** Default scan frequency. */ + public static final int DFLT_SCAN_FREQ = 5000; + + /** Per-URI contexts. */ + private final ConcurrentHashMap<URI, URIContext> uriCtxs = new ConcurrentHashMap<>(); + + /** {@inheritDoc} */ + @Override public boolean acceptsURI(URI uri) { + String proto = uri.getScheme().toLowerCase(); + + return "file".equals(proto); + } + + /** {@inheritDoc} */ + @Override public void scan(UriDeploymentScannerContext scanCtx) { + URI uri = scanCtx.getUri(); + + URIContext uriCtx = uriCtxs.get(uri); + + if (uriCtx == null) { + uriCtx = createUriContext(uri, scanCtx); + + URIContext oldUriCtx = uriCtxs.putIfAbsent(uri, uriCtx); + + if (oldUriCtx != null) + uriCtx = oldUriCtx; + } + + uriCtx.scan(scanCtx); + } + + /** {@inheritDoc} */ + @Override public long getDefaultScanFrequency() { + return DFLT_SCAN_FREQ; + } + + /** + * Create context for the given URI. + * + * @param uri URI. + * @param scanCtx Scanner context. + * @return URI context. + */ + private URIContext createUriContext(URI uri, final UriDeploymentScannerContext scanCtx) { + String scanDirPath = uri.getPath(); + + File scanDir = null; + + if (scanDirPath != null) + scanDir = new File(scanDirPath); + + if (scanDir == null || !scanDir.isDirectory()) + throw new IgniteSpiException("URI is either not provided or is not a directory: " + + U.hidePassword(uri.toString())); + + FileFilter garFilter = new FileFilter() { + /** {@inheritDoc} */ + @Override public boolean accept(File pathname) { + return scanCtx.getFilter().accept(null, pathname.getName()); + } + }; + + FileFilter garDirFilesFilter = new FileFilter() { + /** {@inheritDoc} */ + @Override public boolean accept(File pathname) { + // Allow all files in GAR-directory. + return pathname.isFile(); + } + }; + + return new URIContext(scanDir, garFilter, garDirFilesFilter); + } + + /** + * Converts given file name to the URI with "file" scheme. + * + * @param name File name to be converted. + * @return File name with "file://" prefix. + */ + private static String getFileUri(String name) { + assert name != null; + + name = name.replace("\\","/"); + + return "file://" + (name.charAt(0) == '/' ? "" : '/') + name; + } + + /** + * Context for the given URI. + */ + private static class URIContext { + /** Scanning directory or file. */ + private final File scanDir; + + /** GAR filter. */ + private final FileFilter garFilter; + + /** GAR directory files filter. */ + private final FileFilter garDirFilesFilter; + + /** Cache of found GAR-files or GAR-directories to check if any of it has been updated. */ + private final Map<File, Long> tstampCache = new HashMap<>(); + + /** Cache of found files in GAR-folder to check if any of it has been updated. */ + private final Map<File, Map<File, Long>> garDirFilesTstampCache = new HashMap<>(); + + /** + * Constructor. + * + * @param scanDir Scan directory. + * @param garFilter Gar filter. + * @param garDirFilesFilter GAR directory files filter. + */ + private URIContext(File scanDir, FileFilter garFilter, FileFilter garDirFilesFilter) { + this.scanDir = scanDir; + this.garFilter = garFilter; + this.garDirFilesFilter = garDirFilesFilter; + } + + /** + * Perform scan. + * + * @param scanCtx Scan context. + */ + private void scan(final UriDeploymentScannerContext scanCtx) { + final Set<File> foundFiles = scanCtx.isFirstScan() ? + new HashSet<File>() : U.<File>newHashSet(tstampCache.size()); + + GridDeploymentFileHandler hnd = new GridDeploymentFileHandler() { + /** {@inheritDoc} */ + @Override public void handle(File file) { + foundFiles.add(file); + + handleFile(file, scanCtx); + } + }; + + // Scan directory for deploy units. + GridDeploymentFolderScannerHelper.scanFolder(scanDir, garFilter, hnd); + + // Print warning if no GAR-units found first time. + if (scanCtx.isFirstScan() && foundFiles.isEmpty()) + U.warn(scanCtx.getLogger(), "No GAR-units found in: " + U.hidePassword(scanCtx.getUri().toString())); + + if (!scanCtx.isFirstScan()) { + Collection<File> deletedFiles = new HashSet<>(tstampCache.keySet()); + + deletedFiles.removeAll(foundFiles); + + if (!deletedFiles.isEmpty()) { + List<String> uris = new ArrayList<>(); + + for (File file : deletedFiles) { + uris.add(getFileUri(file.getAbsolutePath())); + } + + // Clear cache. + tstampCache.keySet().removeAll(deletedFiles); + + garDirFilesTstampCache.keySet().removeAll(deletedFiles); + + scanCtx.getListener().onDeletedFiles(uris); + } + } + } + + /** + * Tests whether given directory or file was changed since last check and if so + * copies all directory sub-folders and files or file itself to the deployment + * directory and than notifies listener about new or updated files. + * + * @param file Scanning directory or file. + * @param ctx Scanner context. + */ + private void handleFile(File file, UriDeploymentScannerContext ctx) { + boolean changed; + + Long lastMod; + + if (file.isDirectory()) { + GridTuple<Long> dirLastModified = F.t(file.lastModified()); + + changed = checkGarDirectoryChanged(file, dirLastModified); + + lastMod = dirLastModified.get(); + } + else { + lastMod = tstampCache.get(file); + + changed = lastMod == null || lastMod != file.lastModified(); + + lastMod = file.lastModified(); + } + + // If file is new or has been modified. + if (changed) { + tstampCache.put(file, lastMod); + + if (ctx.getLogger().isDebugEnabled()) + ctx.getLogger().debug("Discovered deployment file or directory: " + file); + + String fileName = file.getName(); + + try { + File cpFile = ctx.createTempFile(fileName, ctx.getDeployDirectory()); + + // Delete file when JVM stopped. + cpFile.deleteOnExit(); + + if (file.isDirectory()) { + cpFile = new File(cpFile.getParent(), "dir_" + cpFile.getName()); + + // Delete directory when JVM stopped. + cpFile.deleteOnExit(); + } + + // Copy file to deploy directory. + U.copy(file, cpFile, true); + + String fileUri = getFileUri(file.getAbsolutePath()); + + assert lastMod != null; + + ctx.getListener().onNewOrUpdatedFile(cpFile, fileUri, lastMod); + } + catch (IOException e) { + U.error(ctx.getLogger(), "Error saving file: " + fileName, e); + } + } + } + + /** + * Tests whether certain directory was changed since given modification date. + * It scans all directory files one by one and compares their modification + * dates with those ones that was collected before. + * <p> + * If at least one file was changed (has modification date after given one) + * whole directory is considered as modified. + * + * @param dir Scanning directory. + * @param lastModified Last calculated Directory modification date. + * @return {@code true} if directory was changed since last check and + * {@code false} otherwise. + */ + @SuppressWarnings("ConstantConditions") + private boolean checkGarDirectoryChanged(File dir, final GridTuple<Long> lastModified) { + final Map<File, Long> clssTstampCache; + + boolean firstScan = false; + + if (!garDirFilesTstampCache.containsKey(dir)) { + firstScan = true; + + garDirFilesTstampCache.put(dir, clssTstampCache = new HashMap<>()); + } + else + clssTstampCache = garDirFilesTstampCache.get(dir); + + assert clssTstampCache != null; + + final GridTuple<Boolean> changed = F.t(false); + + final Set<File> foundFiles = firstScan ? new HashSet<File>() : U.<File>newHashSet(clssTstampCache.size()); + + GridDeploymentFileHandler hnd = new GridDeploymentFileHandler() { + @Override public void handle(File file) { + foundFiles.add(file); + + Long fileLastModified = clssTstampCache.get(file); + + if (fileLastModified == null || fileLastModified != file.lastModified()) { + clssTstampCache.put(file, fileLastModified = file.lastModified()); + + changed.set(true); + } + + // Calculate last modified file in folder. + if (fileLastModified > lastModified.get()) + lastModified.set(fileLastModified); + } + }; + + // Scan GAR-directory for changes. + GridDeploymentFolderScannerHelper.scanFolder(dir, garDirFilesFilter, hnd); + + // Clear cache for deleted files. + if (!firstScan && clssTstampCache.keySet().retainAll(foundFiles)) + changed.set(true); + + return changed.get(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0755231c/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/http/GridUriDeploymentHttpScanner.java ---------------------------------------------------------------------- diff --git a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/http/GridUriDeploymentHttpScanner.java b/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/http/GridUriDeploymentHttpScanner.java deleted file mode 100644 index 49ac343..0000000 --- a/modules/urideploy/src/main/java/org/apache/ignite/spi/deployment/uri/scanners/http/GridUriDeploymentHttpScanner.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * 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.ignite.spi.deployment.uri.scanners.http; - -import org.apache.ignite.*; -import org.apache.ignite.internal.util.tostring.*; -import org.apache.ignite.internal.util.typedef.*; -import org.apache.ignite.internal.util.typedef.internal.*; -import org.apache.ignite.spi.*; -import org.apache.ignite.spi.deployment.uri.scanners.*; -import org.jetbrains.annotations.*; -import org.w3c.dom.*; -import org.w3c.tidy.*; - -import javax.net.ssl.*; -import java.io.*; -import java.net.*; -import java.security.*; -import java.security.cert.*; -import java.util.*; - -/** - * URI deployment HTTP scanner. - */ -public class GridUriDeploymentHttpScanner extends GridUriDeploymentScanner { - /** Secure socket protocol to use. */ - private static final String PROTOCOL = "TLS"; - - /** */ - @GridToStringExclude - private URL scanDir; - - /** Cache of found files to check if any of it has been updated. */ - private Map<String, Long> tstampCache = new HashMap<>(); - - /** */ - @GridToStringExclude - private final Tidy tidy; - - /** Outgoing data SSL socket factory. */ - private SSLSocketFactory sockFactory; - - /** - * @param gridName Grid instance name. - * @param uri HTTP URI. - * @param deployDir Deployment directory. - * @param freq Scanner frequency. - * @param filter Filename filter. - * @param lsnr Deployment listener. - * @param log Logger to use. - * @throws org.apache.ignite.spi.IgniteSpiException Thrown in case of any error. - */ - public GridUriDeploymentHttpScanner( - String gridName, - URI uri, - File deployDir, - long freq, - FilenameFilter filter, - GridUriDeploymentScannerListener lsnr, - IgniteLogger log) throws IgniteSpiException { - super(gridName, uri, deployDir, freq, filter, lsnr, log); - - initialize(uri); - - tidy = new Tidy(); - - tidy.setQuiet(true); - tidy.setOnlyErrors(true); - tidy.setShowWarnings(false); - tidy.setInputEncoding("UTF8"); - tidy.setOutputEncoding("UTF8"); - } - - /** - * @param uri HTTP URI. - * @throws org.apache.ignite.spi.IgniteSpiException Thrown in case of any error. - */ - private void initialize(URI uri) throws IgniteSpiException { - assert "http".equals(uri.getScheme()) || "https".equals(uri.getScheme()); - - try { - scanDir = new URL(uri.getScheme(), uri.getHost(), uri.getPort(), uri.getPath()); - } - catch (MalformedURLException e) { - scanDir = null; - - throw new IgniteSpiException("Wrong value for scanned HTTP directory with URI: " + uri, e); - } - - try { - if ("https".equals(uri.getScheme())) { - // Set up socket factory to do authentication. - SSLContext ctx = SSLContext.getInstance(PROTOCOL); - - ctx.init(null, getTrustManagers(), null); - - sockFactory = ctx.getSocketFactory(); - } - } - catch (NoSuchAlgorithmException e) { - throw new IgniteSpiException("Failed to initialize SSL context. URI: " + uri, e); - } - catch (KeyManagementException e) { - throw new IgniteSpiException("Failed to initialize SSL context. URI:" + uri, e); - } - } - - /** {@inheritDoc} */ - @Override protected void process() { - Collection<String> foundFiles = U.newHashSet(tstampCache.size()); - - long start = U.currentTimeMillis(); - - processHttp(foundFiles); - - if (getLogger().isDebugEnabled()) - getLogger().debug("HTTP scanner time in ms: " + (U.currentTimeMillis() - start)); - - if (!isFirstScan()) { - Collection<String> deletedFiles = new HashSet<>(tstampCache.keySet()); - - deletedFiles.removeAll(foundFiles); - - if (!deletedFiles.isEmpty()) { - List<String> uris = new ArrayList<>(); - - for (String file : deletedFiles) { - uris.add(getFileUri(getFileName(file))); - } - - tstampCache.keySet().removeAll(deletedFiles); - - getListener().onDeletedFiles(uris); - } - } - } - - /** - * @param files Files to process. - */ - private void processHttp(Collection<String> files) { - Set<String> urls = getUrls(scanDir); - - for (String url : urls) { - String fileName = getFileName(url); - - if (getFilter().accept(null, fileName)) { - files.add(url); - - Long lastModified = tstampCache.get(url); - - InputStream in = null; - OutputStream out = null; - - File file = null; - - try { - URLConnection conn = new URL(url).openConnection(); - - if (conn instanceof HttpsURLConnection) { - HttpsURLConnection httpsConn = (HttpsURLConnection)conn; - - httpsConn.setHostnameVerifier(new DeploymentHostnameVerifier()); - - assert sockFactory != null; - - // Initialize socket factory. - httpsConn.setSSLSocketFactory(sockFactory); - } - - if (lastModified != null) - conn.setIfModifiedSince(lastModified); - - in = conn.getInputStream(); - - long rcvLastModified = conn.getLastModified(); - - if (in == null || lastModified != null && (lastModified == rcvLastModified || - conn instanceof HttpURLConnection && - ((HttpURLConnection)conn).getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED)) - continue; - - tstampCache.put(url, rcvLastModified); - - lastModified = rcvLastModified; - - if (getLogger().isDebugEnabled()) { - getLogger().debug("Discovered deployment file or directory: " + - U.hidePassword(url)); - } - - file = createTempFile(fileName, getDeployDirectory()); - - // Delete file when JVM stopped. - file.deleteOnExit(); - - out = new FileOutputStream(file); - - U.copy(in, out); - } - catch (IOException e) { - if (!isCancelled()) { - if (X.hasCause(e, ConnectException.class)) { - LT.warn(getLogger(), e, "Failed to connect to HTTP server (connection refused): " + - U.hidePassword(url)); - } - else if (X.hasCause(e, UnknownHostException.class)) { - LT.warn(getLogger(), e, "Failed to connect to HTTP server (host is unknown): " + - U.hidePassword(url)); - } - else - U.error(getLogger(), "Failed to save file: " + fileName, e); - } - } - finally { - U.closeQuiet(in); - U.closeQuiet(out); - } - - if (file != null && file.exists() && file.length() > 0) - getListener().onNewOrUpdatedFile(file, getFileUri(fileName), lastModified); - } - } - } - - /** - * @param node XML element node. - * @param res Set of URLs in string format to populate. - * @param baseUrl Base URL. - */ - @SuppressWarnings( {"UnusedCatchParameter", "UnnecessaryFullyQualifiedName"}) - private void findReferences(org.w3c.dom.Node node, Set<String> res, URL baseUrl) { - if (node instanceof Element && "a".equals(node.getNodeName().toLowerCase())) { - Element element = (Element)node; - - String href = element.getAttribute("href"); - - if (href != null && !href.isEmpty()) { - URL url = null; - - try { - url = new URL(href); - } - catch (MalformedURLException e) { - try { - url = new URL(baseUrl.getProtocol(), baseUrl.getHost(), baseUrl.getPort(), - href.charAt(0) == '/' ? href : baseUrl.getFile() + '/' + href); - } - catch (MalformedURLException e1) { - U.error(getLogger(), "Skipping bad URL: " + url, e1); - } - } - - if (url != null) - res.add(url.toString()); - } - } - - NodeList childNodes = node.getChildNodes(); - - for (int i = 0; i < childNodes.getLength(); i++) - findReferences(childNodes.item(i), res, baseUrl); - } - - /** - * @param url Base URL. - * @return Set of referenced URLs in string format. - */ - private Set<String> getUrls(URL url) { - assert url != null; - - InputStream in = null; - - Set<String> urls = new HashSet<>(); - - Document dom = null; - - try { - URLConnection conn = url.openConnection(); - - if (conn instanceof HttpsURLConnection) { - HttpsURLConnection httpsConn = (HttpsURLConnection)conn; - - httpsConn.setHostnameVerifier(new DeploymentHostnameVerifier()); - - assert sockFactory != null; - - // Initialize socket factory. - httpsConn.setSSLSocketFactory(sockFactory); - } - - in = conn.getInputStream(); - - if (in == null) - throw new IOException("Failed to open connection: " + U.hidePassword(url.toString())); - - dom = tidy.parseDOM(in, null); - } - catch (IOException e) { - if (!isCancelled()) { - if (X.hasCause(e, ConnectException.class)) { - LT.warn(getLogger(), e, "Failed to connect to HTTP server (connection refused): " + - U.hidePassword(url.toString())); - } - else if (X.hasCause(e, UnknownHostException.class)) { - LT.warn(getLogger(), e, "Failed to connect to HTTP server (host is unknown): " + - U.hidePassword(url.toString())); - } - else - U.error(getLogger(), "Failed to get HTML page: " + U.hidePassword(url.toString()), e); - } - } - finally{ - U.closeQuiet(in); - } - - if (dom != null) - findReferences(dom, urls, url); - - return urls; - } - - /** - * @param url Base URL string format. - * @return File name extracted from {@code url} string format. - */ - private String getFileName(String url) { - assert url != null; - - return url.substring(url.lastIndexOf('/') + 1); - } - - /** - * Construct array with one trust manager which don't reject input certificates. - * - * @return Array with one X509TrustManager implementation of trust manager. - */ - private TrustManager[] getTrustManagers() { - return new TrustManager[]{ - new X509TrustManager() { - /** {@inheritDoc} */ - @Nullable @Override public X509Certificate[] getAcceptedIssuers() { return null; } - - /** {@inheritDoc} */ - @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { - StringBuilder buf = new StringBuilder(); - - buf.append("Trust manager handle client certificates [authType="); - buf.append(authType); - buf.append(", certificates="); - - for (X509Certificate cert : certs) { - buf.append("{type="); - buf.append(cert.getType()); - buf.append(", principalName="); - buf.append(cert.getSubjectX500Principal().getName()); - buf.append('}'); - } - - buf.append(']'); - - if (getLogger().isDebugEnabled()) - getLogger().debug(buf.toString()); - } - - /** {@inheritDoc} */ - @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { - StringBuilder buf = new StringBuilder(); - - buf.append("Trust manager handle server certificates [authType="); - buf.append(authType); - buf.append(", certificates="); - - for (X509Certificate cert : certs) { - buf.append("{type="); - buf.append(cert.getType()); - buf.append(", principalName="); - buf.append(cert.getSubjectX500Principal().getName()); - buf.append('}'); - } - - buf.append(']'); - - if (getLogger().isDebugEnabled()) - getLogger().debug(buf.toString()); - } - } - }; - } - - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(GridUriDeploymentHttpScanner.class, this, - "scanDir", scanDir != null ? U.hidePassword(scanDir.toString()) : null); - } - - /** - * Verifier always return successful result for any host. - */ - private static class DeploymentHostnameVerifier implements HostnameVerifier { - /** {@inheritDoc} */ - @Override public boolean verify(String hostname, SSLSession ses) { - // Remote host trusted by default. - return true; - } - } -}