/** * The contents of this file are subject to the OpenMRS Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.module.sync.server; import java.text.DecimalFormat; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.openmrs.module.sync.SyncRecord; import org.openmrs.module.sync.SyncServerClass; import org.openmrs.module.sync.SyncTransmissionState; import org.openmrs.util.OpenmrsUtil; /** * Represents another server that we are going to sync to/from. */ public class RemoteServer { private Integer serverId; private String nickname; private String address; private RemoteServerType serverType; private String username; private String password; private Date lastSync; private SyncTransmissionState lastSyncState; private Set<SyncServerClass> serverClasses; private Set<SyncServerRecord> serverRecords; private String uuid; private Boolean disabled = false; private String childUsername = null; private static Map<Integer, Date> syncServersInProgress = new LinkedHashMap<Integer, Date>(); public Boolean getDisabled() { return disabled; } public void setDisabled(Boolean disabled) { this.disabled = disabled; } public String getUuid() { return uuid; } public void setUuid(String uuid) { this.uuid = uuid; } public Set<SyncServerClass> getServerClasses() { return serverClasses; } public void setServerClasses(Set<SyncServerClass> serverClasses) { this.serverClasses = serverClasses; } public Date getLastSync() { return lastSync; } public void setLastSync(Date lastSync) { this.lastSync = lastSync; } public SyncTransmissionState getLastSyncState() { return lastSyncState; } public void setLastSyncState(SyncTransmissionState value) { this.lastSyncState = value; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Boolean getIsSSL() { return this.address.startsWith("https"); } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getServerId() { return serverId; } public void setServerId(Integer serverId) { this.serverId = serverId; } public RemoteServerType getServerType() { return serverType; } public void setServerType(RemoteServerType serverType) { this.serverType = serverType; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Set<String> getClassesNotSent() { Set<String> ret = new HashSet<String>(); if (this.serverClasses != null) { for (SyncServerClass serverClass : this.serverClasses) { if (serverClass.getSendTo() == false) ret.add(serverClass.getSyncClass().getName()); } } return ret; } public Set<String> getClassesNotReceived() { Set<String> ret = new HashSet<String>(); if (this.serverClasses != null) { for (SyncServerClass serverClass : this.serverClasses) { if (serverClass.getReceiveFrom() == false) ret.add(serverClass.getSyncClass().getName()); } } return ret; } /** * Find out if a given sync record should be processed (i.e. ingested) by the provided server * based on the contained types. Note, method can be called both on parent and child; i.e on * child it is called while deciding what to send to the parent server (in this case object * instance of the RemoteServer is 'parent'). * * Remarks: The naming of methods shouldReceive() and shouldSend() is from the standpoint of * the server. * * @return */ public Boolean shouldBeSentSyncRecord(SyncRecord record) { Boolean ret = true; //assume it is good less we find match if (record == null) return false; StringBuffer recordTypesStrings = new StringBuffer(); Set<String> recordTypes = record.getContainedClassSet(); if (recordTypes == null) return ret; if (this.serverClasses == null) return ret; //build up the search string of types for (String type : recordTypes) { recordTypesStrings.append("<"); recordTypesStrings.append(type); recordTypesStrings.append(">"); } //now do the comparison, note these can have wild cards for (SyncServerClass serverClass : this.serverClasses) { if (serverClass.getSendTo() == false) { String typeToTest = serverClass.getSyncClass().getName(); if (Pattern.matches(".*<" + typeToTest + ".*>*", recordTypesStrings)) { ret = false; break; } } } return ret; } /** * Find out if given sync record should be processed (i.e. ingested) by this server based on the * contained types. * * Remarks: See shouldBeSentSyncRecord() for more background on how this works. * * @return */ public Boolean shouldReceiveSyncRecordFrom(SyncRecord record) { Boolean ret = true; //assume it is good less we find match if (record == null) return false; StringBuffer recordTypesStrings = new StringBuffer(); Set<String> recordTypes = record.getContainedClassSet(); if (recordTypes == null) return ret; if (this.serverClasses == null) return ret; //build up the search string of types for (String type : recordTypes) { recordTypesStrings.append("<"); recordTypesStrings.append(type); recordTypesStrings.append(">"); } //now do the comparison, note these can be package names too thus do the .* Pattern for (SyncServerClass serverClass : this.serverClasses) { if (serverClass.getReceiveFrom() == false) { String typeToExclude = serverClass.getSyncClass().getName(); if (Pattern.matches(".*<" + typeToExclude + ".*>*", recordTypesStrings)) { ret = false; break; } } } return ret; } @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + ((address == null) ? 0 : address.hashCode()); result = PRIME * result + ((uuid == null) ? 0 : uuid.hashCode()); result = PRIME * result + ((lastSync == null) ? 0 : lastSync.hashCode()); result = PRIME * result + ((nickname == null) ? 0 : nickname.hashCode()); result = PRIME * result + ((password == null) ? 0 : password.hashCode()); result = PRIME * result + ((serverClasses == null) ? 0 : serverClasses.hashCode()); result = PRIME * result + ((serverId == null) ? 0 : serverId.hashCode()); result = PRIME * result + ((serverType == null) ? 0 : serverType.hashCode()); result = PRIME * result + ((username == null) ? 0 : username.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof RemoteServer)) return false; final RemoteServer other = (RemoteServer) obj; if (OpenmrsUtil.nullSafeEquals(uuid, other.uuid)) return true; if (OpenmrsUtil.nullSafeEquals(serverId, other.serverId)) return true; // both the uuid and id are not equal, return false return false; } public String getChildUsername() { return childUsername; } public void setChildUsername(String childUsername) { this.childUsername = childUsername; } public Set<SyncServerRecord> getServerRecords() { return serverRecords; } public void setServerRecords(Set<SyncServerRecord> serverRecords) { this.serverRecords = serverRecords; } public Boolean getSyncInProgress() { synchronized (syncServersInProgress) { if (getServerId() == null) return false; else return syncServersInProgress.get(getServerId()) != null; } } /** * If given true, marks this current server as 'in progress' (static variable not to be saved in the database) * * @param syncInProgress */ public synchronized void setSyncInProgress(Boolean syncInProgress) { synchronized (syncServersInProgress) { if (syncInProgress) { syncServersInProgress.put(getServerId(), new Date()); } else { syncServersInProgress.remove(getServerId()); } } } private static DecimalFormat df = new DecimalFormat("0.00"); /** * @return the number of minutes since the sync was started. If this is new server or sync is * not in progress the empty string is returned */ public String getSyncInProgressMinutes() { synchronized (syncServersInProgress) { if (getServerId() == null || syncServersInProgress.get(getServerId()) == null) return ""; Long difference = System.currentTimeMillis() - syncServersInProgress.get(getServerId()).getTime(); return df.format((float)(difference) / 1000 / 60); } } @Override public String toString() { return "RemoteServer(" + getServerId() + "): " + getNickname(); } }