001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.registry.client.impl.zk;
020
021import com.google.common.base.Preconditions;
022import org.apache.hadoop.classification.InterfaceAudience;
023import org.apache.hadoop.classification.InterfaceStability;
024import org.apache.hadoop.registry.client.api.BindFlags;
025import org.apache.hadoop.registry.client.api.RegistryOperations;
026
027import org.apache.hadoop.registry.client.binding.RegistryTypeUtils;
028import org.apache.hadoop.registry.client.binding.RegistryUtils;
029import org.apache.hadoop.registry.client.binding.RegistryPathUtils;
030import org.apache.hadoop.registry.client.exceptions.InvalidPathnameException;
031import org.apache.hadoop.registry.client.exceptions.NoRecordException;
032import org.apache.hadoop.registry.client.types.RegistryPathStatus;
033import org.apache.hadoop.registry.client.types.ServiceRecord;
034import org.apache.zookeeper.CreateMode;
035import org.apache.zookeeper.data.ACL;
036import org.apache.zookeeper.data.Stat;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040import java.io.IOException;
041import java.util.List;
042
043/**
044 * The Registry operations service.
045 * <p>
046 * This service implements the {@link RegistryOperations}
047 * API by mapping the commands to zookeeper operations, and translating
048 * results and exceptions back into those specified by the API.
049 * <p>
050 * Factory methods should hide the detail that this has been implemented via
051 * the {@link CuratorService} by returning it cast to that
052 * {@link RegistryOperations} interface, rather than this implementation class.
053 */
054@InterfaceAudience.Public
055@InterfaceStability.Evolving
056public class RegistryOperationsService extends CuratorService
057  implements RegistryOperations {
058
059  private static final Logger LOG =
060      LoggerFactory.getLogger(RegistryOperationsService.class);
061
062  private final RegistryUtils.ServiceRecordMarshal serviceRecordMarshal
063      = new RegistryUtils.ServiceRecordMarshal();
064
065  public RegistryOperationsService(String name) {
066    this(name, null);
067  }
068
069  public RegistryOperationsService() {
070    this("RegistryOperationsService");
071  }
072
073  public RegistryOperationsService(String name,
074      RegistryBindingSource bindingSource) {
075    super(name, bindingSource);
076  }
077
078  /**
079   * Get the aggregate set of ACLs the client should use
080   * to create directories
081   * @return the ACL list
082   */
083  public List<ACL> getClientAcls() {
084    return getRegistrySecurity().getClientACLs();
085  }
086
087  /**
088   * Validate a path
089   * @param path path to validate
090   * @throws InvalidPathnameException if a path is considered invalid
091   */
092  protected void validatePath(String path) throws InvalidPathnameException {
093    // currently no checks are performed
094  }
095
096  @Override
097  public boolean mknode(String path, boolean createParents) throws IOException {
098    validatePath(path);
099    return zkMkPath(path, CreateMode.PERSISTENT, createParents, getClientAcls());
100  }
101
102  @Override
103  public void bind(String path,
104      ServiceRecord record,
105      int flags) throws IOException {
106    Preconditions.checkArgument(record != null, "null record");
107    validatePath(path);
108    // validate the record before putting it
109    RegistryTypeUtils.validateServiceRecord(path, record);
110    LOG.info("Bound at {} : {}", path, record);
111
112    CreateMode mode = CreateMode.PERSISTENT;
113    byte[] bytes = serviceRecordMarshal.toBytes(record);
114    zkSet(path, mode, bytes, getClientAcls(),
115        ((flags & BindFlags.OVERWRITE) != 0));
116  }
117
118  @Override
119  public ServiceRecord resolve(String path) throws IOException {
120    byte[] bytes = zkRead(path);
121
122    ServiceRecord record = serviceRecordMarshal.fromBytes(path,
123        bytes, ServiceRecord.RECORD_TYPE);
124    RegistryTypeUtils.validateServiceRecord(path, record);
125    return record;
126  }
127
128  @Override
129  public boolean exists(String path) throws IOException {
130    validatePath(path);
131    return zkPathExists(path);
132  }
133
134  @Override
135  public RegistryPathStatus stat(String path) throws IOException {
136    validatePath(path);
137    Stat stat = zkStat(path);
138
139    String name = RegistryPathUtils.lastPathEntry(path);
140    RegistryPathStatus status = new RegistryPathStatus(
141        name,
142        stat.getCtime(),
143        stat.getDataLength(),
144        stat.getNumChildren());
145    if (LOG.isDebugEnabled()) {
146      LOG.debug("Stat {} => {}", path, status);
147    }
148    return status;
149  }
150
151  @Override
152  public List<String> list(String path) throws IOException {
153    validatePath(path);
154    return zkList(path);
155  }
156
157  @Override
158  public void delete(String path, boolean recursive) throws IOException {
159    validatePath(path);
160    zkDelete(path, recursive, null);
161  }
162
163}