View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.logic;
20  
21  import java.lang.reflect.Method;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Objects;
25  import java.util.Optional;
26  import java.util.Set;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.stream.Collectors;
29  import javax.validation.ConstraintViolation;
30  import javax.validation.ValidationException;
31  import javax.validation.Validator;
32  import org.apache.commons.lang3.tuple.Pair;
33  import org.apache.syncope.common.lib.SyncopeClientException;
34  import org.apache.syncope.common.lib.command.CommandArgs;
35  import org.apache.syncope.common.lib.command.CommandTO;
36  import org.apache.syncope.common.lib.to.EntityTO;
37  import org.apache.syncope.common.lib.types.ClientExceptionType;
38  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
39  import org.apache.syncope.common.lib.types.IdRepoImplementationType;
40  import org.apache.syncope.core.logic.api.Command;
41  import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
42  import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
43  import org.apache.syncope.core.persistence.api.dao.NotFoundException;
44  import org.apache.syncope.core.persistence.api.entity.Implementation;
45  import org.apache.syncope.core.spring.implementation.ImplementationManager;
46  import org.springframework.security.access.prepost.PreAuthorize;
47  import org.springframework.transaction.annotation.Transactional;
48  
49  public class CommandLogic extends AbstractLogic<EntityTO> {
50  
51      protected final ImplementationDAO implementationDAO;
52  
53      protected final Validator validator;
54  
55      protected final Map<String, Command<?>> perContextCommands = new ConcurrentHashMap<>();
56  
57      public CommandLogic(final ImplementationDAO implementationDAO, final Validator validator) {
58          this.implementationDAO = implementationDAO;
59          this.validator = validator;
60      }
61  
62      @PreAuthorize("hasRole('" + IdRepoEntitlement.IMPLEMENTATION_LIST + "')")
63      @Transactional(readOnly = true)
64      public Pair<Integer, List<CommandTO>> search(final int page, final int size, final String keyword) {
65          List<Implementation> result = implementationDAO.findByTypeAndKeyword(IdRepoImplementationType.COMMAND, keyword);
66  
67          int count = result.size();
68  
69          List<CommandTO> commands = result.stream().
70                  skip((page - 1) * size).
71                  limit(size).
72                  map(command -> {
73                      try {
74                          return new CommandTO.Builder(command.getKey()).
75                                  args(ImplementationManager.emptyArgs(command)).build();
76                      } catch (Exception e) {
77                          LOG.error("Could not get arg class for {}", command, e);
78                          return null;
79                      }
80                  }).
81                  filter(Objects::nonNull).
82                  collect(Collectors.toList());
83  
84          return Pair.of(count, commands);
85      }
86  
87      @PreAuthorize("hasRole('" + IdRepoEntitlement.IMPLEMENTATION_READ + "')")
88      @Transactional(readOnly = true)
89      public CommandTO read(final String key) {
90          Implementation impl = Optional.ofNullable(implementationDAO.find(key)).
91                  orElseThrow(() -> new NotFoundException("Implementation " + key));
92  
93          try {
94              return new CommandTO.Builder(impl.getKey()).
95                      args(ImplementationManager.emptyArgs(impl)).build();
96          } catch (Exception e) {
97              SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidImplementation);
98              sce.getElements().add("Could not build " + impl.getKey());
99              throw sce;
100         }
101     }
102 
103     @PreAuthorize("hasRole('" + IdRepoEntitlement.COMMAND_RUN + "')")
104     @SuppressWarnings("unchecked")
105     public String run(final CommandTO command) {
106         Implementation impl = Optional.ofNullable(implementationDAO.find(command.getKey())).
107                 orElseThrow(() -> new NotFoundException("Implementation " + command.getKey()));
108 
109         Command<CommandArgs> runnable;
110         try {
111             runnable = (Command<CommandArgs>) ImplementationManager.build(
112                     impl,
113                     () -> perContextCommands.get(impl.getKey()),
114                     instance -> perContextCommands.put(impl.getKey(), instance));
115         } catch (Exception e) {
116             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidImplementation);
117             sce.getElements().add("Could not build " + impl.getKey());
118             throw sce;
119         }
120 
121         if (command.getArgs() != null) {
122             try {
123                 Set<ConstraintViolation<Object>> violations = validator.validate(command.getArgs());
124                 if (!violations.isEmpty()) {
125                     throw new InvalidEntityException(command.getArgs().getClass().getName(), violations);
126                 }
127             } catch (ValidationException e) {
128                 LOG.error("While validating {}", command.getArgs(), e);
129 
130                 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidValues);
131                 sce.getElements().add(e.getMessage());
132                 throw sce;
133             }
134         }
135 
136         try {
137             return runnable.run(command.getArgs());
138         } catch (Exception e) {
139             LOG.error("While running {} on {}", command.getKey(), command.getArgs(), e);
140 
141             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RunError);
142             sce.getElements().add(e.getMessage());
143             throw sce;
144         }
145     }
146 
147     @Override
148     protected EntityTO resolveReference(final Method method, final Object... args)
149             throws UnresolvedReferenceException {
150 
151         throw new UnsupportedOperationException();
152     }
153 }