/* * 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 java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.vertx.java.core.Vertx; import org.vertx.java.core.http.HttpClient; import org.vertx.java.core.json.JsonObject; import com.globo.galeb.bus.ICallbackConnectionCounter; import com.globo.galeb.bus.IQueueService; import com.globo.galeb.entity.EntitiesMap; import com.globo.galeb.request.RemoteUser; 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 Backend without pool. * * @author: See AUTHORS file. * @version: 1.0.0, Oct 23, 2014. */ public class BackendWithoutSessionPool extends EntitiesMap<BackendSession> implements ICallbackConnectionCounter, IBackend { /** The Constant NUM_CONNECTIONS_INFO. */ public static final String NUM_CONNECTIONS_INFO = "numConnections"; /** The Constant UUID_INFO_ID. */ public static final String UUID_INFO_ID = "uuid"; /** The Constant CLEANUP_SESSION_TIME. */ public static final long CLEANUP_SESSION_TIME = 1000L; /** The host name or IP. */ private final String host; /** The port. */ private final Integer port; /** The my uuid. */ private final String myUUID; /** The queue active connections. */ private final String queueActiveConnections; /** The cleanup session scheduler. */ private IScheduler cleanupSessionScheduler = new NullScheduler(); /** The is locked. */ private AtomicBoolean isLocked = new AtomicBoolean(false); /** The registered. */ private boolean registered = false; /** The num external sessions. */ private int numExternalSessions = 0; /** The prefix. */ private String prefix = UNDEF; /** The maxconn. */ private int maxconn = 0; /** * Class CleanUpSessionHandler. * * @author See AUTHORS file. * @version 1.0.0, Nov 4, 2014. */ class CleanUpSessionHandler implements ISchedulerHandler { /** The backend. */ private final BackendWithoutSessionPool backend; /** * Instantiates a new clean up session handler. * * @param backend the backend */ public CleanUpSessionHandler(BackendWithoutSessionPool backend) { this.backend = backend; } /* (non-Javadoc) * @see com.globo.galeb.scheduler.ISchedulerHandler#handle() */ @Override public void handle() { if (!getEntities().isEmpty() && !backend.isLocked.get()) { backend.isLocked.set(true); Map<String, BackendSession> tmpSessions = new HashMap<>(getEntities()); for (BackendSession backendSession : tmpSessions.values()) { if (backendSession.isClosed()) { removeEntity(backendSession); } } backend.isLocked.set(false); } publishConnection(getEntities().size()); setNumExternalSessions(0); } } /** * Instantiates a new backend. * * @param json the json */ public BackendWithoutSessionPool(JsonObject json) { super(json); String[] hostWithPortArray = id!=null ? id.split(":") : null; if (hostWithPortArray != null && hostWithPortArray.length>1) { this.host = hostWithPortArray[0]; int myPort; try { myPort = Integer.parseInt(hostWithPortArray[1]); } catch (NumberFormatException e) { myPort = 80; } this.port = myPort; } else { this.host = id; this.port = 80; } this.queueActiveConnections = String.format("%s%s", IQueueService.QUEUE_BACKEND_CONNECTIONS_PREFIX, this); this.myUUID = UUID.randomUUID().toString(); } /* (non-Javadoc) * @see com.globo.galeb.core.entity.Entity#start() */ @Override public void start() { registerConnectionsCounter(); publishConnection(0); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getHost() */ @Override public String getHost() { return host; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getPort() */ @Override public Integer getPort() { return port; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getConnectionTimeout() */ @Override public Integer getConnectionTimeout() { return (Integer) getOrCreateProperty(CONNECTION_TIMEOUT_FIELDNAME, DEFAULT_CONNECTION_TIMEOUT); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#setConnectionTimeout(java.lang.Integer) */ @Override public IBackend setConnectionTimeout(Integer timeout) { properties.putNumber(CONNECTION_TIMEOUT_FIELDNAME, timeout); updateModifiedTimestamp(); return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#isKeepalive() */ @Override public Boolean isKeepalive() { return (Boolean) getOrCreateProperty(KEEPALIVE_FIELDNAME, DEFAULT_KEEPALIVE); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#setKeepAlive(boolean) */ @Override public IBackend setKeepAlive(boolean keepalive) { properties.putBoolean(KEEPALIVE_FIELDNAME, keepalive); updateModifiedTimestamp(); return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getKeepAliveMaxRequest() */ @Override public Long getKeepAliveMaxRequest() { return (Long) getOrCreateProperty(KEEPALIVE_MAXREQUEST_FIELDNAME, DEFAULT_KEEPALIVE_MAXREQUEST); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#setKeepAliveMaxRequest(java.lang.Long) */ @Override public IBackend setKeepAliveMaxRequest(Long maxRequestCount) { properties.putNumber(KEEPALIVE_MAXREQUEST_FIELDNAME, maxRequestCount); updateModifiedTimestamp(); return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getKeepAliveTimeOut() */ @Override public Long getKeepAliveTimeOut() { return (Long) getOrCreateProperty(KEEPALIVE_TIMEOUT_FIELDNAME, DEFAULT_KEEPALIVE_TIMEOUT); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#setKeepAliveTimeOut(java.lang.Long) */ @Override public IBackend setKeepAliveTimeOut(Long keepAliveTimeOut) { properties.putNumber(KEEPALIVE_TIMEOUT_FIELDNAME, keepAliveTimeOut); updateModifiedTimestamp(); return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getMaxPoolSize() */ @Override public Integer getMaxPoolSize() { return (Integer) getOrCreateProperty(MAXPOOL_SIZE_FIELDNAME, DEFAULT_MAX_POOL_SIZE); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#setMaxPoolSize(java.lang.Integer) */ @Override public IBackend setMaxPoolSize(Integer maxPoolSize) { properties.putNumber(MAXPOOL_SIZE_FIELDNAME, maxPoolSize); updateModifiedTimestamp(); return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#isUsePooledBuffers() */ @Override public Boolean isUsePooledBuffers() { return (Boolean) getOrCreateProperty(USE_POOLED_BUFFERS_FIELDNAME, DEFAULT_USE_POOLED_BUFFERS); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getSendBufferSize() */ @Override public Integer getSendBufferSize() { return (Integer) getOrCreateProperty(SEND_BUFFER_SIZE_FIELDNAME, DEFAULT_SEND_BUFFER_SIZE); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getReceiveBufferSize() */ @Override public Integer getReceiveBufferSize() { return (Integer) getOrCreateProperty(RECEIVED_BUFFER_SIZE_FIELDNAME, DEFAULT_RECEIVE_BUFFER_SIZE); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#isPipelining() */ @Override public Boolean isPipelining() { return (Boolean) getOrCreateProperty(PIPELINING_FIELDNAME, DEFAULT_PIPELINING); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getMinSessionPoolSize() */ @Override public int getMinSessionPoolSize() { return 0; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#setMinSessionPoolSize(int) */ @Override public BackendWithoutSessionPool setMinSessionPoolSize(int minPoolSize) { return this; } /* (non-Javadoc) * @see com.globo.galeb.entity.impl.backend.IBackend#setMaxConn(int) */ @Override public IBackend setMaxConn(int maxConn) { this.maxconn = maxConn; return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#connect(com.globo.galeb.core.RemoteUser) */ @Override public HttpClient connect(RemoteUser remoteUser) { if (remoteUser==null) { return null; } if (maxconn>0) { if (getActiveConnections()>maxconn-1) { return null; } } String remoteUserId = remoteUser.toString(); if (cleanupSessionScheduler instanceof NullScheduler && getPlataform() instanceof Vertx) { cleanupSessionScheduler = new VertxPeriodicScheduler((Vertx) getPlataform()) .setPeriod(CLEANUP_SESSION_TIME) .setHandler(new CleanUpSessionHandler(this)) .start(); } BackendSession backendSession = getEntityById(remoteUserId); if (backendSession==null) { backendSession = new BackendSession(new JsonObject().putString(ID_FIELDNAME, remoteUserId) .putString(PARENT_ID_FIELDNAME, id) .putObject(PROPERTIES_FIELDNAME, properties)); addEntity(backendSession); String backendId = this.toString(); if (!"".equals(parentId) && !"UNDEF".equals(parentId) && !"".equals(backendId) && !"UNDEF".equals(backendId)) { counter.sendActiveSessions(prefix, backendId, 1L); } } return backendSession.connect(); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#close(java.lang.String) */ @Override public void close(String remoteUser) throws RuntimeException { if (remoteUser==null || "".equals(remoteUser) || UNDEF.equals(remoteUser)) { return; } BackendSession backendSession = getEntityById(remoteUser); if (backendSession!=null) { removeEntity(remoteUser); if (!backendSession.isClosed()) { backendSession.close(); } backendSession = null; } } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#getActiveConnections() */ @Override public int getActiveConnections() { return getEntities().size() + numExternalSessions; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#isClosed(java.lang.String) */ @Override public boolean isClosed(String remoteUser) { if (!(remoteUser==null) && getEntityById(remoteUser)!=null) { return getEntityById(remoteUser).isClosed(); } return true; } /* (non-Javadoc) * @see com.globo.galeb.core.Entity#toJson() */ @Override public JsonObject toJson() { prepareJson(); idObj.putNumber(ACTIVE_CONNECTIONS_FIELDNAME, getActiveConnections()); return super.toJson(); } /** * Register connections counter. */ public void registerConnectionsCounter() { if (queueService!=null) { queueService.registerConnectionsCounter(this, queueActiveConnections); } } /** * Unregister connections counter. */ public void unregisterConnectionsCounter() { if (queueService!=null) { publishConnection(0); queueService.unregisterConnectionsCounter(this, queueActiveConnections); } } /** * Publish connection. * * @param numConnections the num connections */ public void publishConnection(int numConnections) { if (queueService!=null) { queueService.publishActiveConnections(queueActiveConnections, makeConnectionInfoMessage(numConnections)); } } /** * Make connection info message. * * @param numConnection the num connection * @return the json object */ public JsonObject makeConnectionInfoMessage(int numConnection) { JsonObject myConnections = new JsonObject(); myConnections.putString(UUID_INFO_ID, myUUID); myConnections.putNumber(NUM_CONNECTIONS_INFO, numConnection); return myConnections; } /* (non-Javadoc) * @see com.globo.galeb.core.bus.ICallbackConnectionCounter#setRegistered(boolean) */ @Override public void setRegistered(boolean registered) { this.registered = registered; } /* (non-Javadoc) * @see com.globo.galeb.core.bus.ICallbackConnectionCounter#isRegistered() */ @Override public boolean isRegistered() { return registered; } /* (non-Javadoc) * @see com.globo.galeb.core.bus.ICallbackConnectionCounter#callbackGlobalConnectionsInfo(org.(Vertx) getPlataform().java.core.json.JsonObject) */ @Override public void callbackGlobalConnectionsInfo(JsonObject message) { String uuid = message.getString(UUID_INFO_ID); if (uuid != myUUID) { int numConnections = message.getInteger(NUM_CONNECTIONS_INFO); if (numConnections>=0) { numExternalSessions += numConnections; } else { numExternalSessions = 0; } } } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#startSessionPool() */ @Override public IBackend startSessionPool() { return this; } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#closeAllForced() */ @Override public void closeAllForced() { for (BackendSession backendSession: getEntities().values()) { backendSession.close(); } clearEntities(); cleanupSessionScheduler.cancel(); } /* (non-Javadoc) * @see com.globo.galeb.core.IBackend#closeAll() */ @Override public void closeAll() { for (String remoteUser: getEntities().keySet()) { close(remoteUser); } } /* (non-Javadoc) * @see com.globo.galeb.entity.impl.backend.IBackend#setMetricPrefix(java.lang.String) */ @Override public IBackend setMetricPrefix(String prefix) { this.prefix = prefix; return this; } /** * Sets the num external sessions. * * @param numExternalSessions the new num external sessions */ private void setNumExternalSessions(int numExternalSessions) { this.numExternalSessions = numExternalSessions; } }