LinkCheckerTask.java 13.1 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.RepositoryException
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.http.StatusLine
 *  org.apache.http.client.config.RequestConfig
 *  org.apache.http.client.config.RequestConfig$Builder
 *  org.apache.http.client.methods.CloseableHttpResponse
 *  org.apache.http.client.methods.HttpGet
 *  org.apache.http.client.methods.HttpHead
 *  org.apache.http.client.methods.HttpUriRequest
 *  org.apache.http.conn.HttpClientConnectionManager
 *  org.apache.http.impl.client.CloseableHttpClient
 *  org.apache.http.impl.client.HttpClientBuilder
 *  org.apache.http.impl.conn.PoolingHttpClientConnectionManager
 *  org.apache.http.osgi.services.HttpClientBuilderFactory
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.apache.sling.discovery.InstanceDescription
 *  org.apache.sling.discovery.TopologyEvent
 *  org.apache.sling.discovery.TopologyEvent$Type
 *  org.apache.sling.discovery.TopologyEventListener
 *  org.apache.sling.discovery.TopologyView
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.rewriter.linkchecker.impl;

import com.day.cq.rewriter.linkchecker.ExternalLinkChecker;
import com.day.cq.rewriter.linkchecker.LinkCheckerConfigProvider;
import com.day.cq.rewriter.linkchecker.LinkInfo;
import com.day.cq.rewriter.linkchecker.LinkInfoStorage;
import com.day.cq.rewriter.linkchecker.impl.LinkCheckerTransformerConfig;
import java.io.IOException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.net.UnknownServiceException;
import java.util.Calendar;
import java.util.Dictionary;
import javax.jcr.RepositoryException;
import javax.net.ssl.SSLException;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.osgi.services.HttpClientBuilderFactory;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.discovery.InstanceDescription;
import org.apache.sling.discovery.TopologyEvent;
import org.apache.sling.discovery.TopologyEventListener;
import org.apache.sling.discovery.TopologyView;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(label="%taskservice.name", description="%taskservice.description", metatype=1)
@Service
@Properties(value={@Property(name="scheduler.period", longValue={3600}), @Property(name="scheduler.concurrent", boolValue={0})})
public class LinkCheckerTask
implements Runnable,
ExternalLinkChecker,
TopologyEventListener {
    private final Logger log;
    private Boolean isMasterInstance;
    private static final int DEFAULT_GOOD_LINK_TEST_INTERVAL = 24;
    @Property(intValue={24})
    private static final String GOOD_LINK_TEST_INTERVAL = "good_link_test_interval";
    private static final int DEFAULT_BAD_LINK_TEST_INTERVAL = 1;
    @Property(intValue={1})
    private static final String BAD_LINK_TEST_INTERVAL = "bad_link_test_interval";
    private static final int DEFAULT_LINK_UNUSED_INTERVAL = 168;
    @Property(intValue={168})
    private static final String LINK_UNUSED_INTERVAL = "link_unused_interval";
    private static final int DEFAULT_CONNECTION_TIMEOUT = 10000;
    @Reference
    private HttpClientBuilderFactory httpClientBuilderFactory;
    @Property(intValue={10000})
    private static final String CONNECTION_TIMEOUT = "connection.timeout";
    @Reference
    protected LinkInfoStorage storage;
    @Reference
    protected LinkCheckerConfigProvider configProvider;
    protected int goodLinkTestInterval;
    protected int badLinkTestInterval;
    protected int linkUnusedInterval;
    private CloseableHttpClient httpClient;

    public LinkCheckerTask() {
        this.log = LoggerFactory.getLogger(this.getClass());
        this.isMasterInstance = Boolean.FALSE;
    }

    @Activate
    protected void activate(ComponentContext context) throws RepositoryException {
        Dictionary props = context.getProperties();
        this.goodLinkTestInterval = OsgiUtil.toInteger(props.get("good_link_test_interval"), (int)24);
        this.badLinkTestInterval = OsgiUtil.toInteger(props.get("bad_link_test_interval"), (int)1);
        this.linkUnusedInterval = OsgiUtil.toInteger(props.get("link_unused_interval"), (int)168);
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        int connectionTimeout = OsgiUtil.toInteger(props.get("connection.timeout"), (int)10000);
        if (connectionTimeout >= 5000) {
            RequestConfig config = RequestConfig.custom().setConnectTimeout(connectionTimeout).build();
            this.httpClient = this.httpClientBuilderFactory.newBuilder().setDefaultRequestConfig(config).setConnectionManager((HttpClientConnectionManager)cm).build();
        } else {
            this.httpClient = this.httpClientBuilderFactory.newBuilder().setConnectionManager((HttpClientConnectionManager)cm).build();
        }
        this.log.info("LinkChecker service activated");
    }

    @Deactivate
    protected void deactivate(ComponentContext componentContext) {
        if (this.httpClient != null) {
            try {
                this.httpClient.close();
            }
            catch (IOException e) {
                this.log.error("Unable to close the HTTP client", (Object)e.getMessage());
            }
            this.httpClient = null;
        }
        this.log.info("LinkChecker service shut down");
    }

    @Override
    public void run() {
        if (!this.isMasterInstance.booleanValue() || this.configProvider.getConfig().getDisableChecking()) {
            return;
        }
        this.checkLinks();
    }

    @Override
    public void checkLinks() {
        String[] links;
        if (!this.isMasterInstance.booleanValue()) {
            return;
        }
        for (String link : links = this.storage.getLinks()) {
            this.checkLink(link, false);
        }
    }

    @Override
    public LinkInfo checkLink(String link, boolean force) {
        boolean check;
        LinkInfo info = this.storage.getLinkInfo(link);
        if (info == null) {
            return null;
        }
        Calendar now = Calendar.getInstance();
        if (this.hoursPassed(info.getLastAccessed(), this.linkUnusedInterval)) {
            this.storage.deleteLinkInfo(info);
            return null;
        }
        boolean bl = check = info.getLastChecked() == null || info.isValid() && this.hoursPassed(info.getLastChecked(), this.goodLinkTestInterval) || !info.isValid() && this.hoursPassed(info.getLastChecked(), this.badLinkTestInterval);
        if (check || force) {
            int status;
            try {
                this.log.debug("Checking URL {}", (Object)link);
                status = this.check(link);
                if (status == 404 || status == 301) {
                    this.log.info("Checked URL {}: {} (invalid)", (Object)link, (Object)status);
                    info.setValid(false);
                } else if (status == 200 || status == 302) {
                    this.log.info("Checked URL {}: {} (valid)", (Object)link, (Object)status);
                    info.setValid(true);
                    info.setLastAvailable(now);
                }
            }
            catch (SSLException e) {
                status = -7;
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (UnknownServiceException e) {
                status = -1;
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (NoRouteToHostException e) {
                status = -4;
                info.setValid(false);
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (UnknownHostException e) {
                status = -5;
                info.setValid(false);
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (ConnectException e) {
                status = -3;
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (URISyntaxException e) {
                status = -2;
                info.setValid(false);
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (IOException e) {
                status = -6;
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            catch (Exception e) {
                status = -6;
                this.log.error("Failed to validate URL {}: {}", (Object)link, (Object)e.toString());
            }
            info.setLastStatus(status);
            info.setLastChecked(now);
            this.storage.putLinkInfo(info);
        }
        return info;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    @Override
    public int check(String url) throws UnknownServiceException, URISyntaxException, ConnectException, NoRouteToHostException, UnknownHostException, IOException {
        URI uri;
        HttpGet getMethod;
        HttpHead headMethod;
        uri = new URI(url);
        try {
            headMethod = new HttpHead(uri.toString());
        }
        catch (Exception e) {
            throw new UnknownServiceException(e.getMessage());
        }
        try {
            int status;
            CloseableHttpResponse httpResp = this.httpClient.execute((HttpUriRequest)headMethod);
            if (httpResp != null && ((status = httpResp.getStatusLine().getStatusCode()) == 200 || status == 302)) {
                int n = status;
                return n;
            }
        }
        finally {
            headMethod.releaseConnection();
        }
        try {
            getMethod = new HttpGet(uri.toString());
        }
        catch (Exception e) {
            throw new UnknownServiceException(e.getMessage());
        }
        try {
            int e = this.httpClient.execute((HttpUriRequest)getMethod).getStatusLine().getStatusCode();
            return e;
        }
        finally {
            getMethod.releaseConnection();
        }
    }

    private boolean hoursPassed(Calendar calendar, int hours) {
        if (calendar == null) {
            return true;
        }
        Calendar now = Calendar.getInstance();
        Calendar check = (Calendar)calendar.clone();
        check.add(10, hours);
        return now.after(check);
    }

    public void handleTopologyEvent(TopologyEvent event) {
        if (event.getType() == TopologyEvent.Type.TOPOLOGY_CHANGED || event.getType() == TopologyEvent.Type.TOPOLOGY_INIT) {
            this.isMasterInstance = event.getNewView().getLocalInstance().isLeader();
        } else if (event.getType() == TopologyEvent.Type.TOPOLOGY_CHANGING) {
            this.isMasterInstance = false;
        }
    }

    protected void bindHttpClientBuilderFactory(HttpClientBuilderFactory httpClientBuilderFactory) {
        this.httpClientBuilderFactory = httpClientBuilderFactory;
    }

    protected void unbindHttpClientBuilderFactory(HttpClientBuilderFactory httpClientBuilderFactory) {
        if (this.httpClientBuilderFactory == httpClientBuilderFactory) {
            this.httpClientBuilderFactory = null;
        }
    }

    protected void bindStorage(LinkInfoStorage linkInfoStorage) {
        this.storage = linkInfoStorage;
    }

    protected void unbindStorage(LinkInfoStorage linkInfoStorage) {
        if (this.storage == linkInfoStorage) {
            this.storage = null;
        }
    }

    protected void bindConfigProvider(LinkCheckerConfigProvider linkCheckerConfigProvider) {
        this.configProvider = linkCheckerConfigProvider;
    }

    protected void unbindConfigProvider(LinkCheckerConfigProvider linkCheckerConfigProvider) {
        if (this.configProvider == linkCheckerConfigProvider) {
            this.configProvider = null;
        }
    }
}