/**
* Copyright (C) 2009-2015 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.store.format;
import com.foundationdb.ais.model.HasStorage;
import com.foundationdb.ais.model.StorageDescription;
import com.foundationdb.ais.model.validation.AISValidationFailure;
import com.foundationdb.ais.model.validation.AISValidationOutput;
import com.foundationdb.ais.protobuf.AISProtobuf.Storage;
import com.foundationdb.ais.protobuf.MemoryProtobuf;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.row.ValuesHolderRow;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.qp.rowtype.Schema;
import com.foundationdb.server.error.AkibanInternalException;
import com.foundationdb.server.error.StorageDescriptionInvalidException;
import com.foundationdb.server.service.session.Session;
import com.foundationdb.server.store.StoreStorageDescription;
import com.foundationdb.server.store.MemoryStore;
import com.foundationdb.server.store.MemoryStoreData;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.server.types.value.ValueSources;
import com.foundationdb.util.WrappingByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.UUID;
/** Row<->byte[] via standard Java serialization. */
public class MemoryStorageDescription extends StoreStorageDescription<MemoryStore,MemoryStoreData>
{
private static final Logger LOG = LoggerFactory.getLogger(MemoryStorageDescription.class);
private volatile UUID uuid;
private volatile byte[] uuidBytes;
public MemoryStorageDescription(HasStorage forObject, String storageFormat) {
super(forObject, storageFormat);
}
public MemoryStorageDescription(HasStorage forObject, MemoryStorageDescription other, String storageFormat) {
super(forObject, other, storageFormat);
setUUID(other.uuid);
}
public void setUUID(UUID uuid) {
assert this.uuid == null;
this.uuid = uuid;
this.uuidBytes = (uuid == null) ? null : MemoryStore.packUUID(uuid);
}
public UUID getUUID() {
return uuid;
}
public byte[] getUUIDBytes() {
return uuidBytes;
}
//
// StorageDescription
//
@Override
public StorageDescription cloneForObject(HasStorage forObject) {
return new MemoryStorageDescription(forObject, this, storageFormat);
}
@Override
public StorageDescription cloneForObjectWithoutState(HasStorage forObject) {
return new MemoryStorageDescription(forObject, storageFormat);
}
@Override
public void writeProtobuf(Storage.Builder builder) {
builder.setExtension(MemoryProtobuf.uuid, uuid.toString());
writeUnknownFields(builder);
}
@Override
public Object getUniqueKey() {
return uuid;
}
@Override
public String getNameString() {
return uuid.toString();
}
@Override
public void validate(AISValidationOutput output) {
if(uuid == null) {
output.reportFailure(new AISValidationFailure(new StorageDescriptionInvalidException(object, "is missing UUID")));
}
}
//
// StoreStorageDescription
//
@Override
public Row expandRow(MemoryStore store, Session session, MemoryStoreData storeData, Schema schema) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(storeData.rawValue);
ObjectInputStream ois = new ObjectInputStream(bais);
int tableID = ois.readInt();
RowType rowType = schema.tableRowType(tableID);
Object[] fields = new Object[rowType.nFields()];
for(int i = 0; i < rowType.nFields(); ++i) {
fields[i] = ois.readObject();
}
return new ValuesHolderRow(rowType, fields);
} catch(ClassNotFoundException | IOException e) {
LOG.error("Expand failure", e);
throw new AkibanInternalException("Expand failure", e);
}
}
@Override
public void packRow(MemoryStore store, Session session, MemoryStoreData storeData, Row row) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
// Table ID
oos.writeInt(row.rowType().table().getTableId());
// Values
for(int i = 0; i < row.rowType().nFields(); ++i) {
ValueSource value = row.value(i);
Object o = ValueSources.toObject(value);
if(o instanceof WrappingByteSource) {
WrappingByteSource wbs = (WrappingByteSource)o;
byte[] bytes = wbs.byteArray();
if((wbs.byteArrayOffset() != 0) || (wbs.byteArrayLength() != bytes.length)) {
bytes = Arrays.copyOfRange(bytes, wbs.byteArrayOffset(), wbs.byteArrayLength());
}
o = bytes;
}
oos.writeObject(o);
}
storeData.rawValue = baos.toByteArray();
} catch(IOException e) {
LOG.error("Packing failure", e);
throw new AkibanInternalException("Pack failure", e);
}
}
}