/*
* Copyright (c) 2014 Globo.com - ATeam
* All rights reserved.
*
* This source is subject to the Apache License, Version 2.0.
* Please see the LICENSE file for more information.
*
* Authors: See AUTHORS file
*
* 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 com.globo.galeb.entity.impl.backend;
import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace;
import java.util.concurrent.atomic.AtomicBoolean;
import org.vertx.java.core.Handler;
import org.vertx.java.core.Vertx;
import org.vertx.java.core.http.HttpClient;
import org.vertx.java.core.json.JsonObject;
import com.globo.galeb.entity.Entity;
import com.globo.galeb.scheduler.IScheduler;
import com.globo.galeb.scheduler.ISchedulerHandler;
import com.globo.galeb.scheduler.impl.NullScheduler;
import com.globo.galeb.scheduler.impl.VertxPeriodicScheduler;
/**
* Class BackendSession.
*
* @author: See AUTHORS file.
* @version: 1.0.0, Oct 23, 2014.
*/
public class BackendSession extends Entity {
/** The Constant HOST_DEFAULT. */
public static final String HOST_DEFAULT = "127.0.0.1";
/** The Constant PORT_DEFAULT. */
public static final int PORT_DEFAULT = 80;
/** The http client instance. */
private HttpClient client = null;
/** The keep alive. */
private boolean keepAlive = true;
/** The max pool size. */
private int maxPoolSize = 1;
/** The keep alive limit scheduler. */
private IScheduler keepAliveLimitScheduler = new NullScheduler();
/** The keep alive interval. */
private long keepAliveLimitInterval = 1000L;
/** The request count. */
private long requestCount = 0L;
/** The keep alive time mark. */
private long keepAliveTimeMark = System.currentTimeMillis();
/** The keep alive max request. */
private long keepAliveMaxRequest = Long.MAX_VALUE;
/** The keep alive time out. */
private long keepAliveTimeOut = 86400000L; // One day
/** The is locked. */
private AtomicBoolean isLocked = new AtomicBoolean(false);
/**
* Class KeepAliveCheckLimitHandler.
*
* @author See AUTHORS file.
* @version 1.0.0, Nov 4, 2014.
*/
class KeepAliveCheckLimitHandler implements ISchedulerHandler {
/** The backend session. */
private BackendSession backendSession;
/**
* Instantiates a new keep alive check limit handler.
*
* @param backendSession the backend session
*/
public KeepAliveCheckLimitHandler(final BackendSession backendSession) {
this.backendSession = backendSession;
}
/* (non-Javadoc)
* @see com.globo.galeb.scheduler.ISchedulerHandler#handle()
*/
@Override
public void handle() {
if (isLocked.get()) {
return;
}
isLocked.set(true);
if (backendSession.isKeepAliveLimit() && !backendSession.isClosed()) {
backendSession.close();
}
isLocked.compareAndSet(true, false);
}
}
/**
* Instantiates a new backend session.
*
* @param json the json object
*/
public BackendSession(final JsonObject json) {
super(json);
keepAlive = properties.getBoolean(IBackend.KEEPALIVE_FIELDNAME, true);
keepAliveMaxRequest = properties.getLong(IBackend.KEEPALIVE_MAXREQUEST_FIELDNAME, IBackend.DEFAULT_KEEPALIVE_MAXREQUEST);
keepAliveTimeOut = properties.getLong(IBackend.KEEPALIVE_TIMEOUT_FIELDNAME, IBackend.DEFAULT_KEEPALIVE_TIMEOUT);
maxPoolSize = properties.getInteger(IBackend.MAXPOOL_SIZE_FIELDNAME, IBackend.DEFAULT_MAX_POOL_SIZE);
}
/**
* Checks if is keep alive limit.
*
* @return true, if is keep alive limit
*/
public boolean isKeepAliveLimit() {
Long now = System.currentTimeMillis();
if (requestCount<keepAliveMaxRequest) {
requestCount++;
}
if ((requestCount==Long.MAX_VALUE) || (requestCount>=keepAliveMaxRequest) ||
(now-keepAliveTimeMark)>keepAliveTimeOut) {
keepAliveTimeMark = now;
requestCount = 0L;
return true;
}
return false;
}
// Lazy initialization
/**
* Connect and gets http client instance.
*
* @return the http client
*/
public HttpClient connect() {
if (client!=null && !isClosed()) {
if (isKeepAliveLimit()) {
close();
} else {
return client;
}
}
if (keepAlive && keepAliveLimitScheduler instanceof NullScheduler && plataform instanceof Vertx) {
keepAliveLimitScheduler = new VertxPeriodicScheduler((Vertx)plataform)
.setHandler(new KeepAliveCheckLimitHandler(this))
.setPeriod(keepAliveLimitInterval)
.start();
}
String[] hostWithPortArray = parentId!=null ? parentId.split(":") : null;
String host = null;
int port = -1;
if (hostWithPortArray != null && hostWithPortArray.length>1) {
host = hostWithPortArray[0];
try {
port = Integer.parseInt(hostWithPortArray[1]);
} catch (NumberFormatException e) {
port = PORT_DEFAULT;
}
} else {
host = parentId;
port = PORT_DEFAULT;
}
host = host != null ? host : HOST_DEFAULT;
port = port >= 0 ? port : PORT_DEFAULT;
if (client==null && plataform instanceof Vertx) {
final Vertx vertx = (Vertx) plataform;
client = vertx.createHttpClient()
.setKeepAlive(keepAlive)
.setTCPKeepAlive(keepAlive)
.setMaxPoolSize(maxPoolSize);
if (!client.toString().startsWith("Mock")) {
client.setHost(host)
.setPort(port);
}
client.exceptionHandler(new Handler<Throwable>() {
@Override
public void handle(Throwable e) {
if (queueService!=null) {
queueService.publishBackendFail(toJson());
}
}
});
}
return client;
}
/**
* Close connection and destroy http client instance.
*/
public void close() {
if (keepAliveLimitScheduler!=null) {
keepAliveLimitScheduler.cancel();
}
if (client!=null) {
try {
client.close();
} catch (IllegalStateException ignore) {
// Already closed. Ignore exception.
} finally {
client=null;
}
}
}
/**
* Checks if is closed.
*
* @return true, if is closed
*/
public boolean isClosed() {
if (client==null) {
return true;
}
boolean httpClientClosed = false;
try {
client.getReceiveBufferSize();
} catch (IllegalStateException e) {
httpClientClosed = true;
} catch (RuntimeException e) {
logger.debug(getStackTrace(e));
}
return httpClientClosed;
}
/**
* Sets the remote user.
*
* @param remoteUser the remote user
* @return this
*/
public BackendSession setRemoteUser(String remoteUser) {
this.id = remoteUser;
return this;
}
}