/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.dataprocessing; import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; import com.servoy.j2db.server.annotations.TerracottaAutolockRead; import com.servoy.j2db.server.annotations.TerracottaAutolockWrite; import com.servoy.j2db.server.annotations.TerracottaInstrumentedClass; import com.servoy.j2db.server.annotations.TerracottaTransient; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.SerializableObject; /** * @author sebster */ @TerracottaInstrumentedClass public final class ClientInfo implements Serializable { private static final long serialVersionUID = 1L; private String clientId; private String hostIdentifier; private String hostName; private String hostAddress; private int hostPort = -1; private int applicationType; private String specialClientIndentifier; private String userUid; private String userName; // last successful authentication private String authenticatorType; private String authenticatorMethod; private String jsCredentials; // update tc-config.xml and obfuscation list if you modify this! (@TerracottaTransient annotation is just decorative) @TerracottaTransient private TimeZone timeZone; // normal transient fields, not terracotta transient; so these are clustered with terracotta // we could optimise clustering by making it so that servers this client does not belong to will not use or have access to these ever-changing timestamps (terracotta transient) private transient long loginTimestamp = 0; private transient long idleTimestamp = 0; private transient String[] groups; private long openSolutionTimestamp = 0; private int solutionReleaseNumber = -1; private int openSolutionId = -1; private List<String> infos = new ArrayList<String>();//to make it possible for developer to give a client a meaning full name/description in the admin page private String solutionIntendedToBeLoaded; /** * A private lock to synchronize read/write to the long valued properties. Since this lock is PRIVATE, all deadlock related issues are local to this class. * Since the methods consist of 1 statement, the lock is always given up immediately and no deadlock on this lock can occur. The lock must be a serializable * object because ClientInfo must be serializable, and the lock must always exist (and cannot be transient). * * The lock is also used for Terracotta (you need to lock on stuff that changes if you want changes to be broadcasted in the cluster). */ private final SerializableObject lock = new SerializableObject(); public ClientInfo() { initHostInfo(); } /** * @param clientInfo */ @TerracottaAutolockWrite public ClientInfo(ClientInfo clientInfo) { // create a copy synchronized (lock) { clientId = clientInfo.clientId; hostIdentifier = clientInfo.hostIdentifier; hostName = clientInfo.hostName; hostAddress = clientInfo.hostAddress; hostPort = clientInfo.hostPort; applicationType = clientInfo.applicationType; specialClientIndentifier = clientInfo.specialClientIndentifier; userUid = clientInfo.userUid; userName = clientInfo.userName; timeZone = clientInfo.timeZone; openSolutionId = clientInfo.openSolutionId; infos = new ArrayList<String>(clientInfo.infos); solutionIntendedToBeLoaded = clientInfo.solutionIntendedToBeLoaded; } } @TerracottaAutolockWrite public void addInfo(String info) { synchronized (lock) { if (!infos.contains(info)) infos.add(info); } } @TerracottaAutolockWrite public boolean removeInfo(String info) { synchronized (lock) { return infos.remove(info); } } @TerracottaAutolockWrite public void removeAllInfo() { synchronized (lock) { infos.clear(); } } @TerracottaAutolockWrite public void setInfos(List<String> infos) { synchronized (lock) { this.infos = infos; } } @TerracottaAutolockRead public List<String> getInfos() { synchronized (lock) { return infos; } } @TerracottaAutolockWrite public void setClientId(String clientId) { synchronized (lock) { this.clientId = clientId; } } @TerracottaAutolockRead public String getClientId() { synchronized (lock) { return clientId; } } @TerracottaAutolockWrite public void setHostIdentifier(String hostIdentifier) { synchronized (lock) { this.hostIdentifier = hostIdentifier; } } @TerracottaAutolockRead public String getHostIdentifier() { synchronized (lock) { return hostIdentifier; } } @TerracottaAutolockWrite public void setUserUid(String userUid) { synchronized (lock) { this.userUid = userUid; } } @TerracottaAutolockRead public String getUserUid() { synchronized (lock) { return userUid; } } /** * Returns the hostName. * * @return String */ @TerracottaAutolockRead public String getHostName() { synchronized (lock) { return hostName; } } /** * Returns the hostAddress. * * @return String */ @TerracottaAutolockRead public String getHostAddress() { synchronized (lock) { return hostAddress; } } /** * Sets the hostName. * * @param hostName The hostName to set */ @TerracottaAutolockWrite public void setHostName(String hostname) { synchronized (lock) { this.hostName = hostname; } } /** * Sets the hostAddress. * * @param hostAddress The hostAddress to set */ @TerracottaAutolockWrite public void setHostAddress(String ipAddress) { synchronized (lock) { this.hostAddress = ipAddress; } } /** * @return the timeZone */ public TimeZone getTimeZone() { return timeZone; } /** * @param timeZone the timeZone to set */ public void setTimeZone(TimeZone timeZone) { this.timeZone = timeZone; } /** * Returns the userName. * * @return String */ @TerracottaAutolockRead public String getUserName() { synchronized (lock) { return userName; } } /** * Sets the userName. * * @param userName The userName to set */ @TerracottaAutolockWrite public void setUserName(String userName) { synchronized (lock) { this.userName = userName; } } @TerracottaAutolockRead public String getSolutionIntendedToBeLoaded() { synchronized (lock) { return solutionIntendedToBeLoaded; } } /** * This property is used only in the specific situation when pre-import and post-import * hooks are being executed during solution import. In a common scenario by the time the * post-import hook is executed the server will be in maintenance mode, so normally no * client could connect. By setting this property, the server can check if the solution * that is intended to be loaded is a pre/post-import hook and still let the client * connect. * * @param solutionIntendedToBeLoaded The name of the solution to be loaded. */ @TerracottaAutolockWrite public void setSolutionIntendedToBeLoaded(String solutionIntendedToBeLoaded) { synchronized (lock) { this.solutionIntendedToBeLoaded = solutionIntendedToBeLoaded; } } @TerracottaAutolockRead public int getOpenSolutionId() { synchronized (lock) { return openSolutionId; } } @TerracottaAutolockRead public int getSolutionReleaseNumber() { synchronized (lock) { return solutionReleaseNumber; } } @TerracottaAutolockWrite public void setOpenSolutionId(int openSolutionId) { synchronized (lock) { this.openSolutionId = openSolutionId; } } @TerracottaAutolockWrite public void setSolutionReleaseNumber(int releaseNumber) { synchronized (lock) { this.solutionReleaseNumber = releaseNumber; } } @TerracottaAutolockWrite public void setLoginTimestamp(long loginTimestamp) { synchronized (lock) { this.loginTimestamp = loginTimestamp; } } @TerracottaAutolockRead public long getLoginTimestamp() { synchronized (lock) { return loginTimestamp; } } @TerracottaAutolockWrite public void setOpenSolutionTimestamp(long openSolutionTimestamp) { synchronized (lock) { this.openSolutionTimestamp = openSolutionTimestamp; } } @TerracottaAutolockRead public long getOpenSolutionTimestamp() { synchronized (lock) { return openSolutionTimestamp; } } @TerracottaAutolockWrite public void setIdleTimestamp(long idleTimestamp) { synchronized (lock) { this.idleTimestamp = idleTimestamp; } } @TerracottaAutolockRead public long getIdleTimestamp() { synchronized (lock) { return idleTimestamp; } } @Override @TerracottaAutolockRead public String toString() { synchronized (lock) { StringBuffer buffer = new StringBuffer(); // User part if (userName != null) { buffer.append(userName); buffer.append('@'); } else if (userUid != null) { buffer.append('['); buffer.append(userUid); buffer.append(']'); buffer.append('@'); } // Host part if (hostName != null) { buffer.append(hostName); if (hostAddress != null) { buffer.append('['); buffer.append(hostAddress); if (hostPort != -1) { buffer.append(':'); buffer.append(hostPort); } buffer.append(']'); } } else if (hostAddress != null) { buffer.append(hostAddress); } else { buffer.append("unknown"); //$NON-NLS-1$ } return buffer.toString(); } } @TerracottaAutolockWrite public void initHostInfo() { timeZone = TimeZone.getDefault(); synchronized (lock) { // TODO check a webclient never sets the timezone // so a webclient can always use the default of the server.. try { InetAddress inetAddress = InetAddress.getLocalHost(); hostName = inetAddress.getHostName(); hostAddress = inetAddress.getHostAddress(); hostIdentifier = hostName + '_' + hostAddress; } catch (UnknownHostException e) { hostIdentifier = "Failed " + new Date().getTime(); //$NON-NLS-1$ Debug.log("Could not resolve local host: " + e.getMessage()); //$NON-NLS-1$ } catch (Exception e) { hostIdentifier = "Failed " + new Date().getTime(); //$NON-NLS-1$ Debug.error(e); } } } @TerracottaAutolockRead public String[] getUserGroups() { synchronized (lock) { return groups; } } @TerracottaAutolockWrite public void setUserGroups(String[] g) { synchronized (lock) { groups = g; } } /** * */ @TerracottaAutolockWrite public void clearUserInfo() { synchronized (lock) { userUid = null; groups = null; userName = null; authenticatorType = null; authenticatorMethod = null; jsCredentials = null; } } @TerracottaAutolockRead public int getApplicationType() { synchronized (lock) { return this.applicationType; } } @TerracottaAutolockWrite public void setApplicationType(int applicationType) { synchronized (lock) { this.applicationType = applicationType; } } /** * @param specialClientIndentifier the specialClientIndentifier to set */ @TerracottaAutolockWrite public void setSpecialClientIndentifier(String specialClientIndentifier) { synchronized (lock) { this.specialClientIndentifier = specialClientIndentifier; } } /** * @return the specialClientIndentifier */ @TerracottaAutolockRead public String getSpecialClientIndentifier() { synchronized (lock) { return specialClientIndentifier; } } /** * @param port */ @TerracottaAutolockWrite public void setHostPort(int port) { synchronized (lock) { this.hostPort = port; } } @TerracottaAutolockWrite public void setLastAuthentication(String authenticatorType, String method, String jsCredentials) { synchronized (lock) { this.authenticatorType = authenticatorType; this.authenticatorMethod = method; this.jsCredentials = jsCredentials; } } @TerracottaAutolockRead public String getAuthenticatorType() { synchronized (lock) { return authenticatorType; } } @TerracottaAutolockRead public String getAuthenticatorMethod() { synchronized (lock) { return authenticatorMethod; } } @TerracottaAutolockRead public String getJsCredentials() { synchronized (lock) { return jsCredentials; } } }