blob: 98faad2b28c179537931efff80f649dc9e04666d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.apache.geode.internal.cache;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.geode.DataSerializer;
import org.apache.geode.annotations.Immutable;
import org.apache.geode.distributed.DistributedSystemDisconnectedException;
import org.apache.geode.distributed.Locator;
import org.apache.geode.distributed.internal.DistributionAdvisee;
import org.apache.geode.distributed.internal.DistributionAdvisor;
import org.apache.geode.distributed.internal.InternalLocator;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.internal.serialization.DeserializationContext;
import org.apache.geode.internal.serialization.SerializationContext;
/**
* Used to share code with BridgeServerAdvisor and ControllerAdvisor
*/
public abstract class GridAdvisor extends DistributionAdvisor {
/** Creates a new instance of GridAdvisor */
protected GridAdvisor(DistributionAdvisee server) {
super(server);
}
private final Object cacheLock = new Object();
private volatile List/* <BridgeServerProfile> */ cachedBridgeServerProfiles;
private volatile List/* <ControllerProfile> */ cachedControllerProfiles;
private volatile Set/* <DistributedMember> */ cachedBridgeServerAdvise;
private volatile Set/* <DistributedMember> */ cachedControllerAdvise;
@Immutable
private static final Filter CONTROLLER_FILTER = new Filter() {
@Override
public boolean include(Profile profile) {
return profile instanceof ControllerAdvisor.ControllerProfile;
}
};
@Immutable
private static final Filter BRIDGE_SERVER_FILTER = new Filter() {
@Override
public boolean include(Profile profile) {
return profile instanceof CacheServerAdvisor.CacheServerProfile;
}
};
/**
* Return an unmodifiable Set<DistributedMember> of the cnx controllers in this system.
*/
public Set adviseControllers() {
Set/* <DistributedMember> */ result = this.cachedControllerAdvise;
if (result == null) {
synchronized (this.cacheLock) {
result = this.cachedControllerAdvise;
if (result == null) {
result = Collections.unmodifiableSet(adviseFilter(CONTROLLER_FILTER));
this.cachedControllerAdvise = result;
}
}
}
return result;
}
/**
* Return an unmodifiable Set<DistributedMember> of the cache servers in this system.
*/
public Set adviseBridgeServers() {
Set/* <DistributedMember> */ result = this.cachedBridgeServerAdvise;
if (result == null) {
synchronized (this.cacheLock) {
result = this.cachedBridgeServerAdvise;
if (result == null) {
result = Collections.unmodifiableSet(adviseFilter(BRIDGE_SERVER_FILTER));
this.cachedBridgeServerAdvise = result;
}
}
}
return result;
}
/**
* Returns an unmodifiable {@code List} of the {@code BridgeServerProfile}s for all known bridge
* servers.
*/
public List/* <BridgeServerProfile> */ fetchBridgeServers() {
List/* <BridgeServerProfile> */ result = null;
// TODO: remove double-checking
if (result == null) {
synchronized (this.cacheLock) {
// result = this.cachedBridgeServerProfiles;
if (result == null) {
result = fetchProfiles(BRIDGE_SERVER_FILTER);
this.cachedBridgeServerProfiles = result;
}
}
}
return result;
}
/**
* Returns an unmodifiable {@code List} of the {@code ControllerProfile}s for all known cnx
* controllers.
*/
public List/* <ControllerProfile> */ fetchControllers() {
List/* <ControllerProfile> */ result = this.cachedControllerProfiles;
if (result == null) {
synchronized (this.cacheLock) {
result = this.cachedControllerProfiles;
if (result == null) {
result = fetchProfiles(CONTROLLER_FILTER);
this.cachedControllerProfiles = result;
}
}
}
return result;
}
public int getBridgeServerCount() {
List/* <BridgeServerProfile> */ l = this.cachedBridgeServerProfiles;
if (l == null) {
l = fetchProfiles(BRIDGE_SERVER_FILTER);
}
return l.size();
}
public int getControllerCount() {
List/* <ControllerProfile> */ l = this.cachedControllerProfiles;
if (l == null) {
l = fetchProfiles(CONTROLLER_FILTER);
}
return l.size();
}
/**
* Need ALL others (both normal members and admin members).
*/
@Override
public boolean useAdminMembersForDefault() {
return true;
}
/**
* Just always return true. This method could use getAllOtherMembers but it would cause us to make
* lots of copies of the member set. The problem with our super.isCurrentMember is it ignores
* admin members.
*/
@Override
protected boolean isCurrentMember(Profile p) {
return true;
}
@Override
protected void profileCreated(Profile profile) {
profilesChanged();
}
@Override
protected void profileUpdated(Profile profile) {
profilesChanged();
}
@Override
protected void profileRemoved(Profile profile) {
profilesChanged();
}
/**
* Used to drop any cached profile views we have since the master list of profiles changed. The
* next time someone asks for a view it will be recomputed.
*/
protected void profilesChanged() {
if (pollIsInitialized()) {
// no need to synchronize here since all cached* fields are volatile
this.cachedBridgeServerProfiles = null;
this.cachedControllerProfiles = null;
this.cachedBridgeServerAdvise = null;
this.cachedControllerAdvise = null;
}
}
/**
* Tell everyone else who we are and find out who they are.
*/
public void handshake() {
if (initializationGate()) {
// Exchange with any local servers or controllers.
List<Profile> otherProfiles = new ArrayList<Profile>();
GridProfile profile = (GridProfile) createProfile();
profile.tellLocalBridgeServers(getDistributionManager().getCache(), false, true,
otherProfiles);
profile.tellLocalControllers(false, true, otherProfiles);
for (Profile otherProfile : otherProfiles) {
if (!otherProfile.equals(profile)) {
this.putProfile(otherProfile);
}
}
}
profilesChanged();
}
@Override
public void close() {
try {
new UpdateAttributesProcessor(getAdvisee(), true/* removeProfile */).distribute();
// Notify any local cache servers or controllers
// that we are closing.
GridProfile profile = (GridProfile) createProfile();
profile.tellLocalBridgeServers(getDistributionManager().getCache(), true, false, null);
profile.tellLocalControllers(true, false, null);
super.close();
} catch (DistributedSystemDisconnectedException ignore) {
// we are closing so ignore a shutdown exception.
}
profilesChanged();
}
@Override
public Set adviseProfileRemove() {
// Our set of profiles includes local members. However, the update
// attributes message doesn't seem to be able to handle being sent to local
// members
Set results = super.adviseProfileRemove();
results.remove(getDistributionManager().getId());
return results;
}
/**
* Describes profile data common for all Grid resources
*/
public abstract static class GridProfile extends DistributionAdvisor.Profile {
private String host;
/**
* a negative port value is used when creating a fake profile meant to only gather information
* about all available locators.
*/
private int port;
private ProfileId id;
/** for internal use, required for DataSerializer.readObject */
public GridProfile() {}
public GridProfile(InternalDistributedMember memberId, int version) {
super(memberId, version);
}
public GridProfile(GridProfile toCopy) {
super(toCopy.getDistributedMember(), toCopy.version);
this.host = toCopy.host;
this.port = toCopy.port;
finishInit();
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return this.host;
}
public int getPort() {
return this.port;
}
@Override
public ProfileId getId() {
if (this.id == null)
throw new IllegalStateException("profile id not yet initialized");
return this.id;
}
/**
* Tell local controllers about the received profile. Also if exchange profiles then add each
* local controller to reply.
*
* @since GemFire 5.7
*/
protected void tellLocalControllers(boolean removeProfile, boolean exchangeProfiles,
final List<Profile> replyProfiles) {
final List<Locator> locators = Locator.getLocators();
for (int i = 0; i < locators.size(); i++) {
InternalLocator l = (InternalLocator) locators.get(i);
DistributionAdvisee advisee = l.getServerLocatorAdvisee();
if (advisee != null && advisee.getProfile().equals(this)) {
continue;
}
// negative value for port indicates fake profile
// meant to only gather remote profiles during profile exchange
if (this.port > 0) {
handleDistributionAdvisee(advisee, removeProfile, exchangeProfiles, replyProfiles);
} else if (exchangeProfiles && advisee != null) {
replyProfiles.add(advisee.getProfile());
}
}
}
/**
* Tell local cache servers about the received profile. Also if exchange profiles then add each
* local cache server to reply.
*
* @since GemFire 5.7
*/
protected void tellLocalBridgeServers(InternalCache cache, boolean removeProfile,
boolean exchangeProfiles, final List<Profile> replyProfiles) {
if (cache != null && !cache.isClosed()) {
List<?> bridgeServers = cache.getCacheServersAndGatewayReceiver();
for (int i = 0; i < bridgeServers.size(); i++) {
CacheServerImpl bsi = (CacheServerImpl) bridgeServers.get(i);
if (bsi.isRunning()) {
if (bsi.getProfile().equals(this)) {
continue;
}
// negative value for port indicates fake
// profile meant to only gather remote profiles during profile
// exchange
if (this.port > 0) {
handleDistributionAdvisee(bsi, removeProfile, exchangeProfiles, replyProfiles);
} else if (exchangeProfiles) {
replyProfiles.add(bsi.getProfile());
}
}
}
}
}
@Override
public void toData(DataOutput out,
SerializationContext context) throws IOException {
super.toData(out, context);
DataSerializer.writeString(this.host, out);
DataSerializer.writePrimitiveInt(this.port, out);
}
@Override
public void fromData(DataInput in,
DeserializationContext context) throws IOException, ClassNotFoundException {
super.fromData(in, context);
this.host = DataSerializer.readString(in);
this.port = DataSerializer.readPrimitiveInt(in);
finishInit();
}
public void finishInit() {
this.id = new GridProfileId(this);
}
@Override
public void fillInToString(StringBuilder sb) {
super.fillInToString(sb);
sb.append("; host=").append(this.host);
sb.append("; port=").append(this.port);
}
}
/**
* Basic idea is to base id off of host and port of GridProfile
*/
public static class GridProfileId implements ProfileId {
private final GridProfile gp;
public GridProfileId(GridProfile gp) {
this.gp = gp;
}
public InternalDistributedMember getMemberId() {
return this.gp.getDistributedMember();
}
public String getHost() {
return this.gp.getHost();
}
public int getPort() {
return this.gp.getPort();
}
@Override
public String toString() {
return "GridProfile[host=" + this.gp.getHost() + ",port=" + gp.getPort() + ']';
}
@Override
public int hashCode() {
final String thisHost = this.gp.getHost();
final int thisPort = this.gp.getPort();
return thisHost != null ? (thisHost.hashCode() ^ thisPort) : thisPort;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GridProfileId) {
final GridProfileId other = (GridProfileId) obj;
if (this.gp.getPort() == other.gp.getPort()) {
final String thisHost = this.gp.getHost();
final String otherHost = other.gp.getHost();
if (thisHost != null) {
return thisHost.equals(otherHost);
} else {
return (otherHost == null);
}
}
}
return false;
}
}
}