/*
* Copyright 2010 Outerthought bvba
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.lilyproject.repository.remote;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Set;
import org.apache.avro.AvroRemoteException;
import org.apache.avro.ipc.Transceiver;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.lilyproject.avro.AvroAuthzContext;
import org.lilyproject.avro.AvroConverter;
import org.lilyproject.avro.AvroGenericException;
import org.lilyproject.avro.AvroLily;
import org.lilyproject.avro.AvroRepositoryException;
import org.lilyproject.repository.api.BlobManager;
import org.lilyproject.repository.api.IORecordException;
import org.lilyproject.repository.api.IdGenerator;
import org.lilyproject.repository.api.MutationCondition;
import org.lilyproject.repository.api.Record;
import org.lilyproject.repository.api.RecordBuilder;
import org.lilyproject.repository.api.RecordException;
import org.lilyproject.repository.api.RecordFactory;
import org.lilyproject.repository.api.RecordId;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.repository.api.TableManager;
import org.lilyproject.repository.impl.AbstractRepositoryManager;
import org.lilyproject.repository.impl.BaseRepository;
import org.lilyproject.repository.impl.RecordBuilderImpl;
import org.lilyproject.repository.impl.RepoTableKey;
import org.lilyproject.repository.spi.AuthorizationContextHolder;
import org.lilyproject.util.io.Closer;
// ATTENTION: when adding new methods, do not forget to add handling for UndeclaredThrowableException! This is
// necessary because, at the time of this writing, Avro did not include IOException in its generated
// interfaces.
public class RemoteRepository extends BaseRepository {
private AvroLily lilyProxy;
private final AvroConverter converter;
private Transceiver client;
private String repositoryName;
private String tableName;
public RemoteRepository(RepoTableKey repoTableKey, AvroLilyTransceiver lilyTransceiver, AvroConverter converter,
AbstractRepositoryManager repositoryManager, BlobManager blobManager, HTableInterface recordTable,
HTableInterface nonAuthRecordTable, TableManager tableManager, RecordFactory recordFactory)
throws IOException, InterruptedException {
super(repoTableKey, repositoryManager, blobManager, recordTable, nonAuthRecordTable, null, tableManager,
recordFactory);
this.converter = converter;
client = lilyTransceiver.getTransceiver();
lilyProxy = lilyTransceiver.getLilyProxy();
this.repositoryName = repoTableKey.getRepositoryName();
this.tableName = repoTableKey.getTableName();
}
@Override
public void close() throws IOException {
Closer.close(client);
}
@Override
public IdGenerator getIdGenerator() {
return idGenerator;
}
private AvroAuthzContext getAuthzContext() {
return converter.convert(AuthorizationContextHolder.getCurrentContext());
}
@Override
public Record create(Record record) throws RepositoryException, InterruptedException {
try {
return converter.convertRecord(lilyProxy.create(getAuthzContext(), converter.convert(record, this),
repositoryName, tableName), this);
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
@Override
public Record delete(RecordId recordId, List<MutationCondition> conditions)
throws RepositoryException, InterruptedException {
try {
ByteBuffer record = lilyProxy.delete(getAuthzContext(), converter.convert(recordId), repositoryName,
tableName, converter.convert(null, conditions, this), null);
return record == null ? null : converter.convertRecord(record, this);
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
@Override
public void delete(RecordId recordId) throws RepositoryException, InterruptedException {
try {
lilyProxy.delete(getAuthzContext(), converter.convert(recordId), repositoryName, tableName, null, null);
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
@Override
public void delete(Record record) throws RepositoryException, InterruptedException {
try {
lilyProxy.delete(getAuthzContext(), converter.convert(record.getId()), repositoryName, tableName, null,
record.getAttributes());
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
@Override
public Record update(Record record) throws RepositoryException, InterruptedException {
return update(record, false, true);
}
@Override
public Record update(Record record, List<MutationCondition> conditions)
throws RepositoryException, InterruptedException {
return update(record, false, true, conditions);
}
@Override
public Record update(Record record, boolean updateVersion, boolean useLatestRecordType)
throws RepositoryException, InterruptedException {
return update(record, updateVersion, useLatestRecordType, null);
}
@Override
public Record update(Record record, boolean updateVersion, boolean useLatestRecordType,
List<MutationCondition> conditions) throws RepositoryException, InterruptedException {
try {
return converter
.convertRecord(lilyProxy.update(getAuthzContext(), converter.convert(record, this), repositoryName,
tableName, updateVersion, useLatestRecordType, converter.convert(record, conditions, this)),
this);
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
@Override
public Record createOrUpdate(Record record) throws RepositoryException, InterruptedException {
return createOrUpdate(record, true);
}
@Override
public Record createOrUpdate(Record record, boolean useLatestRecordType)
throws RepositoryException, InterruptedException {
try {
return converter.convertRecord(lilyProxy.createOrUpdate(getAuthzContext(), converter.convert(record, this),
repositoryName, tableName, useLatestRecordType), this);
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
@Override
public Set<RecordId> getVariants(RecordId recordId) throws RepositoryException, InterruptedException {
try {
return converter.convertAvroRecordIds(lilyProxy.getVariants(getAuthzContext(), converter.convert(recordId),
repositoryName, tableName), this);
} catch (AvroRepositoryException e) {
throw converter.convert(e);
} catch (AvroGenericException e) {
throw converter.convert(e);
} catch (AvroRemoteException e) {
throw handleAvroRemoteException(e);
} catch (UndeclaredThrowableException e) {
throw handleUndeclaredRecordThrowable(e);
}
}
private RuntimeException handleUndeclaredRecordThrowable(UndeclaredThrowableException e) throws RecordException {
if (e.getCause() instanceof IOException) {
throw new IORecordException(e.getCause());
} else {
throw e;
}
}
private RuntimeException handleAvroRemoteException(AvroRemoteException e) throws RecordException {
// AvroRemoteException's are exceptions which are not declared in the avro protocol and
// which are not RuntimeException's.
if (e.getCause() instanceof IOException) {
throw new IORecordException(e.getCause());
} else {
throw converter.convert(e);
}
}
@Override
public RecordBuilder recordBuilder() throws RecordException {
return new RecordBuilderImpl(this, getIdGenerator());
}
}