code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.sdk.server.services;
  
  import java.util.List;
  import java.util.Optional;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.ExecutorService;
  import java.util.stream.Stream;
  
  import org.eclipse.milo.opcua.sdk.server.DiagnosticsContext;
  import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
  import org.eclipse.milo.opcua.sdk.server.Session;
  import org.eclipse.milo.opcua.sdk.server.api.services.ViewServices.RegisterNodesContext;
  import org.eclipse.milo.opcua.sdk.server.api.services.ViewServices.UnregisterNodesContext;
  import org.eclipse.milo.opcua.sdk.server.services.helpers.BrowseHelper;
  import org.eclipse.milo.opcua.sdk.server.services.helpers.BrowsePathsHelper;
  import org.eclipse.milo.opcua.stack.core.StatusCodes;
  import org.eclipse.milo.opcua.stack.core.UaException;
  import org.eclipse.milo.opcua.stack.core.types.builtin.DiagnosticInfo;
  import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseDescription;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.BrowseResult;
  import org.eclipse.milo.opcua.stack.core.types.structured.RegisterNodesRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.RegisterNodesResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.ResponseHeader;
  import org.eclipse.milo.opcua.stack.core.types.structured.UnregisterNodesRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.UnregisterNodesResponse;
  import org.eclipse.milo.opcua.stack.core.util.FutureUtils;
  import org.eclipse.milo.opcua.stack.server.services.ServiceRequest;
  import org.eclipse.milo.opcua.stack.server.services.ViewServiceSet;
  
  import static org.eclipse.milo.opcua.stack.core.util.ConversionUtil.a;
  import static org.eclipse.milo.opcua.stack.core.util.ConversionUtil.l;
  
  public class DefaultViewServiceSet implements ViewServiceSet {
  
      private final ServiceCounter browseCounter = new ServiceCounter();
      private final ServiceCounter browseNextCounter = new ServiceCounter();
      private final ServiceCounter translateBrowsePathsCounter = new ServiceCounter();
  
      private final BrowseHelper browseHelper;
  
      public DefaultViewServiceSet(ExecutorService executor) {
          browseHelper = new BrowseHelper(executor);
      }
  
      @Override
      public void onBrowse(ServiceRequest service) {
          browseCounter.record(service);
  
          BrowseRequest request = (BrowseRequest) service.getRequest();
  
          DiagnosticsContext<BrowseDescription> diagnosticsContext = new DiagnosticsContext<>();
  
          OpcUaServer server = service.attr(ServiceAttributes.SERVER_KEY).get();
          Session session = service.attr(ServiceAttributes.SESSION_KEY).get();
  
          List<BrowseDescription> nodesToBrowse = l(request.getNodesToBrowse());
  
          if (nodesToBrowse.isEmpty()) {
              service.setServiceFault(StatusCodes.Bad_NothingToDo);
              return;
          }
  
          if (nodesToBrowse.size() > server.getConfig().getLimits().getMaxNodesPerBrowse().intValue()) {
              service.setServiceFault(StatusCodes.Bad_TooManyOperations);
              return;
          }
  
          if (request.getView().getViewId().isNotNull() &&
              !server.getRegisteredViews().contains(request.getView().getViewId())) {
  
              service.setServiceFault(StatusCodes.Bad_ViewIdUnknown);
              return;
          }
  
          Stream<CompletableFuture<BrowseResult>> futures = nodesToBrowse.stream().map(
              browseDescription ->
                  browseHelper.browse(
                      () -> Optional.of(session),
                      server,
                      request.getView(),
                      request.getRequestedMaxReferencesPerNode(),
                      browseDescription
                  )
          );
  
          FutureUtils.sequence(futures).thenAccept(results -> {
              ResponseHeader header = service.createResponseHeader();
  
              DiagnosticInfo[] diagnosticInfos =
                  diagnosticsContext.getDiagnosticInfos(nodesToBrowse);
  
              BrowseResponse response = new BrowseResponse(
                  header,
                  a(results, BrowseResult.class),
                  diagnosticInfos
              );
  
              service.setResponse(response);
          });
      }
  
      @Override
      public void onBrowseNext(ServiceRequest service) {
          browseNextCounter.record(service);
  
          browseHelper.browseNext(service);
      }
  
      @Override
      public void onTranslateBrowsePaths(ServiceRequest service) {
          translateBrowsePathsCounter.record(service);
  
          OpcUaServer server = service.attr(ServiceAttributes.SERVER_KEY).get();
          Session session = service.attr(ServiceAttributes.SESSION_KEY).get();
  
          BrowsePathsHelper browsePathsHelper = new BrowsePathsHelper(
              () -> Optional.ofNullable(session),
              server
          );
  
          browsePathsHelper.onTranslateBrowsePaths(service);
      }
  
      @Override
      public void onRegisterNodes(ServiceRequest service) throws UaException {
          OpcUaServer server = service.attr(ServiceAttributes.SERVER_KEY).get();
          Session session = service.attr(ServiceAttributes.SESSION_KEY).get();
  
          RegisterNodesRequest request = (RegisterNodesRequest) service.getRequest();
  
          List<NodeId> nodeIds = l(request.getNodesToRegister());
  
          if (nodeIds.isEmpty()) {
              throw new UaException(StatusCodes.Bad_NothingToDo);
          }
  
          if (nodeIds.size() > server.getConfig().getLimits().getMaxNodesPerRegisterNodes().intValue()) {
              throw new UaException(StatusCodes.Bad_TooManyOperations);
          }
  
          RegisterNodesContext context = new RegisterNodesContext(server, session);
  
          server.getAddressSpaceManager().registerNodes(context, nodeIds);
  
          context.getFuture().thenAccept(registeredNodeIds -> {
              ResponseHeader header = service.createResponseHeader();
  
              RegisterNodesResponse response = new RegisterNodesResponse(
                  header,
                  registeredNodeIds.toArray(new NodeId[0])
              );
  
              service.setResponse(response);
          });
      }
  
      @Override
      public void onUnregisterNodes(ServiceRequest service) throws UaException {
          OpcUaServer server = service.attr(ServiceAttributes.SERVER_KEY).get();
          Session session = service.attr(ServiceAttributes.SESSION_KEY).get();
  
          UnregisterNodesRequest request = (UnregisterNodesRequest) service.getRequest();
  
          List<NodeId> nodeIds = l(request.getNodesToUnregister());
  
          if (nodeIds.isEmpty()) {
              throw new UaException(StatusCodes.Bad_NothingToDo);
          }
  
          if (nodeIds.size() > server.getConfig().getLimits().getMaxNodesPerRegisterNodes().intValue()) {
              throw new UaException(StatusCodes.Bad_TooManyOperations);
          }
  
          UnregisterNodesContext context = new UnregisterNodesContext(server, session);
  
          server.getAddressSpaceManager().unregisterNodes(context, nodeIds);
  
          context.getFuture().thenAccept(registeredNodeIds -> {
              ResponseHeader header = service.createResponseHeader();
  
              UnregisterNodesResponse response = new UnregisterNodesResponse(header);
  
              service.setResponse(response);
          });
      }
  
  }
  