All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jaeksoft.searchlib.InstanceProperties Maven / Gradle / Ivy

/**   
 * License Agreement for OpenSearchServer
 *
 * Copyright (C) 2011-2013 Emmanuel Keller / Jaeksoft
 * 
 * http://www.open-search-server.com
 * 
 * This file is part of OpenSearchServer.
 *
 * OpenSearchServer is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 * OpenSearchServer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenSearchServer. 
 *  If not, see .
 **/

package com.jaeksoft.searchlib;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import redis.clients.jedis.Jedis;

import com.jaeksoft.searchlib.util.FileUtils;
import com.jaeksoft.searchlib.util.IOUtils;
import com.jaeksoft.searchlib.util.NetworksUtils;
import com.jaeksoft.searchlib.util.ReadWriteLock;
import com.jaeksoft.searchlib.util.StringUtils;
import com.jaeksoft.searchlib.util.XPathParser;
import com.jaeksoft.searchlib.web.StartStopListener;
import com.jaeksoft.searchlib.webservice.ApiIdentifier;

public class InstanceProperties {

	private final long maxDocumentLimit;

	private final long maxStorage;

	private final int maxIndexNumber;

	private final int minCrawlerDelay;

	private final int maxApiRate;

	private final int minApiDelay;

	private final int requestPerMonth;

	private int requestPerMonthCount;

	private long lastTimeRequestPerMonthStore;

	private final File requestperMonthFile;

	private long nextApiTime;

	private int countApiCall;

	private int countApiWait;

	private final String redisApiServerHostname;

	private final int redisApiServerPort;

	private final String redisApiAuth;

	private final boolean chroot;

	private final static String LIMIT_NODEPATH = "/instanceProperties/limit";

	private final static String LIMIT_CHROOT_ATTR = "chroot";

	private final static String LIMIT_MAXDOCUMENTLIMIT_ATTR = "maxDocumentLimit";

	private final static String LIMIT_MAX_STORAGE_ATTR = "maxStorage";

	private final static String LIMIT_MAX_INDEX_NUMBER_ATTR = "maxIndexNumber";

	private final static String LIMIT_MINCRAWLERDELAY_ATTR = "minCrawlerDelay";

	private final static String LIMIT_MAX_API_RATE = "maxApiRate";

	private final static String LIMIT_REQUEST_PER_MONTH = "requestPerMonth";

	private final static String REDIS_API_NODE = "/instanceProperties/redisApi";

	private final static String REDIS_API_HOSTNAME_ATTR = "hostname";

	private final static String REDIS_API_PORT_ATTR = "port";

	private final static String REDIS_API_AUTH_ATTR = "auth";

	public InstanceProperties(File xmlFile)
			throws ParserConfigurationException, SAXException, IOException,
			XPathExpressionException, URISyntaxException {
		nextApiTime = System.currentTimeMillis();
		countApiCall = 0;
		countApiWait = 0;
		lastTimeRequestPerMonthStore = 0;
		requestPerMonthCount = 0;
		if (xmlFile.exists()) {
			requestperMonthFile = new File(xmlFile.getParent(),
					"requestPerMonth.txt");
			loadRequestPerMonth();
			XPathParser xpp = new XPathParser(xmlFile);
			Node node = xpp.getNode(LIMIT_NODEPATH);
			if (node != null) {
				maxDocumentLimit = XPathParser.getAttributeLong(node,
						LIMIT_MAXDOCUMENTLIMIT_ATTR);
				chroot = "yes".equalsIgnoreCase(XPathParser.getAttributeString(
						node, LIMIT_CHROOT_ATTR));
				minCrawlerDelay = XPathParser.getAttributeValue(node,
						LIMIT_MINCRAWLERDELAY_ATTR);
				maxIndexNumber = XPathParser.getAttributeValue(node,
						LIMIT_MAX_INDEX_NUMBER_ATTR);
				maxStorage = XPathParser.getAttributeLong(node,
						LIMIT_MAX_STORAGE_ATTR);
				maxApiRate = XPathParser.getAttributeValue(node,
						LIMIT_MAX_API_RATE);
				requestPerMonth = XPathParser.getAttributeValue(node,
						LIMIT_REQUEST_PER_MONTH);
				minApiDelay = maxApiRate != 0 ? 1000 / maxApiRate : 0;
			} else {
				maxDocumentLimit = 0;
				chroot = false;
				minCrawlerDelay = 0;
				maxIndexNumber = 0;
				maxStorage = 0;
				maxApiRate = 0;
				requestPerMonth = 0;
				minApiDelay = 0;
			}
			node = xpp.getNode(REDIS_API_NODE);
			if (node != null) {
				redisApiServerHostname = XPathParser.getAttributeString(node,
						REDIS_API_HOSTNAME_ATTR);
				redisApiServerPort = XPathParser.getAttributeValue(node,
						REDIS_API_PORT_ATTR);
				redisApiAuth = XPathParser.getAttributeString(node,
						REDIS_API_AUTH_ATTR);
			} else {
				redisApiServerHostname = null;
				redisApiServerPort = 0;
				redisApiAuth = null;
			}
		} else {
			requestperMonthFile = null;
			maxDocumentLimit = 0;
			chroot = false;
			minCrawlerDelay = 0;
			maxIndexNumber = 0;
			maxStorage = 0;
			maxApiRate = 0;
			requestPerMonth = 0;
			minApiDelay = 0;
			redisApiServerHostname = null;
			redisApiServerPort = 0;
			redisApiAuth = null;
		}
	}

