/**
* 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.ambari.server.state;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.ObjectNotFoundException;
import org.apache.ambari.server.ServiceComponentNotFoundException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.controller.ServiceResponse;
import org.apache.ambari.server.events.MaintenanceModeEvent;
import org.apache.ambari.server.events.ServiceInstalledEvent;
import org.apache.ambari.server.events.ServiceRemovedEvent;
import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
import org.apache.ambari.server.orm.dao.ClusterDAO;
import org.apache.ambari.server.orm.dao.ClusterServiceDAO;
import org.apache.ambari.server.orm.dao.ServiceConfigDAO;
import org.apache.ambari.server.orm.dao.ServiceDesiredStateDAO;
import org.apache.ambari.server.orm.dao.StackDAO;
import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
import org.apache.ambari.server.orm.entities.ClusterEntity;
import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
import org.apache.ambari.server.orm.entities.ClusterServiceEntityPK;
import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
import org.apache.ambari.server.orm.entities.ServiceDesiredStateEntity;
import org.apache.ambari.server.orm.entities.ServiceDesiredStateEntityPK;
import org.apache.ambari.server.orm.entities.StackEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.google.inject.persist.Transactional;
public class ServiceImpl implements Service {
private final Lock lock = new ReentrantLock();
private ServiceDesiredStateEntityPK serviceDesiredStateEntityPK;
private ClusterServiceEntityPK serviceEntityPK;
private static final Logger LOG = LoggerFactory.getLogger(ServiceImpl.class);
private final Cluster cluster;
private final ConcurrentMap<String, ServiceComponent> components = new ConcurrentHashMap<>();
private boolean isClientOnlyService;
private boolean isCredentialStoreSupported;
private boolean isCredentialStoreRequired;
private AmbariMetaInfo ambariMetaInfo;
@Inject
private ServiceConfigDAO serviceConfigDAO;
private final ClusterServiceDAO clusterServiceDAO;
private final ServiceDesiredStateDAO serviceDesiredStateDAO;
private final ClusterDAO clusterDAO;
private final ServiceComponentFactory serviceComponentFactory;
/**
* Data access object for retrieving stack instances.
*/
private final StackDAO stackDAO;
/**
* Used to publish events relating to service CRUD operations.
*/
private final AmbariEventPublisher eventPublisher;
/**
* The name of the service.
*/
private final String serviceName;
@AssistedInject
ServiceImpl(@Assisted Cluster cluster, @Assisted String serviceName, ClusterDAO clusterDAO,
ClusterServiceDAO clusterServiceDAO, ServiceDesiredStateDAO serviceDesiredStateDAO,
ServiceComponentFactory serviceComponentFactory, StackDAO stackDAO,
AmbariMetaInfo ambariMetaInfo, AmbariEventPublisher eventPublisher)
throws AmbariException {
this.cluster = cluster;
this.clusterDAO = clusterDAO;
this.clusterServiceDAO = clusterServiceDAO;
this.serviceDesiredStateDAO = serviceDesiredStateDAO;
this.serviceComponentFactory = serviceComponentFactory;
this.stackDAO = stackDAO;
this.eventPublisher = eventPublisher;
this.serviceName = serviceName;
this.ambariMetaInfo = ambariMetaInfo;
ClusterServiceEntity serviceEntity = new ClusterServiceEntity();
serviceEntity.setClusterId(cluster.getClusterId());
serviceEntity.setServiceName(serviceName);
ServiceDesiredStateEntity serviceDesiredStateEntity = new ServiceDesiredStateEntity();
serviceDesiredStateEntity.setServiceName(serviceName);
serviceDesiredStateEntity.setClusterId(cluster.getClusterId());
serviceDesiredStateEntityPK = getServiceDesiredStateEntityPK(serviceDesiredStateEntity);
serviceEntityPK = getServiceEntityPK(serviceEntity);
serviceDesiredStateEntity.setClusterServiceEntity(serviceEntity);
serviceEntity.setServiceDesiredStateEntity(serviceDesiredStateEntity);
StackId stackId = cluster.getDesiredStackVersion();
StackEntity stackEntity = stackDAO.find(stackId.getStackName(), stackId.getStackVersion());
serviceDesiredStateEntity.setDesiredStack(stackEntity);
ServiceInfo sInfo = ambariMetaInfo.getService(stackId.getStackName(),
stackId.getStackVersion(), serviceName);
isClientOnlyService = sInfo.isClientOnlyService();
isCredentialStoreSupported = sInfo.isCredentialStoreSupported();
isCredentialStoreRequired = sInfo.isCredentialStoreRequired();
persist(serviceEntity);
}
@AssistedInject
ServiceImpl(@Assisted Cluster cluster, @Assisted ClusterServiceEntity serviceEntity,
ClusterDAO clusterDAO, ClusterServiceDAO clusterServiceDAO,
ServiceDesiredStateDAO serviceDesiredStateDAO,
ServiceComponentFactory serviceComponentFactory, StackDAO stackDAO,
AmbariMetaInfo ambariMetaInfo, AmbariEventPublisher eventPublisher)
throws AmbariException {
this.cluster = cluster;
this.clusterDAO = clusterDAO;
this.clusterServiceDAO = clusterServiceDAO;
this.serviceDesiredStateDAO = serviceDesiredStateDAO;
this.serviceComponentFactory = serviceComponentFactory;
this.stackDAO = stackDAO;
this.eventPublisher = eventPublisher;
serviceName = serviceEntity.getServiceName();
this.ambariMetaInfo = ambariMetaInfo;
ServiceDesiredStateEntity serviceDesiredStateEntity = serviceEntity.getServiceDesiredStateEntity();
serviceDesiredStateEntityPK = getServiceDesiredStateEntityPK(serviceDesiredStateEntity);
serviceEntityPK = getServiceEntityPK(serviceEntity);
if (!serviceEntity.getServiceComponentDesiredStateEntities().isEmpty()) {
for (ServiceComponentDesiredStateEntity serviceComponentDesiredStateEntity
: serviceEntity.getServiceComponentDesiredStateEntities()) {
try {
components.put(serviceComponentDesiredStateEntity.getComponentName(),
serviceComponentFactory.createExisting(this,
serviceComponentDesiredStateEntity));
} catch(ProvisionException ex) {
StackId stackId = cluster.getCurrentStackVersion();
LOG.error(String.format("Can not get component info: stackName=%s, stackVersion=%s, serviceName=%s, componentName=%s",
stackId.getStackName(), stackId.getStackVersion(),
serviceEntity.getServiceName(),serviceComponentDesiredStateEntity.getComponentName()));
ex.printStackTrace();
}
}
}
StackId stackId = getDesiredStackVersion();
ServiceInfo sInfo = ambariMetaInfo.getService(stackId.getStackName(),
stackId.getStackVersion(), getName());
isClientOnlyService = sInfo.isClientOnlyService();
isCredentialStoreSupported = sInfo.isCredentialStoreSupported();
isCredentialStoreRequired = sInfo.isCredentialStoreRequired();
}
/***
* Refresh Service info due to current stack
* @throws AmbariException
*/
@Override
public void updateServiceInfo() throws AmbariException {
try {
ServiceInfo serviceInfo = ambariMetaInfo.getService(cluster.getDesiredStackVersion().getStackName(),
cluster.getDesiredStackVersion().getStackVersion(), getName());
isClientOnlyService = serviceInfo.isClientOnlyService();
isCredentialStoreSupported = serviceInfo.isCredentialStoreSupported();
isCredentialStoreRequired = serviceInfo.isCredentialStoreRequired();
} catch (ObjectNotFoundException e) {
throw new RuntimeException("Trying to create a ServiceInfo"
+ " not recognized in stack info"
+ ", clusterName=" + cluster.getClusterName()
+ ", serviceName=" + getName()
+ ", stackInfo=" + cluster.getDesiredStackVersion().getStackName());
}
}
@Override
public String getName() {
return serviceName;
}
@Override
public long getClusterId() {
return cluster.getClusterId();
}
@Override
public Map<String, ServiceComponent> getServiceComponents() {
return new HashMap<>(components);
}
@Override
public void addServiceComponents(
Map<String, ServiceComponent> components) throws AmbariException {
for (ServiceComponent sc : components.values()) {
addServiceComponent(sc);
}
}
@Override
public void addServiceComponent(ServiceComponent component) throws AmbariException {
if (components.containsKey(component.getName())) {
throw new AmbariException("Cannot add duplicate ServiceComponent"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", serviceComponentName=" + component.getName());
}
components.put(component.getName(), component);
}
@Override
public ServiceComponent addServiceComponent(String serviceComponentName)
throws AmbariException {
ServiceComponent component = serviceComponentFactory.createNew(this, serviceComponentName);
addServiceComponent(component);
return component;
}
@Override
public ServiceComponent getServiceComponent(String componentName)
throws AmbariException {
ServiceComponent serviceComponent = components.get(componentName);
if (null == serviceComponent) {
throw new ServiceComponentNotFoundException(cluster.getClusterName(),
getName(), componentName);
}
return serviceComponent;
}
@Override
public State getDesiredState() {
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
return serviceDesiredStateEntity.getDesiredState();
}
@Override
public void setDesiredState(State state) {
if (LOG.isDebugEnabled()) {
LOG.debug("Setting DesiredState of Service" + ", clusterName="
+ cluster.getClusterName() + ", clusterId="
+ cluster.getClusterId() + ", serviceName=" + getName()
+ ", oldDesiredState=" + getDesiredState() + ", newDesiredState="
+ state);
}
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
serviceDesiredStateEntity.setDesiredState(state);
serviceDesiredStateDAO.merge(serviceDesiredStateEntity);
}
@Override
public SecurityState getSecurityState() {
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
return serviceDesiredStateEntity.getSecurityState();
}
@Override
public void setSecurityState(SecurityState securityState) throws AmbariException {
if(!securityState.isEndpoint()) {
throw new AmbariException("The security state must be an endpoint state");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Setting DesiredSecurityState of Service" + ", clusterName="
+ cluster.getClusterName() + ", clusterId="
+ cluster.getClusterId() + ", serviceName=" + getName()
+ ", oldDesiredSecurityState=" + getSecurityState()
+ ", newDesiredSecurityState=" + securityState);
}
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
serviceDesiredStateEntity.setSecurityState(securityState);
serviceDesiredStateDAO.merge(serviceDesiredStateEntity);
}
@Override
public StackId getDesiredStackVersion() {
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
StackEntity desiredStackEntity = serviceDesiredStateEntity.getDesiredStack();
if( null != desiredStackEntity ) {
return new StackId(desiredStackEntity);
} else {
return null;
}
}
@Override
public void setDesiredStackVersion(StackId stack) {
if (LOG.isDebugEnabled()) {
LOG.debug("Setting DesiredStackVersion of Service" + ", clusterName="
+ cluster.getClusterName() + ", clusterId="
+ cluster.getClusterId() + ", serviceName=" + getName()
+ ", oldDesiredStackVersion=" + getDesiredStackVersion()
+ ", newDesiredStackVersion=" + stack);
}
StackEntity stackEntity = stackDAO.find(stack.getStackName(), stack.getStackVersion());
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
serviceDesiredStateEntity.setDesiredStack(stackEntity);
serviceDesiredStateDAO.merge(serviceDesiredStateEntity);
}
@Override
public ServiceResponse convertToResponse() {
ServiceResponse r = new ServiceResponse(cluster.getClusterId(), cluster.getClusterName(),
getName(), getDesiredStackVersion().getStackId(), getDesiredState().toString(),
isCredentialStoreSupported(), isCredentialStoreEnabled());
r.setMaintenanceState(getMaintenanceState().name());
return r;
}
@Override
public Cluster getCluster() {
return cluster;
}
/**
* Get a true or false value specifying whether
* credential store is supported by this service.
*
* @return true or false
*/
@Override
public boolean isCredentialStoreSupported() {
return isCredentialStoreSupported;
}
/**
* Get a true or false value specifying whether
* credential store is required by this service.
*
* @return true or false
*/
@Override
public boolean isCredentialStoreRequired() {
return isCredentialStoreRequired;
}
/**
* Get a true or false value specifying whether
* credential store use is enabled for this service.
*
* @return true or false
*/
@Override
public boolean isCredentialStoreEnabled() {
ServiceDesiredStateEntity desiredStateEntity = getServiceDesiredStateEntity();
if (desiredStateEntity != null) {
return desiredStateEntity.isCredentialStoreEnabled();
} else {
LOG.warn("Trying to fetch a member from an entity object that may " +
"have been previously deleted, serviceName = " + getName());
}
return false;
}
/**
* Set a true or false value specifying whether this
* service is to be enabled for credential store use.
*
* @param credentialStoreEnabled - true or false
*/
@Override
public void setCredentialStoreEnabled(boolean credentialStoreEnabled) {
if (LOG.isDebugEnabled()) {
LOG.debug("Setting CredentialStoreEnabled of Service" + ", clusterName="
+ cluster.getClusterName() + ", clusterId="
+ cluster.getClusterId() + ", serviceName=" + getName()
+ ", oldCredentialStoreEnabled=" + isCredentialStoreEnabled()
+ ", newCredentialStoreEnabled=" + credentialStoreEnabled);
}
ServiceDesiredStateEntity desiredStateEntity = getServiceDesiredStateEntity();
if (desiredStateEntity != null) {
desiredStateEntity.setCredentialStoreEnabled(credentialStoreEnabled);
desiredStateEntity = serviceDesiredStateDAO.merge(desiredStateEntity);
} else {
LOG.warn("Setting a member on an entity object that may have been "
+ "previously deleted, serviceName = " + getName());
}
}
@Override
public void debugDump(StringBuilder sb) {
sb.append("Service={ serviceName=").append(getName())
.append(", clusterName=").append(cluster.getClusterName())
.append(", clusterId=").append(cluster.getClusterId())
.append(", desiredStackVersion=").append(getDesiredStackVersion())
.append(", desiredState=").append(getDesiredState())
.append(", components=[ ");
boolean first = true;
for (ServiceComponent sc : components.values()) {
if (!first) {
sb.append(" , ");
}
first = false;
sb.append("\n ");
sc.debugDump(sb);
sb.append(" ");
}
sb.append(" ] }");
}
/**
*
*/
private void persist(ClusterServiceEntity serviceEntity) {
persistEntities(serviceEntity);
// publish the service installed event
StackId stackId = cluster.getDesiredStackVersion();
cluster.addService(this);
ServiceInstalledEvent event = new ServiceInstalledEvent(getClusterId(), stackId.getStackName(),
stackId.getStackVersion(), getName());
eventPublisher.publish(event);
}
@Transactional
void persistEntities(ClusterServiceEntity serviceEntity) {
long clusterId = cluster.getClusterId();
ClusterEntity clusterEntity = clusterDAO.findById(clusterId);
serviceEntity.setClusterEntity(clusterEntity);
clusterServiceDAO.create(serviceEntity);
clusterEntity.getClusterServiceEntities().add(serviceEntity);
clusterDAO.merge(clusterEntity);
clusterServiceDAO.merge(serviceEntity);
}
@Override
public boolean canBeRemoved() {
//
// A service can be deleted if all it's components
// can be removed, irrespective of the state of
// the service itself.
//
for (ServiceComponent sc : components.values()) {
if (!sc.canBeRemoved()) {
LOG.warn("Found non removable component when trying to delete service" + ", clusterName="
+ cluster.getClusterName() + ", serviceName=" + getName() + ", componentName="
+ sc.getName());
return false;
}
}
return true;
}
@Transactional
void deleteAllServiceConfigs() throws AmbariException {
long clusterId = getClusterId();
ServiceConfigEntity lastServiceConfigEntity = serviceConfigDAO.findMaxVersion(clusterId, getName());
// de-select every configuration from the service
if (lastServiceConfigEntity != null) {
for (ClusterConfigEntity serviceConfigEntity : lastServiceConfigEntity.getClusterConfigEntities()) {
LOG.info("Disabling configuration {}", serviceConfigEntity);
serviceConfigEntity.setSelected(false);
serviceConfigEntity.setServiceDeleted(true);
clusterDAO.merge(serviceConfigEntity);
}
}
LOG.info("Deleting all configuration associations for {} on cluster {}", getName(), cluster.getClusterName());
List<ServiceConfigEntity> serviceConfigEntities =
serviceConfigDAO.findByService(cluster.getClusterId(), getName());
for (ServiceConfigEntity serviceConfigEntity : serviceConfigEntities) {
// Only delete the historical version information and not original
// config data
serviceConfigDAO.remove(serviceConfigEntity);
}
}
@Override
@Transactional
public void deleteAllComponents() throws AmbariException {
lock.lock();
try {
LOG.info("Deleting all components for service" + ", clusterName=" + cluster.getClusterName()
+ ", serviceName=" + getName());
// FIXME check dependencies from meta layer
for (ServiceComponent component : components.values()) {
if (!component.canBeRemoved()) {
throw new AmbariException("Found non removable component when trying to"
+ " delete all components from service" + ", clusterName=" + cluster.getClusterName()
+ ", serviceName=" + getName() + ", componentName=" + component.getName());
}
}
for (ServiceComponent serviceComponent : components.values()) {
serviceComponent.delete();
}
components.clear();
} finally {
lock.unlock();
}
}
@Override
public void deleteServiceComponent(String componentName)
throws AmbariException {
lock.lock();
try {
ServiceComponent component = getServiceComponent(componentName);
LOG.info("Deleting servicecomponent for cluster" + ", clusterName=" + cluster.getClusterName()
+ ", serviceName=" + getName() + ", componentName=" + componentName);
// FIXME check dependencies from meta layer
if (!component.canBeRemoved()) {
throw new AmbariException("Could not delete component from cluster"
+ ", clusterName=" + cluster.getClusterName()
+ ", serviceName=" + getName()
+ ", componentName=" + componentName);
}
component.delete();
components.remove(componentName);
} finally {
lock.unlock();
}
}
@Override
public boolean isClientOnlyService() {
return isClientOnlyService;
}
@Override
@Transactional
public void delete() throws AmbariException {
deleteAllComponents();
deleteAllServiceConfigs();
removeEntities();
// publish the service removed event
StackId stackId = cluster.getDesiredStackVersion();
ServiceRemovedEvent event = new ServiceRemovedEvent(getClusterId(), stackId.getStackName(),
stackId.getStackVersion(), getName());
eventPublisher.publish(event);
}
@Transactional
protected void removeEntities() throws AmbariException {
serviceDesiredStateDAO.removeByPK(serviceDesiredStateEntityPK);
ClusterServiceEntityPK pk = new ClusterServiceEntityPK();
pk.setClusterId(getClusterId());
pk.setServiceName(getName());
clusterServiceDAO.removeByPK(pk);
}
@Override
public void setMaintenanceState(MaintenanceState state) {
ServiceDesiredStateEntity serviceDesiredStateEntity = getServiceDesiredStateEntity();
serviceDesiredStateEntity.setMaintenanceState(state);
serviceDesiredStateDAO.merge(serviceDesiredStateEntity);
// broadcast the maintenance mode change
MaintenanceModeEvent event = new MaintenanceModeEvent(state, this);
eventPublisher.publish(event);
}
@Override
public MaintenanceState getMaintenanceState() {
return getServiceDesiredStateEntity().getMaintenanceState();
}
private ClusterServiceEntity getServiceEntity() {
return clusterServiceDAO.findByPK(serviceEntityPK);
}
private ClusterServiceEntityPK getServiceEntityPK(ClusterServiceEntity serviceEntity) {
ClusterServiceEntityPK pk = new ClusterServiceEntityPK();
pk.setClusterId(serviceEntity.getClusterId());
pk.setServiceName(serviceEntity.getServiceName());
return pk;
}
private ServiceDesiredStateEntityPK getServiceDesiredStateEntityPK(ServiceDesiredStateEntity serviceDesiredStateEntity) {
ServiceDesiredStateEntityPK pk = new ServiceDesiredStateEntityPK();
pk.setClusterId(serviceDesiredStateEntity.getClusterId());
pk.setServiceName(serviceDesiredStateEntity.getServiceName());
return pk;
}
// Refresh the cached reference on setters
private ServiceDesiredStateEntity getServiceDesiredStateEntity() {
return serviceDesiredStateDAO.findByPK(serviceDesiredStateEntityPK);
}
}