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.common.keymaster.client.zookeeper;
20  
21  import com.fasterxml.jackson.databind.json.JsonMapper;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.UUID;
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.curator.framework.CuratorFramework;
27  import org.apache.syncope.common.keymaster.client.api.KeymasterException;
28  import org.apache.syncope.common.keymaster.client.api.ServiceOps;
29  import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
30  import org.apache.zookeeper.CreateMode;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  import org.springframework.beans.factory.annotation.Autowired;
34  
35  /**
36   * Implements {@link ServiceOps} via Apache Curator / Zookeeper.
37   */
38  public class ZookeeperServiceOps implements ServiceOps {
39  
40      protected static final Logger LOG = LoggerFactory.getLogger(ServiceOps.class);
41  
42      protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
43  
44      protected static final String SERVICE_PATH = "/services";
45  
46      @Autowired
47      protected CuratorFramework client;
48  
49      protected static String buildServicePath(final NetworkService.Type serviceType, final String... parts) {
50          String prefix = SERVICE_PATH + '/' + serviceType.name();
51          String suffix = StringUtils.EMPTY;
52          if (parts != null && parts.length > 0) {
53              suffix = '/' + String.join("/", parts);
54          }
55          return prefix + suffix;
56      }
57  
58      @Override
59      public void register(final NetworkService service) {
60          String id = UUID.randomUUID().toString();
61          try {
62              unregister(service);
63  
64              if (client.checkExists().forPath(buildServicePath(service.getType(), id)) == null) {
65                  client.create().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).
66                          forPath(buildServicePath(service.getType(), id));
67              }
68  
69              client.setData().forPath(
70                      buildServicePath(service.getType(), id), MAPPER.writeValueAsBytes(service));
71          } catch (Exception e) {
72              LOG.error("While writing {}", buildServicePath(service.getType(), id), e);
73              throw new KeymasterException(e);
74          }
75      }
76  
77      @Override
78      public void unregister(final NetworkService service) {
79          try {
80              if (client.checkExists().forPath(buildServicePath(service.getType())) != null) {
81                  client.getChildren().forPath(buildServicePath(service.getType())).stream().
82                          filter(child -> {
83                              try {
84                                  return MAPPER.readValue(client.getData().forPath(
85                                          buildServicePath(service.getType(), child)), NetworkService.class).
86                                          equals(service);
87                              } catch (Exception e) {
88                                  LOG.error("While deregistering {}", service, e);
89                                  throw new KeymasterException(e);
90                              }
91                          }).
92                          findFirst().ifPresent(child -> {
93                              try {
94                                  client.delete().forPath(buildServicePath(service.getType(), child));
95                              } catch (Exception e) {
96                                  LOG.error("While deregistering {}", service, e);
97                                  throw new KeymasterException(e);
98                              }
99                          });
100             }
101         } catch (KeymasterException e) {
102             throw e;
103         } catch (Exception e) {
104             throw new KeymasterException(e);
105         }
106     }
107 
108     @Override
109     public List<NetworkService> list(final NetworkService.Type serviceType) {
110         try {
111             if (client.checkExists().forPath(buildServicePath(serviceType)) == null) {
112                 client.create().creatingParentContainersIfNeeded().forPath(buildServicePath(serviceType));
113             }
114 
115             List<NetworkService> list = new ArrayList<>();
116             for (String child : client.getChildren().forPath(buildServicePath(serviceType))) {
117                 list.add(MAPPER.readValue(client.getData().forPath(buildServicePath(serviceType, child)),
118                         NetworkService.class));
119             }
120 
121             return list;
122         } catch (Exception e) {
123             throw new KeymasterException(e);
124         }
125     }
126 
127     @Override
128     public NetworkService get(final NetworkService.Type serviceType) {
129         List<NetworkService> list = list(serviceType);
130         if (list.isEmpty()) {
131             throw new KeymasterException("No registered services for type " + serviceType);
132         }
133 
134         // always returns first instance, can be improved
135         return list.get(0);
136     }
137 }