package com.tesora.dve.common.catalog; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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/>. * #L% */ import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.tesora.dve.worker.WorkerFactory; import io.netty.channel.EventLoopGroup; import org.apache.log4j.Logger; import com.tesora.dve.common.UserVisibleDatabase; import com.tesora.dve.common.catalog.CatalogDAO.CatalogDAOFactory; import com.tesora.dve.exceptions.PECodingException; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.membership.GroupTopicPublisher; import com.tesora.dve.groupmanager.SiteFailureMessage; import com.tesora.dve.resultset.ColumnSet; import com.tesora.dve.resultset.ResultRow; import com.tesora.dve.server.statistics.SiteStatKey.SiteType; import com.tesora.dve.server.statistics.manager.LogSiteStatisticRequest; import com.tesora.dve.singleton.Singletons; import com.tesora.dve.sql.schema.PEStorageSite.TCacheSite; import com.tesora.dve.worker.AdditionalConnectionInfo; import com.tesora.dve.worker.UserAuthentication; import com.tesora.dve.worker.Worker; @Entity @Table(name = "storage_site", uniqueConstraints = @UniqueConstraint(columnNames = "name")) public class PersistentSite implements CatalogEntity, StorageSite { transient private Logger logger = Logger.getLogger(PersistentSite.class); private static final long serialVersionUID = 1L; @Id @GeneratedValue int id; String name; String haType; @OneToMany(cascade = CascadeType.ALL, mappedBy = "storageSite", fetch = FetchType.EAGER) @OrderBy("id ASC") List<SiteInstance> siteInstances; transient SiteInstance masterInstance = null; private transient ColumnSet showColumnSet = null; PersistentSite() { } public PersistentSite(String name, SiteInstance siteInstance) throws PEException { this(name); addInstance(siteInstance); haType = WorkerFactory.SINGLE_DIRECT_HA_TYPE; } public PersistentSite(String name) { this(name, WorkerFactory.SINGLE_DIRECT_HA_TYPE); } public PersistentSite(String name, String haType) { this.name = name; this.haType = haType; this.siteInstances = new ArrayList<SiteInstance>(); } @Override public String getMasterUrl() { if (getMasterInstance() == null) { return null; } return getMasterInstance().getInstanceURL(); } @Override public String getName() { return name; } public String getHAType() { return haType; } @Override public int getId() { return id; } public void addInstance(SiteInstance instance) throws PEException { if (instance == null) return; getSiteInstances().add(instance); instance.setStorageSite(this); if (getMasterInstance() == null) setMasterInstance(instance); } public void removeInstance(SiteInstance instance) { synchronized (this) { if (instance.equals(getMasterInstance())) masterInstance = null; for (Iterator<SiteInstance> i = getSiteInstances().iterator(); i.hasNext();) { if (instance.equals(i.next())) { i.remove(); break; } } if (masterInstance == null && !getSiteInstances().isEmpty()) setMasterInstance(siteInstances.get(0)); } } public void addAll(SiteInstance[] replicants) throws PEException { for (SiteInstance instance : replicants) addInstance(instance); } public void setMasterInstance(SiteInstance instance) { synchronized (this) { if (getMasterInstance() != null) masterInstance.setMaster(false); masterInstance = instance; if (masterInstance != null) masterInstance.setMaster(true); } } public SiteInstance getMasterInstance() { synchronized (this) { if (masterInstance == null) { for (SiteInstance site : getSiteInstances()) { if (site.isMaster() && site.isEnabled()) { if (masterInstance == null) masterInstance = site; else throw new PECodingException("Site " + getName() + " has multiple master instances defined - aborting master instance selection"); } } } } return masterInstance; } @Override public PersistentSite getRecoverableSite(CatalogDAO c) { return this; } @Override public Worker pickWorker(Map<StorageSite, Worker> workerMap) throws PEException { return workerMap.get(this); } @Override public void annotateStatistics(LogSiteStatisticRequest sNotice) { sNotice.setSiteDetails(getName(), SiteType.PERSISTENT); } @Override public void incrementUsageCount() { } @Override public Worker createWorker(UserAuthentication auth, AdditionalConnectionInfo additionalConnInfo, EventLoopGroup preferredEventLoop) throws PEException { // If the user is the admin user then use site creds instead of user creds if(auth.isAdminUser()) return getWorkerFactory().newWorker(getMasterInstance().getAuthentication(), additionalConnInfo, this, preferredEventLoop); return getWorkerFactory().newWorker(auth, additionalConnInfo, this, preferredEventLoop); } protected WorkerFactory getWorkerFactory() { return WorkerFactory.getWorkerFactory(getHAType()); } public static void addWorkerFactory(String haType, WorkerFactory factory) { WorkerFactory.registerFactory(haType, factory); } public static boolean isValidHAType(String haType) { return WorkerFactory.hasFactoryFor(haType); } @Override public String toString() { return getClass().getSimpleName() + "(" + name + "/" + id + "," + getMasterInstance() + ")"; } @Override public ColumnSet getShowColumnSet(CatalogQueryOptions cqo) { if (showColumnSet == null) { showColumnSet = new ColumnSet(); showColumnSet.addColumn("Persistent Site", 255, "varchar", Types.VARCHAR); showColumnSet.addColumn("URL", 255, "varchar", Types.VARCHAR); } return showColumnSet; } @Override public ResultRow getShowResultRow(CatalogQueryOptions cqo) { ResultRow rr = new ResultRow(); rr.addResultColumn(this.name, false); rr.addResultColumn(this.masterInstance, false); return rr; } @Override public void removeFromParent() throws Throwable { // TODO Actually implement the removal of this instance from the parent } @Override public List<CatalogEntity> getDependentEntities(CatalogDAO c) throws Throwable { // TODO Return a valid list of dependents return Collections.emptyList(); } public static int computeHashCode(StorageSite ss) { final int prime = 31; int result = 1; result = prime * result + ((ss.getName() == null) ? 0 : ss.getName().hashCode()); return result; } @Override public int hashCode() { return computeHashCode(this); } @Override public boolean equals(Object obj) { /* * if (this == obj) return true; if (obj == null) return false; if * (getClass() != obj.getClass()) return false; PersistentSite other = * (PersistentSite) obj; if (name == null) { if (other.name != null) * return false; } else if (!name.equals(other.name)) return false; * return true; */ return computeEquals(this, obj); } public static boolean computeEquals(StorageSite left, Object right) { if (left == right) return true; // NOPMD by doug on 15/01/13 3:58 PM if (right == null) return false; if (!(left instanceof PersistentSite || left instanceof TCacheSite)) return false; if (!(right instanceof PersistentSite || right instanceof TCacheSite)) return false; StorageSite other = (StorageSite) right; if (left.getName() == null) { if (other.getName() != null) return false; } else if (!left.getName().equals(other.getName())) return false; return true; } @Override public void onSiteFailure(CatalogDAO c) throws PEException { logger.warn("Failure on site " + this + " (instance " + getMasterInstance().getName() + ", url: " + getMasterUrl() + ")"); getWorkerFactory().onSiteFailure(this); } public void ejectMaster() throws PEException { String failedMasterURL = getMasterUrl(); CatalogDAO c = CatalogDAOFactory.newInstance(); c.begin(); try { PersistentSite theSite = c.findByKey(PersistentSite.class, this.getId()); c.refreshForLock(theSite); // If the master after refresh doesn't match the master that we had // when we came in, // the master site has already been switched if (failedMasterURL.equals(theSite.getMasterUrl())) { SiteInstance newMaster = null; for (SiteInstance instance : theSite.getSiteInstances()) { if (!instance.isMaster() && instance.isEnabled()) { newMaster = instance; break; } } theSite.getMasterInstance().failSiteInstance(); theSite.setMasterInstance(newMaster); c.commit(); if (newMaster != null) { logger.info("Site " + theSite + " fails over to " + theSite.getMasterInstance().getName() + " (url: " + theSite.getMasterUrl() + ")"); SiteFailureMessage sfm = new SiteFailureMessage(theSite.getId()); Singletons.require(GroupTopicPublisher.class).publish(sfm); } else throw new PEException("No database instances available for site failover for site " + theSite); } } catch (PEException e) { c.rollback(e); throw e; // } finally { // c.close(); } } @Override public String getInstanceIdentifier() { return getWorkerFactory().getInstanceIdentifier(this, getMasterInstance()); } public void setHaType(String haType) { this.haType = haType; } public final List<SiteInstance> getSiteInstances() { return siteInstances; } @Override public int getMasterInstanceId() { return getMasterInstance().getId(); } @Override public void onUpdate() { } @Override public void onDrop() { } @Override public boolean supportsTransactions() { return true; } @Override public boolean hasDatabase(UserVisibleDatabase ctxDB) { // always true return true; } @Override public void setHasDatabase(UserVisibleDatabase ctxDB) throws PEException { throw new PECodingException("Invalid call to StorageSite.setHasDatabase"); } }