	/**
	 * @return the maxDocumentLimit
	 */
	public long getMaxDocumentLimit() {
		return maxDocumentLimit;
	}

	/**
	 * @return the minCrawlerDelay
	 */
	public int getMinCrawlerDelay() {
		return minCrawlerDelay;
	}

	/**
	 * @return the maxStorage
	 */
	public long getMaxStorage() {
		return maxStorage;
	}

	/**
	 * @return the maxIndexNumber
	 */
	public int getMaxIndexNumber() {
		return maxIndexNumber;
	}

	/**
	 * @return the chroot
	 */
	public boolean isChroot() {
		return chroot;
	}

	public final boolean checkChrootQuietly(File file) throws IOException {
		if (!chroot)
			return true;
		return FileUtils.isSubDirectory(
				StartStopListener.OPENSEARCHSERVER_DATA_FILE, file);
	}

	public final void checkChroot(File file) throws IOException {
		if (!checkChrootQuietly(file))
			throw new IOException(
					"You are not allowed to reach this location in the file system: "
							+ file.getAbsolutePath());
	}

	public final void checkMaxDocumentLimit() throws SearchLibException,
			IOException {
		if (maxDocumentLimit == 0)
			return;
		long count = ClientCatalog.countAllDocuments();
		if (count < maxDocumentLimit)
			return;
		throw new SearchLibException(
				"The maximum number of allowable documents has been reached ("
						+ maxDocumentLimit + ")");
	}

	public final void checkMaxIndexNumber() throws SearchLibException,
			IOException {
		if (maxIndexNumber == 0)
			return;
		long count = ClientCatalog.getClientCatalog(null).size();
		if (count < maxIndexNumber)
			return;
		throw new SearchLibException(
				"The maximum number of allowable index has been reached ("
						+ maxIndexNumber + ")");
	}

	public final void checkMaxStorageLimit() throws SearchLibException {
		if (maxStorage == 0)
			return;
		long size = ClientCatalog.calculateInstanceSize();
		if (size <= maxStorage)
			return;
		throw new SearchLibException(
				"The maximum storage size has been reached ("
						+ StringUtils.humanBytes(maxStorage) + ")");
	}

	protected final void checkApiRate() throws InterruptedException {
		if (minApiDelay == 0)
			return;
		countApiCall++;
		if (countApiCall == 1000) {
			countApiCall = 1;
			countApiWait = 0;
		}
		long newTime = System.currentTimeMillis();
		long sleep = nextApiTime - newTime;
		nextApiTime = newTime + minApiDelay;
		if (sleep > 0) {
			Thread.sleep(sleep);
			countApiWait++;
		}
	}

	private final void loadRequestPerMonth() throws IOException {
		if (!requestperMonthFile.exists())
			return;
		String s = FileUtils.readFileToString(requestperMonthFile);
		if (s == null)
			return;
		try {
			requestPerMonthCount = Integer.parseInt(s.trim());
		} catch (NumberFormatException e) {
			Logging.warn(e);
		}
	}

	private final void storeRequestPerMonthCount(long t) throws IOException {
		synchronized (requestperMonthFile) {
			if (!requestperMonthFile.exists())
				resetRequestPerMonthCount();
			FileUtils.write(requestperMonthFile,
					Integer.toString(getRequestPerMonthCount()));
			lastTimeRequestPerMonthStore = t + 10000;
		}
	}

	private ReadWriteLock requestPerMonthLock = new ReadWriteLock();

	private final void incRequestPerMonthCount() {
		requestPerMonthLock.w.lock();
		try {
			requestPerMonthCount++;
		} finally {
			requestPerMonthLock.w.unlock();
		}
	}

