blob: 128cc295a85fe455046760cc2471494648c141e6 [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.ambari.server.state;
import java.util.*;
import java.util.Map.Entry;
import org.apache.ambari.server.AmbariException;
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.orm.dao.*;
import org.apache.ambari.server.orm.entities.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.google.inject.persist.Transactional;
public class ServiceImpl implements Service {
private ClusterServiceEntity serviceEntity;
private ServiceDesiredStateEntity serviceDesiredStateEntity;
private static final Logger LOG =
LoggerFactory.getLogger(ServiceImpl.class);
private boolean persisted = false;
private final Cluster cluster;
// [ String type -> Config Tag ], no need to hold the direct reference to the config
private Map<String, String> desiredConfigs;
private Map<String, ServiceComponent> components;
private final boolean isClientOnlyService;
@Inject
Gson gson;
@Inject
private ClusterServiceDAO clusterServiceDAO;
@Inject
private Clusters clusters;
@Inject
private ServiceDesiredStateDAO serviceDesiredStateDAO;
@Inject
private ServiceComponentDesiredStateDAO serviceComponentDesiredStateDAO;
@Inject
private ClusterDAO clusterDAO;
@Inject
private ServiceConfigMappingDAO serviceConfigMappingDAO;
@Inject
private ServiceComponentFactory serviceComponentFactory;
@Inject
private AmbariMetaInfo ambariMetaInfo;
private void init() {
// TODO load from DB during restart?
}
@AssistedInject
public ServiceImpl(@Assisted Cluster cluster, @Assisted String serviceName,
Injector injector) {
injector.injectMembers(this);
serviceEntity = new ClusterServiceEntity();
serviceEntity.setServiceName(serviceName);
serviceDesiredStateEntity = new ServiceDesiredStateEntity();
serviceDesiredStateEntity.setClusterServiceEntity(serviceEntity);
serviceEntity.setServiceDesiredStateEntity(serviceDesiredStateEntity);
this.cluster = cluster;
this.desiredConfigs = new HashMap<String, String>();
this.components = new HashMap<String, ServiceComponent>();
StackId stackId = cluster.getDesiredStackVersion();
setDesiredStackVersion(stackId);
ServiceInfo sInfo = ambariMetaInfo.getServiceInfo(stackId.getStackName(),
stackId.getStackVersion(), serviceName);
this.isClientOnlyService = sInfo.isClientOnlyService();
init();
}
@AssistedInject
public ServiceImpl(@Assisted Cluster cluster, @Assisted ClusterServiceEntity
serviceEntity, Injector injector) {
injector.injectMembers(this);
this.serviceEntity = serviceEntity;
this.cluster = cluster;
//TODO check for null states?
this.serviceDesiredStateEntity = serviceEntity.getServiceDesiredStateEntity();
this.desiredConfigs = new HashMap<String, String>();
this.components = new HashMap<String, ServiceComponent>();
if (!serviceEntity.getServiceComponentDesiredStateEntities().isEmpty()) {
for (ServiceComponentDesiredStateEntity serviceComponentDesiredStateEntity
: serviceEntity.getServiceComponentDesiredStateEntities()) {
components.put(serviceComponentDesiredStateEntity.getComponentName(),
serviceComponentFactory.createExisting(this,
serviceComponentDesiredStateEntity));
}
}
for (ServiceConfigMappingEntity mappingEntity :
serviceEntity.getServiceConfigMappings()) {
desiredConfigs.put(mappingEntity.getConfigType(),
mappingEntity.getVersionTag());
}
StackId stackId = getDesiredStackVersion();
ServiceInfo sInfo = ambariMetaInfo.getServiceInfo(stackId.getStackName(),
stackId.getStackVersion(), getName());
this.isClientOnlyService = sInfo.isClientOnlyService();
persisted = true;
}
@Override
public String getName() {
return serviceEntity.getServiceName();
}
@Override
public long getClusterId() {
return cluster.getClusterId();
}
@Override
public synchronized Map<String, ServiceComponent> getServiceComponents() {
return Collections.unmodifiableMap(components);
}
@Override
public synchronized void addServiceComponents(
Map<String, ServiceComponent> components) throws AmbariException {
for (ServiceComponent sc : components.values()) {
addServiceComponent(sc);
}
}
@Override
public synchronized void addServiceComponent(ServiceComponent component)
throws AmbariException {
// TODO validation
if (LOG.isDebugEnabled()) {
LOG.debug("Adding a ServiceComponent to Service"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", serviceComponentName=" + component.getName());
}
if (components.containsKey(component.getName())) {
throw new AmbariException("Cannot add duplicate ServiceComponent"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", serviceComponentName=" + component.getName());
}
this.components.put(component.getName(), component);
}
@Override
public synchronized ServiceComponent addServiceComponent(
String serviceComponentName) throws AmbariException {
if (LOG.isDebugEnabled()) {
LOG.debug("Adding a ServiceComponent to Service"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", serviceComponentName=" + serviceComponentName);
}
if (components.containsKey(serviceComponentName)) {
throw new AmbariException("Cannot add duplicate ServiceComponent"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", serviceComponentName=" + serviceComponentName);
}
ServiceComponent component = serviceComponentFactory.createNew(this, serviceComponentName);
this.components.put(component.getName(), component);
return component;
}
@Override
public ServiceComponent getServiceComponent(String componentName)
throws AmbariException {
if (!components.containsKey(componentName)) {
throw new ServiceComponentNotFoundException(cluster.getClusterName(),
getName(),
componentName);
}
return this.components.get(componentName);
}
@Override
public synchronized State getDesiredState() {
return this.serviceDesiredStateEntity.getDesiredState();
}
@Override
public synchronized void setDesiredState(State state) {
if (LOG.isDebugEnabled()) {
LOG.debug("Setting DesiredState of Service"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", oldDesiredState=" + this.getDesiredState()
+ ", newDesiredState=" + state);
}
this.serviceDesiredStateEntity.setDesiredState(state);
saveIfPersisted();
}
@Override
public synchronized Map<String, Config> getDesiredConfigs() {
Map<String, Config> map = new HashMap<String, Config>();
for (Entry<String, String> entry : desiredConfigs.entrySet()) {
Config config = cluster.getDesiredConfig(entry.getKey(), entry.getValue());
if (null != config) {
map.put(entry.getKey(), config);
} else {
// FIXME this is an error - should throw a proper exception
throw new RuntimeException("Found an invalid config"
+ ", clusterName=" + getCluster().getClusterName()
+ ", serviceName=" + getName()
+ ", configType=" + entry.getKey()
+ ", configVersionTag=" + entry.getValue());
}
}
return Collections.unmodifiableMap(map);
}
@Override
public synchronized void updateDesiredConfigs(Map<String, Config> configs) {
for (Entry<String,Config> entry : configs.entrySet()) {
boolean contains = false;
for (ServiceConfigMappingEntity serviceConfigMappingEntity : serviceEntity.getServiceConfigMappings()) {
if (entry.getKey().equals(serviceConfigMappingEntity.getConfigType())) {
contains = true;
serviceConfigMappingEntity.setTimestamp(new Date().getTime());
serviceConfigMappingEntity.setVersionTag(entry.getValue().getVersionTag());
}
}
if (!contains) {
ServiceConfigMappingEntity newEntity = new ServiceConfigMappingEntity();
newEntity.setClusterId(serviceEntity.getClusterId());
newEntity.setServiceName(serviceEntity.getServiceName());
newEntity.setConfigType(entry.getKey());
newEntity.setVersionTag(entry.getValue().getVersionTag());
newEntity.setTimestamp(new Date().getTime());
newEntity.setServiceEntity(serviceEntity);
serviceEntity.getServiceConfigMappings().add(newEntity);
}
this.desiredConfigs.put(entry.getKey(), entry.getValue().getVersionTag());
}
saveIfPersisted();
}
@Override
public synchronized StackId getDesiredStackVersion() {
return gson.fromJson(serviceDesiredStateEntity.getDesiredStackVersion(), StackId.class);
}
@Override
public synchronized void setDesiredStackVersion(StackId stackVersion) {
if (LOG.isDebugEnabled()) {
LOG.debug("Setting DesiredStackVersion of Service"
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", serviceName=" + getName()
+ ", oldDesiredStackVersion=" + getDesiredStackVersion()
+ ", newDesiredStackVersion=" + stackVersion);
}
serviceDesiredStateEntity.setDesiredStackVersion(gson.toJson(stackVersion));
saveIfPersisted();
}
@Override
public synchronized ServiceResponse convertToResponse() {
ServiceResponse r = new ServiceResponse(cluster.getClusterId(),
cluster.getClusterName(),
getName(),
desiredConfigs,
getDesiredStackVersion().getStackId(),
getDesiredState().toString());
return r;
}
@Override
public Cluster getCluster() {
return cluster;
}
@Override
public synchronized void debugDump(StringBuilder sb) {
sb.append("Service={ serviceName=" + getName()
+ ", clusterName=" + cluster.getClusterName()
+ ", clusterId=" + cluster.getClusterId()
+ ", desiredStackVersion=" + getDesiredStackVersion()
+ ", desiredState=" + getDesiredState().toString()
+ ", configs=[");
boolean first = true;
if (desiredConfigs != null) {
for (Entry<String, String> entry : desiredConfigs.entrySet()) {
if (!first) {
sb.append(" , ");
}
first = false;
sb.append("{ Config type=" + entry.getKey()
+ ", versionTag=" + entry.getValue() + "}");
}
}
sb.append("], components=[ ");
first = true;
for(ServiceComponent sc : components.values()) {
if (!first) {
sb.append(" , ");
}
first = false;
sb.append("\n ");
sc.debugDump(sb);
sb.append(" ");
}
sb.append(" ] }");
}
@Override
public synchronized boolean isPersisted() {
return persisted;
}
@Override
public synchronized void persist() {
if (!persisted) {
persistEntities();
refresh();
cluster.refresh();
persisted = true;
} else {
saveIfPersisted();
}
}
@Transactional
protected void persistEntities() {
ClusterEntity clusterEntity = clusterDAO.findById(cluster.getClusterId());
serviceEntity.setClusterEntity(clusterEntity);
clusterServiceDAO.create(serviceEntity);
serviceDesiredStateDAO.create(serviceDesiredStateEntity);
clusterEntity.getClusterServiceEntities().add(serviceEntity);
clusterDAO.merge(clusterEntity);
// serviceEntity =
clusterServiceDAO.merge(serviceEntity);
// serviceDesiredStateEntity =
serviceDesiredStateDAO.merge(serviceDesiredStateEntity);
}
@Transactional
private void saveIfPersisted() {
if (isPersisted()) {
clusterServiceDAO.merge(serviceEntity);
serviceDesiredStateDAO.merge(serviceDesiredStateEntity);
}
}
@Override
@Transactional
public synchronized void refresh() {
if (isPersisted()) {
ClusterServiceEntityPK pk = new ClusterServiceEntityPK();
pk.setClusterId(getClusterId());
pk.setServiceName(getName());
serviceEntity = clusterServiceDAO.findByPK(pk);
serviceDesiredStateEntity = serviceEntity.getServiceDesiredStateEntity();
clusterServiceDAO.refresh(serviceEntity);
serviceDesiredStateDAO.refresh(serviceDesiredStateEntity);
}
}
@Override
public synchronized boolean canBeRemoved() {
if (!getDesiredState().isRemovableState()) {
return false;
}
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;
}
@Override
@Transactional
public synchronized void deleteAllComponents() throws AmbariException {
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();
}
@Override
public synchronized void deleteServiceComponent(String componentName)
throws AmbariException {
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);
}
@Override
public boolean isClientOnlyService() {
return isClientOnlyService;
}
@Override
@Transactional
public synchronized void delete() throws AmbariException {
deleteAllComponents();
if (persisted) {
removeEntities();
persisted = false;
}
desiredConfigs.clear();
}
@Transactional
protected void removeEntities() throws AmbariException {
ClusterServiceEntityPK pk = new ClusterServiceEntityPK();
pk.setClusterId(getClusterId());
pk.setServiceName(getName());
clusterServiceDAO.removeByPK(pk);
}
}