/* * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl; import static java.util.Objects.requireNonNull; import com.google.common.collect.ImmutableSet; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import javax.ws.rs.Path; import javax.ws.rs.core.UriInfo; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.mdsal.dom.api.DOMSchemaService; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload; import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfOperationsService; import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; import org.opendaylight.yangtools.yang.common.Empty; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of {@link RestconfOperationsService}. */ @Path("/") public class RestconfOperationsServiceImpl implements RestconfOperationsService { private static final Logger LOG = LoggerFactory.getLogger(RestconfOperationsServiceImpl.class); private final SchemaContextHandler schemaContextHandler; private final DOMMountPointService mountPointService; /** * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}. * * @param schemaContextHandler handling schema context * @param mountPointService handling dom mount point service */ public RestconfOperationsServiceImpl(final SchemaContextHandler schemaContextHandler, final DOMMountPointService mountPointService) { this.schemaContextHandler = requireNonNull(schemaContextHandler); this.mountPointService = requireNonNull(mountPointService); } @Override public String getOperationsJSON() { return OperationsContent.JSON.bodyFor(schemaContextHandler.get()); } @Override public String getOperationsXML() { return OperationsContent.XML.bodyFor(schemaContextHandler.get()); } @Override public NormalizedNodePayload getOperations(final String identifier, final UriInfo uriInfo) { if (!identifier.contains(RestconfConstants.MOUNT)) { final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to " + " end with " + RestconfConstants.MOUNT; LOG.debug("{} for {}", errMsg, identifier); throw new RestconfDocumentedException(errMsg + RestconfConstants.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } final InstanceIdentifierContext mountPointIdentifier = ParserIdentifier.toInstanceIdentifier(identifier, schemaContextHandler.get(), Optional.of(mountPointService)); final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint(); final var entry = contextForModelContext(modelContext(mountPoint), mountPoint); return NormalizedNodePayload.of(entry.getKey(), entry.getValue()); } private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { return mountPoint.getService(DOMSchemaService.class) .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) .orElse(null); } // FIXME: remove this method and everything it uses @Deprecated(forRemoval = true, since = "4.0.0") private static @NonNull Entry contextForModelContext( final @NonNull EffectiveModelContext context, final @Nullable DOMMountPoint mountPoint) { // Determine which modules we need and construct leaf schemas to correspond to all RPC definitions final Collection modules = new ArrayList<>(); final ArrayList rpcLeafSchemas = new ArrayList<>(); for (final Module m : context.getModules()) { final Collection rpcs = m.getRpcs(); if (!rpcs.isEmpty()) { modules.add(new OperationsImportedModule(m)); rpcLeafSchemas.ensureCapacity(rpcLeafSchemas.size() + rpcs.size()); for (RpcDefinition rpc : rpcs) { rpcLeafSchemas.add(new OperationsLeafSchemaNode(rpc)); } } } // Now generate a module for RESTCONF so that operations contain what they need final OperationsContainerSchemaNode operatationsSchema = new OperationsContainerSchemaNode(rpcLeafSchemas); modules.add(new OperationsRestconfModule(operatationsSchema)); // Now build the operations container and combine it with the context final DataContainerNodeBuilder operationsBuilder = Builders.containerBuilder() .withNodeIdentifier(new NodeIdentifier(OperationsContainerSchemaNode.QNAME)); for (final OperationsLeafSchemaNode leaf : rpcLeafSchemas) { operationsBuilder.withChild(ImmutableNodes.leafNode(leaf.getQName(), Empty.value())); } final var opContext = new OperationsEffectiveModuleContext(ImmutableSet.copyOf(modules)); final var stack = SchemaInferenceStack.of(opContext); stack.enterSchemaTree(operatationsSchema.getQName()); return Map.entry(InstanceIdentifierContext.ofStack(stack, mountPoint), operationsBuilder.build()); } }