	private final void resetRequestPerMonthCount() {
		requestPerMonthLock.w.lock();
		try {
			requestPerMonthCount = 0;
		} finally {
			requestPerMonthLock.w.unlock();
		}
	}

	/**
	 * @return the requestPerMonthCount
	 */
	public int getRequestPerMonthCount() {
		requestPerMonthLock.r.lock();
		try {
			return requestPerMonthCount;
		} finally {
			requestPerMonthLock.r.unlock();
		}
	}

	public final int getRequestPerMonth() {
		return requestPerMonth;
	}

	public final boolean isRequestPerMonth() {
		return requestPerMonth > 0;
	}

	protected final void checkApiRequestPerMonth() throws IOException {
		if (requestPerMonth == 0)
			return;
		long t = System.currentTimeMillis();
		incRequestPerMonthCount();
		if (t < lastTimeRequestPerMonthStore)
			return;
		storeRequestPerMonthCount(t);
	}

	private final static SimpleDateFormat REDIS_STATS_DAYFORMAT = new SimpleDateFormat(
			"yyyyMMdd");

	protected final void checkRedisApi(String apiKey, ApiIdentifier apiId,
			String remoteIpAddress) throws WebApplicationException {
		if (redisApiServerHostname == null)
			return;
		if (apiKey == null || apiKey.length() == 0)
			throw new WebApplicationException(Status.FORBIDDEN);
		Jedis jedis = new Jedis(redisApiServerHostname, redisApiServerPort,
				10000);
		try {
			if (redisApiAuth != null && redisApiAuth.length() > 0)
				jedis.auth(redisApiAuth);
			String[] parts = StringUtils.split(apiKey, '_');
			if (parts.length < 2)
				throw new WebApplicationException(Status.FORBIDDEN);
			StringBuilder sbKey = new StringBuilder("apiAccountService.");
			sbKey.append(parts[1]);
			sbKey.append('.');
			sbKey.append(apiId);
			String skey = sbKey.toString();
			String v = jedis.hget(skey, apiKey);
			if (v == null)
				throw new WebApplicationException(Status.FORBIDDEN);
			if (remoteIpAddress.equals("0:0:0:0:0:0:0:1"))
				remoteIpAddress = "127.0.0.1";
			boolean bAllowed = "0".equals(v) || v.length() == 0;
			if (!bAllowed)
				for (SubnetInfo subnetInfo : NetworksUtils.getSubnetArray(v))
					if (subnetInfo.isInRange(remoteIpAddress))
						bAllowed = true;
			if (!bAllowed) {
				Logging.warn("Authentication failure: " + apiKey + " "
						+ remoteIpAddress);
				throw new WebApplicationException(Status.FORBIDDEN);
			}
			String statKey;
			synchronized (REDIS_STATS_DAYFORMAT) {
				statKey = REDIS_STATS_DAYFORMAT.format(new Date());
			}
			jedis.hincrBy(skey + ".stats", statKey, 1);
		} catch (IOException e) {
			throw new WebApplicationException(Status.FORBIDDEN);
		} finally {
			if (jedis != null) {
				if (jedis.isConnected())
					jedis.disconnect();
				IOUtils.close(jedis);
			}
		}
	}

	public final void checkApi() throws InterruptedException, IOException {
		checkApiRate();
		checkApiRequestPerMonth();
	}

	public final void checkApi(String apiKey, ApiIdentifier apiId,
			String remoteAddr) throws WebApplicationException,
			InterruptedException {
		checkApiRate();
		checkRedisApi(apiKey, apiId, remoteAddr);
	}

	public final float getApiWaitRate() {
		return (float) (((float) countApiWait / (float) countApiCall) * 100);
	}

	public final boolean isMaxApiRate() {
		return maxApiRate > 0;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(super.toString());
		sb.append(" - maxDocumentLimit: ");
		sb.append(maxDocumentLimit);
		sb.append(" - maxStorage: ");
		sb.append(StringUtils.humanBytes(maxStorage));
		sb.append(" - maxIndexNumber: ");
		sb.append(maxIndexNumber);
		sb.append(" - minCrawlerDelay: ");
		sb.append(minCrawlerDelay);
		sb.append(" - maxApiRate: ");
		sb.append(maxApiRate);
		sb.append(" - chroot: ");
		sb.append(chroot);
		return sb.toString();
	}

	/**
	 * @return the redisApiServerHostname
	 */
	public String getRedisApiServerHostname() {
		return redisApiServerHostname;
	}

	/**
	 * @return the redisApiServerPort
	 */
	public int getRedisApiServerPort() {
		return redisApiServerPort;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy