http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionLinkResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionLinkResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionLinkResourceProvider.java new file mode 100644 index 0000000..67cc972 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionLinkResourceProvider.java @@ -0,0 +1,241 @@ +/** + * 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.controller.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.StaticallyInject; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.ExtensionLinkRequest; +import org.apache.ambari.server.controller.RequestStatusResponse; +import org.apache.ambari.server.controller.spi.*; +import org.apache.ambari.server.controller.spi.Resource.Type; +import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.orm.dao.ExtensionLinkDAO; +import org.apache.ambari.server.orm.entities.ExtensionLinkEntity; + +import com.google.inject.Inject; + +/** + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +@StaticallyInject +public class ExtensionLinkResourceProvider extends AbstractControllerResourceProvider { + + public static final String LINK_ID_PROPERTY_ID = PropertyHelper + .getPropertyId("ExtensionLink", "link_id"); + + public static final String STACK_NAME_PROPERTY_ID = PropertyHelper + .getPropertyId("ExtensionLink", "stack_name"); + + public static final String STACK_VERSION_PROPERTY_ID = PropertyHelper + .getPropertyId("ExtensionLink", "stack_version"); + + public static final String EXTENSION_NAME_PROPERTY_ID = PropertyHelper + .getPropertyId("ExtensionLink", "extension_name"); + + public static final String EXTENSION_VERSION_PROPERTY_ID = PropertyHelper + .getPropertyId("ExtensionLink", "extension_version"); + + private static Set<String> pkPropertyIds = new HashSet<String>( + Arrays.asList(new String[] { LINK_ID_PROPERTY_ID, STACK_NAME_PROPERTY_ID, STACK_VERSION_PROPERTY_ID, EXTENSION_NAME_PROPERTY_ID, EXTENSION_VERSION_PROPERTY_ID })); + + @Inject + private static ExtensionLinkDAO dao; + + protected ExtensionLinkResourceProvider(Set<String> propertyIds, + Map<Type, String> keyPropertyIds, + AmbariManagementController managementController) { + super(propertyIds, keyPropertyIds, managementController); + } + + @Override + public RequestStatus createResources(Request request) + throws SystemException, UnsupportedPropertyException, + NoSuchParentResourceException, ResourceAlreadyExistsException { + + final Set<ExtensionLinkRequest> requests = new HashSet<ExtensionLinkRequest>(); + for (Map<String, Object> propertyMap : request.getProperties()) { + requests.add(getRequest(propertyMap)); + } + + createResources(new Command<Void>() { + @Override + public Void invoke() throws AmbariException { + for (ExtensionLinkRequest extensionLinkRequest : requests) { + getManagementController().createExtensionLink(extensionLinkRequest); + } + return null; + } + }); + + if (requests.size() > 0) { + //Need to reread the stacks/extensions directories so the latest information is available + try { + getManagementController().updateStacks(); + } catch (AmbariException e) { + throw new SystemException(e.getMessage(), e); + } + + notifyCreate(Resource.Type.ExtensionLink, request); + } + + return getRequestStatus(null); + } + + protected RequestStatus deleteResourcesAuthorized(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + final Set<ExtensionLinkRequest> requests = new HashSet<ExtensionLinkRequest>(); + if (predicate == null) { + requests.add(getRequest(Collections.<String, Object>emptyMap())); + } else { + for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) { + requests.add(getRequest(propertyMap)); + } + } + + RequestStatusResponse response = modifyResources(new Command<RequestStatusResponse>() { + @Override + public RequestStatusResponse invoke() throws AmbariException { + for (ExtensionLinkRequest extensionLinkRequest : requests) { + getManagementController().deleteExtensionLink(extensionLinkRequest); + } + return null; + } + }); + + //Need to reread the stacks/extensions directories so the latest information is available + try { + getManagementController().updateStacks(); + } catch (AmbariException e) { + throw new SystemException(e.getMessage(), e); + } + + notifyDelete(Resource.Type.ExtensionLink, predicate); + + return getRequestStatus(response); + } + + @Override + public Set<Resource> getResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + final Set<Resource> resources = new HashSet<Resource>(); + final Set<String> requestedIds = getRequestPropertyIds(request, predicate); + + final Set<ExtensionLinkRequest> requests = new HashSet<ExtensionLinkRequest>(); + if (predicate == null) { + requests.add(getRequest(Collections.<String, Object>emptyMap())); + } else { + for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) { + requests.add(getRequest(propertyMap)); + } + } + + Set<ExtensionLinkEntity> entities = new HashSet<ExtensionLinkEntity>(); + + for (ExtensionLinkRequest extensionLinkRequest : requests) { + verifyStackAndExtensionExist(extensionLinkRequest); + entities.addAll(dao.find(extensionLinkRequest)); + } + + for (ExtensionLinkEntity entity : entities) { + Resource resource = new ResourceImpl(Resource.Type.ExtensionLink); + setResourceProperty(resource, LINK_ID_PROPERTY_ID, + entity.getLinkId(), requestedIds); + setResourceProperty(resource, STACK_NAME_PROPERTY_ID, + entity.getStack().getStackName(), requestedIds); + setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, + entity.getStack().getStackVersion(), requestedIds); + setResourceProperty(resource, EXTENSION_NAME_PROPERTY_ID, + entity.getExtension().getExtensionName(), requestedIds); + setResourceProperty(resource, EXTENSION_VERSION_PROPERTY_ID, + entity.getExtension().getExtensionVersion(), requestedIds); + + resources.add(resource); + } + return resources; + } + + @Override + public RequestStatus updateResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + //Need to reread the stacks/extensions directories so the latest information is available + try { + getManagementController().updateStacks(); + } catch (AmbariException e) { + throw new SystemException(e.getMessage(), e); + } + + notifyUpdate(Resource.Type.ExtensionLink, request, predicate); + return getRequestStatus(null); + } + + private void verifyStackAndExtensionExist(ExtensionLinkRequest request) throws NoSuchParentResourceException { + try { + if (request.getStackName() != null && request.getStackVersion() != null) { + getManagementController().getAmbariMetaInfo().getStack(request.getStackName(), request.getStackVersion()); + } + if (request.getExtensionName() != null && request.getExtensionVersion() != null) { + getManagementController().getAmbariMetaInfo().getExtension(request.getExtensionName(), request.getExtensionVersion()); + } + } + catch (AmbariException ambariException) { + throw new NoSuchParentResourceException(ambariException.getMessage()); + } + } + + private ExtensionLinkRequest getRequest(Map<String, Object> properties) { + return new ExtensionLinkRequest( + (String) properties.get(LINK_ID_PROPERTY_ID), + (String) properties.get(STACK_NAME_PROPERTY_ID), + (String) properties.get(STACK_VERSION_PROPERTY_ID), + (String) properties.get(EXTENSION_NAME_PROPERTY_ID), + (String) properties.get(EXTENSION_VERSION_PROPERTY_ID)); + } + + private ExtensionLinkRequest createExtensionLinkRequest(ExtensionLinkEntity entity) { + if (entity == null) { + return null; + } + + return new ExtensionLinkRequest(String.valueOf(entity.getLinkId()), + entity.getStack().getStackName(), + entity.getStack().getStackVersion(), + entity.getExtension().getExtensionName(), + entity.getExtension().getExtensionVersion()); + } + + @Override + protected Set<String> getPKPropertyIds() { + return pkPropertyIds; + } +}
http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionResourceProvider.java new file mode 100644 index 0000000..8baf202 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionResourceProvider.java @@ -0,0 +1,121 @@ +/** + * 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.controller.internal; + +import java.util.*; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.RequestStatusResponse; +import org.apache.ambari.server.controller.ExtensionRequest; +import org.apache.ambari.server.controller.ExtensionResponse; +import org.apache.ambari.server.controller.spi.*; +import org.apache.ambari.server.controller.spi.Resource.Type; +import org.apache.ambari.server.controller.utilities.PropertyHelper; + +/** + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +public class ExtensionResourceProvider extends ReadOnlyResourceProvider { + + public static final String EXTENSION_NAME_PROPERTY_ID = PropertyHelper + .getPropertyId("Extensions", "extension_name"); + + private static Set<String> pkPropertyIds = new HashSet<String>( + Arrays.asList(new String[] { EXTENSION_NAME_PROPERTY_ID })); + + protected ExtensionResourceProvider(Set<String> propertyIds, + Map<Type, String> keyPropertyIds, + AmbariManagementController managementController) { + super(propertyIds, keyPropertyIds, managementController); + } + + + @Override + public Set<Resource> getResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + final Set<ExtensionRequest> requests = new HashSet<ExtensionRequest>(); + + if (predicate == null) { + requests.add(getRequest(Collections.<String, Object>emptyMap())); + } else { + for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) { + requests.add(getRequest(propertyMap)); + } + } + + Set<String> requestedIds = getRequestPropertyIds(request, predicate); + + Set<ExtensionResponse> responses = getResources(new Command<Set<ExtensionResponse>>() { + @Override + public Set<ExtensionResponse> invoke() throws AmbariException { + return getManagementController().getExtensions(requests); + } + }); + + Set<Resource> resources = new HashSet<Resource>(); + + for (ExtensionResponse response : responses) { + Resource resource = new ResourceImpl(Resource.Type.Extension); + + setResourceProperty(resource, EXTENSION_NAME_PROPERTY_ID, + response.getExtensionName(), requestedIds); + + resource.setProperty(EXTENSION_NAME_PROPERTY_ID, response.getExtensionName()); + + resources.add(resource); + } + + return resources; + } + + @Override + public RequestStatus updateResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + RequestStatusResponse response = modifyResources( + new Command<RequestStatusResponse>() { + + @Override + public RequestStatusResponse invoke() throws AmbariException { + //return getManagementController().updateExtensions(); + //TODO - do we need a separate method + return getManagementController().updateStacks(); + } + }); + + notifyUpdate(Type.Extension, request, predicate); + + return getRequestStatus(response); + } + + private ExtensionRequest getRequest(Map<String, Object> properties) { + return new ExtensionRequest((String) properties.get(EXTENSION_NAME_PROPERTY_ID)); + } + + @Override + protected Set<String> getPKPropertyIds() { + return pkPropertyIds; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionVersionResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionVersionResourceProvider.java new file mode 100644 index 0000000..57ebabd --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExtensionVersionResourceProvider.java @@ -0,0 +1,131 @@ +/** + * 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.controller.internal; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.inject.Inject; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.StaticallyInject; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.ExtensionVersionRequest; +import org.apache.ambari.server.controller.ExtensionVersionResponse; +import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; +import org.apache.ambari.server.controller.spi.NoSuchResourceException; +import org.apache.ambari.server.controller.spi.Predicate; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.spi.Resource.Type; +import org.apache.ambari.server.controller.spi.SystemException; +import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; +import org.apache.ambari.server.controller.utilities.PropertyHelper; + +/** + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +@StaticallyInject +public class ExtensionVersionResourceProvider extends ReadOnlyResourceProvider { + + public static final String EXTENSION_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("Versions", "extension_version"); + public static final String EXTENSION_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Versions", "extension_name"); + public static final String EXTENSION_VALID_PROPERTY_ID = PropertyHelper.getPropertyId("Versions", "valid"); + public static final String EXTENSION_ERROR_SET = PropertyHelper.getPropertyId("Versions", "extension-errors"); + public static final String EXTENSION_PARENT_PROPERTY_ID = PropertyHelper.getPropertyId("Versions", "parent_extension_version"); + + private static Set<String> pkPropertyIds = new HashSet<String>( + Arrays.asList(new String[] { EXTENSION_NAME_PROPERTY_ID, EXTENSION_VERSION_PROPERTY_ID })); + + protected ExtensionVersionResourceProvider(Set<String> propertyIds, + Map<Type, String> keyPropertyIds, + AmbariManagementController managementController) { + super(propertyIds, keyPropertyIds, managementController); + } + + @Override + public Set<Resource> getResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + final Set<ExtensionVersionRequest> requests = new HashSet<ExtensionVersionRequest>(); + + if (predicate == null) { + requests.add(getRequest(Collections.<String, Object>emptyMap())); + } else { + for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) { + requests.add(getRequest(propertyMap)); + } + } + + Set<String> requestedIds = getRequestPropertyIds(request, predicate); + + Set<ExtensionVersionResponse> responses = getResources(new Command<Set<ExtensionVersionResponse>>() { + @Override + public Set<ExtensionVersionResponse> invoke() throws AmbariException { + return getManagementController().getExtensionVersions(requests); + } + }); + + Set<Resource> resources = new HashSet<Resource>(); + + for (ExtensionVersionResponse response : responses) { + Resource resource = new ResourceImpl(Resource.Type.ExtensionVersion); + + setResourceProperty(resource, EXTENSION_NAME_PROPERTY_ID, + response.getExtensionName(), requestedIds); + + setResourceProperty(resource, EXTENSION_VERSION_PROPERTY_ID, + response.getExtensionVersion(), requestedIds); + + setResourceProperty(resource, EXTENSION_VALID_PROPERTY_ID, + response.isValid(), requestedIds); + + setResourceProperty(resource, EXTENSION_ERROR_SET, + response.getErrors(), requestedIds); + + setResourceProperty(resource, EXTENSION_PARENT_PROPERTY_ID, + response.getParentVersion(), requestedIds); + + resources.add(resource); + } + + return resources; + } + + private ExtensionVersionRequest getRequest(Map<String, Object> properties) { + return new ExtensionVersionRequest( + (String) properties.get(EXTENSION_NAME_PROPERTY_ID), + (String) properties.get(EXTENSION_VERSION_PROPERTY_ID)); + } + + @Override + protected Set<String> getPKPropertyIds() { + return pkPropertyIds; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java index 99e4ccd..6853266 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java @@ -92,6 +92,9 @@ public interface Resource { Member, Stack, StackVersion, + ExtensionLink, + Extension, + ExtensionVersion, OperatingSystem, Repository, StackService, @@ -210,6 +213,9 @@ public interface Resource { public static final Type Member = InternalType.Member.getType(); public static final Type Stack = InternalType.Stack.getType(); public static final Type StackVersion = InternalType.StackVersion.getType(); + public static final Type ExtensionLink = InternalType.ExtensionLink.getType(); + public static final Type Extension = InternalType.Extension.getType(); + public static final Type ExtensionVersion = InternalType.ExtensionVersion.getType(); public static final Type OperatingSystem = InternalType.OperatingSystem.getType(); public static final Type Repository = InternalType.Repository.getType(); public static final Type StackService = InternalType.StackService.getType(); http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java new file mode 100644 index 0000000..6c6c3ae --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java @@ -0,0 +1,168 @@ +/** + * 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.orm.dao; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.orm.RequiresSession; +import org.apache.ambari.server.orm.entities.ExtensionEntity; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.persist.Transactional; + +/** + * The {@link ExtensionDAO} class is used to manage the persistence and retrieval of + * {@link ExtensionEntity} instances. + * + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +@Singleton +public class ExtensionDAO { + + /** + * JPA entity manager + */ + @Inject + private Provider<EntityManager> entityManagerProvider; + + /** + * DAO utilities for dealing mostly with {@link TypedQuery} results. + */ + @Inject + private DaoUtils daoUtils; + + /** + * Gets a extension with the specified ID. + * + * @param extensionId + * the ID of the extension to retrieve. + * @return the extension or {@code null} if none exists. + */ + @RequiresSession + public ExtensionEntity findById(long extensionId) { + return entityManagerProvider.get().find(ExtensionEntity.class, extensionId); + } + + /** + * Gets all of the defined extensions. + * + * @return all of the extensions loaded from resources or an empty list (never + * {@code null}). + */ + @RequiresSession + public List<ExtensionEntity> findAll() { + TypedQuery<ExtensionEntity> query = entityManagerProvider.get().createNamedQuery( + "ExtensionEntity.findAll", ExtensionEntity.class); + + return daoUtils.selectList(query); + } + + /** + * Gets the extension that matches the specified name and version. + * + * @return the extension matching the specified name and version or {@code null} + * if none. + */ + @RequiresSession + public ExtensionEntity find(String extensionName, String extensionVersion) { + TypedQuery<ExtensionEntity> query = entityManagerProvider.get().createNamedQuery( + "ExtensionEntity.findByNameAndVersion", ExtensionEntity.class); + + query.setParameter("extensionName", extensionName); + query.setParameter("extensionVersion", extensionVersion); + + return daoUtils.selectOne(query); + } + + /** + * Persists a new extension instance. + * + * @param extension + * the extension to persist (not {@code null}). + */ + @Transactional + public void create(ExtensionEntity extension) + throws AmbariException { + EntityManager entityManager = entityManagerProvider.get(); + entityManager.persist(extension); + } + + /** + * Refresh the state of the extension instance from the database. + * + * @param extension + * the extension to refresh (not {@code null}). + */ + @Transactional + public void refresh(ExtensionEntity extension) { + entityManagerProvider.get().refresh(extension); + } + + /** + * Merge the specified extension with the existing extension in the database. + * + * @param extension + * the extension to merge (not {@code null}). + * @return the updated extension with merged content (never {@code null}). + */ + @Transactional + public ExtensionEntity merge(ExtensionEntity extension) { + return entityManagerProvider.get().merge(extension); + } + + /** + * Creates or updates the specified entity. This method will check + * {@link ExtensionEntity#getStackId()} in order to determine whether the entity + * should be created or merged. + * + * @param extension + * the extension to create or update (not {@code null}). + */ + public void createOrUpdate(ExtensionEntity extension) + throws AmbariException { + if (null == extension.getExtensionId()) { + create(extension); + } else { + merge(extension); + } + } + + /** + * Removes the specified extension and all related clusters, services and + * components. + * + * @param extension + * the extension to remove. + */ + @Transactional + public void remove(ExtensionEntity extension) { + EntityManager entityManager = entityManagerProvider.get(); + extension = findById(extension.getExtensionId()); + if (null != extension) { + entityManager.remove(extension); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java new file mode 100644 index 0000000..d90480b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java @@ -0,0 +1,240 @@ +/** + * 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.orm.dao; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.ExtensionLinkRequest; +import org.apache.ambari.server.orm.RequiresSession; +import org.apache.ambari.server.orm.entities.ExtensionLinkEntity; +import org.apache.ambari.server.orm.entities.ExtensionLinkEntity; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.persist.Transactional; + +/** + * The {@link ExtensionLinkDAO} class is used to manage the persistence and retrieval of + * {@link ExtensionLinkEntity} instances. + * + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +@Singleton +public class ExtensionLinkDAO { + + /** + * JPA entity manager + */ + @Inject + private Provider<EntityManager> entityManagerProvider; + + /** + * DAO utilities for dealing mostly with {@link TypedQuery} results. + */ + @Inject + private DaoUtils daoUtils; + + + /** + * Gets the extension links that match the specified stack name and version. + * + * @return the extension links matching the specified stack name and version if any. + */ + @RequiresSession + public List<ExtensionLinkEntity> find(ExtensionLinkRequest request) { + if (request.getLinkId() != null) { + ExtensionLinkEntity entity = findById(new Long(request.getLinkId())); + List<ExtensionLinkEntity> list = new ArrayList<ExtensionLinkEntity>(); + list.add(entity); + return list; + } + + String stackName = request.getStackName(); + String stackVersion = request.getStackName(); + String extensionName = request.getStackName(); + String extensionVersion = request.getStackName(); + + if (stackName != null && stackVersion != null) { + if (extensionName != null && extensionVersion != null) { + ExtensionLinkEntity entity = findByStackAndExtension(stackName, stackVersion, extensionName, extensionVersion); + List<ExtensionLinkEntity> list = new ArrayList<ExtensionLinkEntity>(); + list.add(entity); + return list; + } + return findByStack(stackName, stackVersion); + } + if (extensionName != null && extensionVersion != null) { + return findByExtension(extensionName, extensionVersion); + } + + return findAll(); + } + + /** + * Gets an extension link with the specified ID. + * + * @param linkId + * the ID of the extension link to retrieve. + * @return the extension or {@code null} if none exists. + */ + @RequiresSession + public ExtensionLinkEntity findById(long linkId) { + return entityManagerProvider.get().find(ExtensionLinkEntity.class, linkId); + } + + /** + * Gets all of the defined extension links. + * + * @return all of the extension links loaded from resources or an empty list (never + * {@code null}). + */ + @RequiresSession + public List<ExtensionLinkEntity> findAll() { + TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery( + "ExtensionLinkEntity.findAll", ExtensionLinkEntity.class); + + return daoUtils.selectList(query); + } + + /** + * Gets the extension links that match the specified extension name and version. + * + * @return the extension links matching the specified extension name and version if any. + */ + @RequiresSession + public List<ExtensionLinkEntity> findByExtension(String extensionName, String extensionVersion) { + TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery( + "ExtensionLinkEntity.findByExtension", ExtensionLinkEntity.class); + + query.setParameter("extensionName", extensionName); + query.setParameter("extensionVersion", extensionVersion); + + return daoUtils.selectList(query); + } + + /** + * Gets the extension links that match the specified stack name and version. + * + * @return the extension links matching the specified stack name and version if any. + */ + @RequiresSession + public List<ExtensionLinkEntity> findByStack(String stackName, String stackVersion) { + TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery( + "ExtensionLinkEntity.findByStack", ExtensionLinkEntity.class); + + query.setParameter("stackName", stackName); + query.setParameter("stackVersion", stackVersion); + + return daoUtils.selectList(query); + } + + /** + * Gets the extension link that match the specified stack name, stack version, extension name and extension version. + * + * @return the extension link matching the specified stack name, stack version, extension name and extension version if any. + */ + @RequiresSession + public ExtensionLinkEntity findByStackAndExtension(String stackName, String stackVersion, String extensionName, String extensionVersion) { + TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery( + "ExtensionLinkEntity.findByStackAndExtension", ExtensionLinkEntity.class); + + query.setParameter("stackName", stackName); + query.setParameter("stackVersion", stackVersion); + query.setParameter("extensionName", extensionName); + query.setParameter("extensionVersion", extensionVersion); + + return daoUtils.selectOne(query); + } + + /** + * Persists a new extension link instance. + * + * @param link + * the extension link to persist (not {@code null}). + */ + @Transactional + public void create(ExtensionLinkEntity link) + throws AmbariException { + EntityManager entityManager = entityManagerProvider.get(); + entityManager.persist(link); + } + + /** + * Refresh the state of the extension instance from the database. + * + * @param link + * the extension link to refresh (not {@code null}). + */ + @Transactional + public void refresh(ExtensionLinkEntity link) { + entityManagerProvider.get().refresh(link); + } + + /** + * Merge the specified extension link with the existing extension link in the database. + * + * @param link + * the extension link to merge (not {@code null}). + * @return the updated extension link with merged content (never {@code null}). + */ + @Transactional + public ExtensionLinkEntity merge(ExtensionLinkEntity link) { + return entityManagerProvider.get().merge(link); + } + + /** + * Creates or updates the specified entity. This method will check + * {@link ExtensionLinkEntity#getLinkId()} in order to determine whether the entity + * should be created or merged. + * + * @param extension + * the link to create or update (not {@code null}). + */ + public void createOrUpdate(ExtensionLinkEntity link) + throws AmbariException { + if (null == link.getLinkId()) { + create(link); + } else { + merge(link); + } + } + + /** + * Removes the specified extension link + * + * @param link + * the extension link to remove. + */ + @Transactional + public void remove(ExtensionLinkEntity link) { + EntityManager entityManager = entityManagerProvider.get(); + link = findById(link.getLinkId()); + if (null != link) { + entityManager.remove(link); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java new file mode 100644 index 0000000..870d941 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java @@ -0,0 +1,156 @@ +/** + * 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.orm.entities; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.UniqueConstraint; + +/** + * The {@link ExtensionEntity} class is used to model an extension to the stack. + * + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +@Entity +@Table(name = "extension", uniqueConstraints = @UniqueConstraint(columnNames = { + "extension_name", "extension_version" })) +@TableGenerator(name = "extension_id_generator", table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value", pkColumnValue = "extension_id_seq", initialValue = 0) +@NamedQueries({ + @NamedQuery(name = "ExtensionEntity.findAll", query = "SELECT extension FROM ExtensionEntity extension"), + @NamedQuery(name = "ExtensionEntity.findByNameAndVersion", query = "SELECT extension FROM ExtensionEntity extension WHERE extension.extensionName = :extensionName AND extension.extensionVersion = :extensionVersion") }) +public class ExtensionEntity { + + @Id + @GeneratedValue(strategy = GenerationType.TABLE, generator = "extension_id_generator") + @Column(name = "extension_id", nullable = false, updatable = false) + private Long extensionId; + + @Column(name = "extension_name", length = 255, nullable = false) + private String extensionName; + + @Column(name = "extension_version", length = 255, nullable = false) + private String extensionVersion; + + /** + * Constructor. + */ + public ExtensionEntity() { + } + + /** + * Gets the unique identifier for this extension. + * + * @return the ID. + */ + public Long getExtensionId() { + return extensionId; + } + + /** + * Gets the name of the extension. + * + * @return the name of the extension (never {@code null}). + */ + public String getExtensionName() { + return extensionName; + } + + /** + * Sets the name of the extension. + * + * @param extensionName + * the extension name (not {@code null}). + */ + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + /** + * Gets the version of the extension. + * + * @return the extension version (never {@code null}). + */ + public String getExtensionVersion() { + return extensionVersion; + } + + /** + * Sets the version of the extension. + * + * @param extensionVersion + * the extension version (not {@code null}). + */ + public void setExtensionVersion(String extensionVersion) { + this.extensionVersion = extensionVersion; + } + + /** + * + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + ExtensionEntity that = (ExtensionEntity) object; + + if (extensionId != null ? !extensionId.equals(that.extensionId) : that.extensionId != null) { + return false; + } + + return true; + } + + /** + * + */ + @Override + public int hashCode() { + int result = null != extensionId ? extensionId.hashCode() : 0; + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(getClass().getSimpleName()); + buffer.append("{"); + buffer.append("id=").append(extensionId); + buffer.append(", name=").append(extensionName); + buffer.append(", version=").append(extensionVersion); + buffer.append("}"); + return buffer.toString(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java new file mode 100644 index 0000000..12b3ce0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java @@ -0,0 +1,139 @@ +/** + * 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.orm.entities; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.UniqueConstraint; + +/** + * The {@link ExtensionLinkEntity} class is used to model the extensions linked to the stack. + * + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +@Table(name = "extensionlink", uniqueConstraints = @UniqueConstraint(columnNames = { + "stack_id", "extension_id" })) +@TableGenerator(name = "link_id_generator", table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value", pkColumnValue = "link_id_seq", initialValue = 0) +@NamedQueries({ + @NamedQuery(name = "ExtensionLinkEntity.findAll", query = "SELECT link FROM ExtensionLinkEntity link"), + @NamedQuery(name = "ExtensionLinkEntity.findByStackAndExtension", query = "SELECT link FROM ExtensionLinkEntity link WHERE link.stack.stackName = :stackName AND link.stack.stackVersion = :stackVersion AND link.extension.extensionName = :extensionName AND link.extension.extensionVersion = :extensionVersion"), + @NamedQuery(name = "ExtensionLinkEntity.findByStack", query = "SELECT link FROM ExtensionLinkEntity link WHERE link.stack.stackName = :stackName AND link.stack.stackVersion = :stackVersion"), + @NamedQuery(name = "ExtensionLinkEntity.findByExtension", query = "SELECT link FROM ExtensionLinkEntity link WHERE link.extension.extensionName = :extensionName AND link.extension.extensionVersion = :extensionVersion") }) +@Entity +public class ExtensionLinkEntity { + + @Id + @GeneratedValue(strategy = GenerationType.TABLE, generator = "link_id_generator") + @Column(name = "link_id", nullable = false, updatable = false) + private Long linkId; + + @OneToOne + @JoinColumn(name = "stack_id", unique = false, nullable = false, insertable = true, updatable = false) + private StackEntity stack; + + @OneToOne + @JoinColumn(name = "extension_id", unique = false, nullable = false, insertable = true, updatable = false) + private ExtensionEntity extension; + + /** + * Constructor. + */ + public ExtensionLinkEntity() { + } + + public Long getLinkId() { + return linkId; + } + + public void setLinkId(Long linkId) { + this.linkId = linkId; + } + + public StackEntity getStack() { + return stack; + } + + public void setStack(StackEntity stack) { + this.stack = stack; + } + + public ExtensionEntity getExtension() { + return extension; + } + + public void setExtension(ExtensionEntity extension) { + this.extension = extension; + } + + /** + * + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + ExtensionLinkEntity that = (ExtensionLinkEntity) object; + + if (linkId != null ? !linkId.equals(that.linkId) : that.linkId != null) { + return false; + } + + return true; + } + + /** + * + */ + @Override + public int hashCode() { + int result = (null != linkId) ? linkId.hashCode() : 0; + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(getClass().getSimpleName()); + buffer.append("{"); + buffer.append("linkId=").append(linkId); + buffer.append(", stackId=").append(stack.getStackId()); + buffer.append(", extensionId=").append(extension.getExtensionId()); + buffer.append("}"); + return buffer.toString(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/BaseModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/BaseModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/BaseModule.java index ef2438f..0e1116f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/stack/BaseModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/BaseModule.java @@ -60,7 +60,7 @@ public abstract class BaseModule<T, I> implements StackDefinitionModule<T, I> { * @return collection of the merged modules */ protected <T extends StackDefinitionModule<T, ?>> Collection<T> mergeChildModules( - Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, T> modules, Map<String, T> parentModules) + Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions, Map<String, T> modules, Map<String, T> parentModules) throws AmbariException { Set<String> addedModules = new HashSet<String>(); Collection<T> mergedModules = new HashSet<T>(); @@ -70,7 +70,7 @@ public abstract class BaseModule<T, I> implements StackDefinitionModule<T, I> { addedModules.add(id); if (!module.isDeleted()) { if (parentModules.containsKey(id)) { - module.resolve(parentModules.get(id), allStacks, commonServices); + module.resolve(parentModules.get(id), allStacks, commonServices, extensions); } mergedModules.add(module); } http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/CommonServiceDirectory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/CommonServiceDirectory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/CommonServiceDirectory.java index cbbdb91..cdedbb4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/stack/CommonServiceDirectory.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/CommonServiceDirectory.java @@ -62,15 +62,13 @@ public class CommonServiceDirectory extends ServiceDirectory { @Override /** - * Parse common service directory + * Calculate the common service directories * packageDir Format: common-services/<serviceName>/<serviceVersion>/package * Example: * directory: "/var/lib/ambari-server/resources/common-services/HDFS/1.0" * packageDir: "common-services/HDFS/1.0/package" - * - * @throws AmbariException */ - protected void parsePath() throws AmbariException { + protected void calculateDirectories() { File serviceVersionDir = new File(getAbsolutePath()); File serviceDir = serviceVersionDir.getParentFile(); @@ -95,6 +93,5 @@ public class CommonServiceDirectory extends ServiceDirectory { LOG.debug(String.format("Service upgrades folder %s for common service %s does not exist.", absUpgradesDir, serviceId )); } - parseMetaInfoFile(); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java index 65da145..d9d3105 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ComponentModule.java @@ -58,7 +58,8 @@ public class ComponentModule extends BaseModule<ComponentModule, ComponentInfo> } @Override - public void resolve(ComponentModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) { + public void resolve(ComponentModule parent, Map<String, StackModule> allStacks, + Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) { if (parent != null) { ComponentInfo parentInfo = parent.getModuleInfo(); if (!parent.isValid()) { http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ConfigurationModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ConfigurationModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ConfigurationModule.java index 9c4e9d1..543c1ec 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ConfigurationModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ConfigurationModule.java @@ -82,7 +82,8 @@ public class ConfigurationModule extends BaseModule<ConfigurationModule, Configu } @Override - public void resolve(ConfigurationModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException { + public void resolve(ConfigurationModule parent, Map<String, StackModule> allStacks, + Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions) throws AmbariException { // merge properties also removes deleted props so should be called even if extension is disabled if (parent != null) { if (parent.info != null) { http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionDirectory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionDirectory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionDirectory.java new file mode 100644 index 0000000..f2647fd --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionDirectory.java @@ -0,0 +1,196 @@ +/** + * 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.stack; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.state.stack.ExtensionMetainfoXml; +import org.apache.ambari.server.state.stack.RepositoryXml; +import org.apache.ambari.server.state.stack.StackRoleCommandOrder; +import org.apache.ambari.server.state.stack.UpgradePack; +import org.apache.commons.io.FilenameUtils; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.xml.bind.JAXBException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +/** + * Encapsulates IO operations on a extension definition extension directory. + * + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +//todo: Normalize all path return values. +//todo: Currently some are relative and some are absolute. +//todo: Current values were dictated by the ExtensionInfo expectations. +public class ExtensionDirectory extends StackDefinitionDirectory { + + /** + * collection of service directories + */ + private Collection<ServiceDirectory> serviceDirectories; + + /** + * metainfo file representation + */ + private ExtensionMetainfoXml metaInfoXml; + + /** + * file unmarshaller + */ + ModuleFileUnmarshaller unmarshaller = new ModuleFileUnmarshaller(); + + /** + * extensions directory name + */ + public final static String EXTENSIONS_FOLDER_NAME = "extensions"; + + /** + * metainfo file name + */ + private static final String EXTENSION_METAINFO_FILE_NAME = "metainfo.xml"; + + /** + * logger instance + */ + private final static Logger LOG = LoggerFactory.getLogger(ExtensionDirectory.class); + + + /** + * Constructor. + * + * @param directory extension directory + * @throws AmbariException if unable to parse the stack directory + */ + public ExtensionDirectory(String directory) throws AmbariException { + super(directory); + parsePath(); + } + + /** + * Obtain the extension directory name. + * + * @return extension directory name + */ + public String getExtensionDirName() { + return getDirectory().getParentFile().getName(); + } + + /** + * Obtain the object representation of the extension metainfo.xml file. + * + * @return object representation of the extension metainfo.xml file + */ + public ExtensionMetainfoXml getMetaInfoFile() { + return metaInfoXml; + } + + /** + * Obtain a collection of all service directories. + * + * @return collection of all service directories + */ + public Collection<ServiceDirectory> getServiceDirectories() { + return serviceDirectories; + } + + /** + * Parse the extension directory. + * + * @throws AmbariException if unable to parse the directory + */ + private void parsePath() throws AmbariException { + Collection<String> subDirs = Arrays.asList(directory.list()); + parseServiceDirectories(subDirs); + parseMetaInfoFile(); + } + + /** + * Parse the extension metainfo file. + * + * @throws AmbariException if unable to parse the extension metainfo file + */ + private void parseMetaInfoFile() throws AmbariException { + File extensionMetaInfoFile = new File(getAbsolutePath() + + File.separator + EXTENSION_METAINFO_FILE_NAME); + + //todo: is it ok for this file not to exist? + if (extensionMetaInfoFile.exists()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Reading extension version metainfo from file " + extensionMetaInfoFile.getAbsolutePath()); + } + + try { + metaInfoXml = unmarshaller.unmarshal(ExtensionMetainfoXml.class, extensionMetaInfoFile); + } catch (JAXBException e) { + metaInfoXml = new ExtensionMetainfoXml(); + metaInfoXml.setValid(false); + metaInfoXml.addError("Unable to parse extension metainfo.xml file at location: " + + extensionMetaInfoFile.getAbsolutePath()); + } + } + } + + /** + * Parse the extension's service directories extension + * @param subDirs extension sub directories + * @throws AmbariException if unable to parse the service directories + */ + private void parseServiceDirectories(Collection<String> subDirs) throws AmbariException { + Collection<ServiceDirectory> dirs = new HashSet<ServiceDirectory>(); + + if (subDirs.contains(ServiceDirectory.SERVICES_FOLDER_NAME)) { + String servicesDir = getAbsolutePath() + File.separator + ServiceDirectory.SERVICES_FOLDER_NAME; + File baseServiceDir = new File(servicesDir); + File[] serviceFolders = baseServiceDir.listFiles(AmbariMetaInfo.FILENAME_FILTER); + if (serviceFolders != null) { + for (File d : serviceFolders) { + if (d.isDirectory()) { + try { + dirs.add(new StackServiceDirectory(d.getAbsolutePath())); + } catch (AmbariException e) { + //todo: this seems as though we should propagate this exception + //todo: eating it now to keep backwards compatibility + LOG.warn(String.format("Unable to parse extension definition service at '%s'. Ignoring service. : %s", + d.getAbsolutePath(), e.toString())); + } + } + } + } + } + + if (dirs.isEmpty()) { + //todo: what does it mean for a extension to have no services? + LOG.info("The extension defined at '" + getAbsolutePath() + "' contains no services"); + } + serviceDirectories = dirs; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/300a7e21/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionHelper.java new file mode 100644 index 0000000..cd4d9f3 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ExtensionHelper.java @@ -0,0 +1,167 @@ +/** + * 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.stack; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.ServiceNotFoundException; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.ExtensionInfo; +import org.apache.ambari.server.state.ServiceInfo; +import org.apache.ambari.server.state.StackInfo; +import org.apache.ambari.server.state.stack.ExtensionMetainfoXml; +import org.apache.ambari.server.utils.VersionUtils; + +/** + * An extension version is like a stack version but it contains custom services. Linking an extension + * version to the current stack version allows the cluster to install the custom services contained in + * the extension version. + */ +public class ExtensionHelper { + + public static void validateDeleteLink(Clusters clusters, StackInfo stack, ExtensionInfo extension) throws AmbariException { + validateNotRequiredExtension(stack, extension); + validateServicesNotInstalled(clusters, stack, extension); + } + + private static void validateServicesNotInstalled(Clusters clusters, StackInfo stack, ExtensionInfo extension) throws AmbariException { + for (Cluster cluster : clusters.getClusters().values()) { + for (ServiceInfo service : extension.getServices()) { + try { + if (service != null && cluster.getService(service.getName()) != null) { + String message = "Extension service is still installed" + + ", stackName=" + stack.getName() + + ", stackVersion=" + stack.getVersion() + + ", service=" + service.getName() + + ", extensionName=" + extension.getName() + + ", extensionVersion=" + extension.getVersion(); + + throw new AmbariException(message); + } + } + catch (ServiceNotFoundException e) { + //Eat the exception + } + } + } + } + + public static void validateCreateLink(StackInfo stack, ExtensionInfo extension) throws AmbariException { + validateSupportedStackVersion(stack, extension); + validateServiceDuplication(stack, extension); + validateRequiredExtensions(stack, extension); + } + + private static void validateSupportedStackVersion(StackInfo stack, ExtensionInfo extension) throws AmbariException { + for (ExtensionMetainfoXml.Stack validStack : extension.getStacks()) { + if (validStack.getName().equals(stack.getName())) { + String minStackVersion = validStack.getVersion(); + if (VersionUtils.compareVersions(stack.getVersion(), minStackVersion) >= 0) { + //Found a supported stack version + return; + } + } + } + + String message = "Stack is not supported by extension" + + ", stackName=" + stack.getName() + + ", stackVersion=" + stack.getVersion() + + ", extensionName=" + extension.getName() + + ", extensionVersion=" + extension.getVersion(); + + throw new AmbariException(message); + } + + private static void validateServiceDuplication(StackInfo stack, ExtensionInfo extension) throws AmbariException { + for (ServiceInfo service : extension.getServices()) { + if (service != null) { + ServiceInfo stackService = null; + try { + stackService = stack.getService(service.getName()); + } + catch (Exception e) { + //Eat the exception + } + if (stackService != null) { + String message = "Existing service is included in extension" + + ", stackName=" + stack.getName() + + ", stackVersion=" + stack.getVersion() + + ", service=" + service.getName() + + ", extensionName=" + extension.getName() + + ", extensionVersion=" + extension.getVersion(); + + throw new AmbariException(message); + } + } + } + } + + private static void validateRequiredExtensions(StackInfo stack, ExtensionInfo extension) throws AmbariException { + for (ExtensionMetainfoXml.Extension requiredExtension : extension.getExtensions()) { + if (requiredExtension != null) { + String message = "Stack has not linked required extension" + + ", stackName=" + stack.getName() + + ", stackVersion=" + stack.getVersion() + + ", extensionName=" + extension.getName() + + ", extensionVersion=" + extension.getVersion() + + ", requiredExtensionName=" + requiredExtension.getName() + + ", requiredExtensionVersion=" + requiredExtension.getVersion(); + try { + ExtensionInfo stackExtension = stack.getExtension(requiredExtension.getName()); + if (stackExtension != null) { + String version = requiredExtension.getVersion(); + if (version.endsWith("*")) { + version = version.substring(0, version.length() - 1); + if (!stackExtension.getVersion().startsWith(version)) { + throw new AmbariException(message); + } + } + else if (!stackExtension.getVersion().equals(version)) { + throw new AmbariException(message); + } + } + } + catch (Exception e) { + throw new AmbariException(message, e); + } + } + } + } + + private static void validateNotRequiredExtension(StackInfo stack, ExtensionInfo extension) throws AmbariException { + for (ExtensionInfo stackExtension : stack.getExtensions()) { + if (stackExtension != null) { + for (ExtensionMetainfoXml.Extension requiredExtension : stackExtension.getExtensions()) { + if (requiredExtension != null && requiredExtension.getName().equals(extension.getName())) { + String message = "Stack extension is required by extension" + + ", stackName=" + stack.getName() + + ", stackVersion=" + stack.getVersion() + + ", extensionName=" + extension.getName() + + ", extensionVersion=" + extension.getVersion() + + ", dependentExtensionName=" + stackExtension.getName() + + ", dependentExtensionVersion=" + stackExtension.getVersion(); + + throw new AmbariException(message); + } + } + } + } + } + +}
