/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.pentaho.di.core.attributes.metastore;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.pentaho.di.core.AttributesInterface;
import org.pentaho.metastore.api.BaseMetaStore;
import org.pentaho.metastore.api.IMetaStoreAttribute;
import org.pentaho.metastore.api.IMetaStoreElement;
import org.pentaho.metastore.api.IMetaStoreElementType;
import org.pentaho.metastore.api.exceptions.MetaStoreDependenciesExistsException;
import org.pentaho.metastore.api.exceptions.MetaStoreElementExistException;
import org.pentaho.metastore.api.exceptions.MetaStoreElementTypeExistsException;
import org.pentaho.metastore.api.exceptions.MetaStoreException;
import org.pentaho.metastore.api.security.IMetaStoreElementOwner;
import org.pentaho.metastore.api.security.MetaStoreElementOwnerType;
import org.pentaho.metastore.stores.memory.MemoryMetaStoreAttribute;
import org.pentaho.metastore.stores.memory.MemoryMetaStoreElement;
import org.pentaho.metastore.stores.memory.MemoryMetaStoreElementOwner;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.pentaho.metastore.util.MetaStoreUtil;
/**
* @author nhudak
*/
public class EmbeddedMetaStore extends BaseMetaStore implements ReadWriteLock {
static final String METASTORE_PREFIX = "METASTORE.";
static final String TYPE_PREFIX = "TYPE.";
private final AttributesInterface attributesInterface;
private final ReadWriteLock lock;
public EmbeddedMetaStore( AttributesInterface attributesInterface ) {
this.attributesInterface = attributesInterface;
this.lock = new ReentrantReadWriteLock();
}
@Override public void createNamespace( final String namespace ) throws MetaStoreException {
// Optional, namespace will be automatically created if not already existing
MetaStoreUtil.executeLockedOperation( writeLock(), new Callable<Void>() {
@Override public Void call() throws Exception {
String groupName = JsonElementType.groupName( namespace );
if ( !attributesInterface.getAttributesMap().containsKey( groupName ) ) {
attributesInterface.setAttributes( groupName, Maps.<String, String>newHashMap() );
}
return null;
}
} );
}
@Override public List<String> getNamespaces() throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<List<String>>() {
@Override public List<String> call() throws Exception {
return FluentIterable.from( attributesInterface.getAttributesMap().keySet() )
.filter( new Predicate<String>() {
@Override public boolean apply( String groupName ) {
return groupName.startsWith( METASTORE_PREFIX );
}
} )
.transform( new Function<String, String>() {
@Override public String apply( String input ) {
return input.substring( METASTORE_PREFIX.length() );
}
} )
.toList();
}
} );
}
@Override public boolean namespaceExists( final String namespace ) throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<Boolean>() {
@Override public Boolean call() throws Exception {
return attributesInterface.getAttributesMap().containsKey( JsonElementType.groupName( namespace ) );
}
} );
}
@Override public synchronized void deleteNamespace( final String namespace ) throws MetaStoreException {
MetaStoreUtil.executeLockedOperation( writeLock(), new Callable<Void>() {
@Override public Void call() throws Exception {
List<String> dependencies = getElementTypeIds( namespace );
if ( !dependencies.isEmpty() ) {
throw new MetaStoreDependenciesExistsException( dependencies,
"Unable to delete the meta store namespace '" + namespace + "' as it still contains element types" );
}
attributesInterface.getAttributesMap().remove( JsonElementType.groupName( namespace ) );
return null;
}
} );
}
@Override
public JsonElementType newElementType( final String namespace ) {
return new EmbeddedElementType( namespace );
}
private class EmbeddedElementType extends JsonElementType {
public EmbeddedElementType( String namespace ) {
super( namespace );
}
@Override public void save() throws MetaStoreException {
update( this );
}
}
@Override
public void createElementType( String namespace, IMetaStoreElementType elementType ) throws MetaStoreException {
elementType.setNamespace( namespace );
if ( !create( JsonElementType.from( elementType ) ) ) {
throw new MetaStoreElementTypeExistsException( getElementTypes( namespace ) );
}
}
@Override public JsonElementType getElementType( final String namespace, final String elementTypeId )
throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<JsonElementType>() {
@Override public JsonElementType call() throws Exception {
JsonElementType type = newElementType( namespace );
type.setId( elementTypeId );
String jsonData = attributesInterface.getAttribute( type.groupName(), type.key() );
return jsonData == null ? null : type.load( jsonData );
}
} );
}
@Override public List<String> getElementTypeIds( final String namespace ) throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<List<String>>() {
@Override public List<String> call() throws Exception {
Map<String, String> attributes = attributesInterface.getAttributes( JsonElementType.groupName( namespace ) );
return attributes == null ? ImmutableList.<String>of() : ImmutableList.copyOf( attributes.keySet() );
}
} );
}
@Override public List<IMetaStoreElementType> getElementTypes( final String namespace )
throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<List<IMetaStoreElementType>>() {
@Override public List<IMetaStoreElementType> call() throws Exception {
List<String> ids = getElementTypeIds( namespace );
List<IMetaStoreElementType> types = Lists.newArrayListWithExpectedSize( ids.size() );
for ( String id : ids ) {
types.add( getElementType( namespace, id ) );
}
return types;
}
} );
}
@Override public IMetaStoreElementType getElementTypeByName( final String namespace, final String elementTypeName )
throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<IMetaStoreElementType>() {
@Override public IMetaStoreElementType call() throws Exception {
List<IMetaStoreElementType> elementTypes = getElementTypes( namespace );
for ( IMetaStoreElementType elementType : elementTypes ) {
if ( elementType.getName().equals( elementTypeName ) ) {
return elementType;
}
}
return null;
}
} );
}
@Override public void updateElementType( String namespace, IMetaStoreElementType elementType )
throws MetaStoreException {
elementType.setNamespace( namespace );
update( JsonElementType.from( elementType ) );
}
@Override public void deleteElementType( final String namespace,
final IMetaStoreElementType elementType ) throws MetaStoreException {
MetaStoreUtil.executeLockedOperation( writeLock(), new Callable<Void>() {
@Override public Void call() throws Exception {
List<String> dependencies = getElementIds( namespace, elementType );
if ( dependencies.isEmpty() ) {
attributesInterface.getAttributesMap().remove( JsonElement.groupName( elementType ) );
Map<String, String> typeMap = attributesInterface.getAttributes( JsonElementType.groupName( namespace ) );
if ( typeMap != null ) {
typeMap.remove( elementType.getId() );
}
} else {
throw new MetaStoreDependenciesExistsException( dependencies );
}
return null;
}
}
);
}
@Override public JsonElement newElement() {
return new JsonElement();
}
@Override public JsonElement newElement( IMetaStoreElementType elementType, String id, Object value ) {
return new JsonElement( new MemoryMetaStoreElement( elementType, id, value ) );
}
@Override public IMetaStoreAttribute newAttribute( String id, Object value ) {
return new MemoryMetaStoreAttribute( id, value );
}
@Override public IMetaStoreElementOwner newElementOwner( String name, MetaStoreElementOwnerType ownerType ) {
return new MemoryMetaStoreElementOwner( name, ownerType );
}
@Override public void createElement( String namespace, IMetaStoreElementType elementType,
IMetaStoreElement element ) throws MetaStoreException {
elementType.setNamespace( namespace );
element.setElementType( elementType );
update( JsonElementType.from( elementType ) );
if ( !create( JsonElement.from( element ) ) ) {
throw new MetaStoreElementExistException( getElements( namespace, elementType ) );
}
}
@Override
public List<IMetaStoreElement> getElements( final String namespace,
final IMetaStoreElementType elementType ) throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<List<IMetaStoreElement>>() {
@Override public List<IMetaStoreElement> call() throws Exception {
List<String> ids = getElementIds( namespace, elementType );
List<IMetaStoreElement> types = Lists.newArrayListWithExpectedSize( ids.size() );
for ( String id : ids ) {
types.add( getElement( namespace, elementType, id ) );
}
return types;
}
} );
}
@Override public List<String> getElementIds( final String namespace,
final IMetaStoreElementType elementType ) throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<List<String>>() {
@Override public List<String> call() throws Exception {
elementType.setNamespace( namespace );
Map<String, String> attributes = attributesInterface.getAttributes( JsonElement.groupName( elementType ) );
return attributes == null ? ImmutableList.<String>of() : ImmutableList.copyOf( attributes.keySet() );
}
}
);
}
@Override public JsonElement getElement( final String namespace, final IMetaStoreElementType elementType,
final String elementId ) throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( readLock(), new Callable<JsonElement>() {
@Override public JsonElement call() throws Exception {
JsonElement element = newElement();
elementType.setNamespace( namespace );
element.setId( elementId );
element.setElementType( elementType );
String jsonData = attributesInterface.getAttribute( element.groupName(), element.key() );
return jsonData == null ? null : element.load( jsonData );
}
} );
}
@Override
public IMetaStoreElement getElementByName( String namespace, IMetaStoreElementType elementType, final String name )
throws MetaStoreException {
for ( IMetaStoreElement element : getElements( namespace, elementType ) ) {
if ( element.getName().equals( name ) ) {
return element;
}
}
return null;
}
@Override public void updateElement( String namespace, IMetaStoreElementType elementType, String elementId,
IMetaStoreElement element ) throws MetaStoreException {
elementType.setNamespace( namespace );
element.setId( elementId );
element.setElementType( elementType );
update( JsonElementType.from( elementType ) );
update( JsonElement.from( element ) );
}
@Override
public void deleteElement( final String namespace, final IMetaStoreElementType elementType, final String elementId )
throws MetaStoreException {
MetaStoreUtil.executeLockedOperation( writeLock(), new Callable<Boolean>() {
@Override public Boolean call() throws Exception {
elementType.setNamespace( namespace );
Map<String, String> attributes = attributesInterface.getAttributes( JsonElement.groupName( elementType ) );
return attributes != null && attributes.remove( elementId ) != null;
}
} );
}
private boolean create( final AttributesInterfaceEntry entry ) throws MetaStoreException {
return MetaStoreUtil.executeLockedOperation( writeLock(), new Callable<Boolean>() {
@Override public Boolean call() throws Exception {
String groupName = entry.groupName();
String key = entry.key();
String existing = attributesInterface.getAttribute( groupName, key );
if ( existing == null ) {
attributesInterface.setAttribute( groupName, key, entry.jsonValue() );
return true;
} else {
return false;
}
}
} );
}
private void update( final AttributesInterfaceEntry entry ) throws MetaStoreException {
MetaStoreUtil.executeLockedOperation( writeLock(), new Callable<Void>() {
@Override public Void call() throws Exception {
attributesInterface.setAttribute( entry.groupName(), entry.key(), entry.jsonValue() );
return null;
}
} );
}
@Override public Lock readLock() {
return lock.readLock();
}
@Override public Lock writeLock() {
return lock.writeLock();
}
}