/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.usergrid.persistence.cassandra; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.usergrid.persistence.ConnectedEntityRef; import org.apache.usergrid.persistence.ConnectionRef; import org.apache.usergrid.persistence.EntityRef; import org.apache.usergrid.persistence.SimpleEntityRef; import org.apache.usergrid.persistence.model.entity.Id; import org.apache.usergrid.persistence.model.entity.SimpleId; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang.StringUtils; import static org.apache.usergrid.persistence.SimpleEntityRef.ref; import static org.apache.usergrid.utils.ConversionUtils.ascii; import static org.apache.usergrid.utils.ConversionUtils.uuidToBytesNullOk; /** @author edanuff */ public class ConnectionRefImpl implements ConnectionRef { public static final int MAX_LINKS = 1; /** * */ public static final int ALL = 0; /** * */ public static final int BY_CONNECTION_TYPE = 1; /** * */ public static final int BY_ENTITY_TYPE = 2; /** * */ public static final int BY_CONNECTION_AND_ENTITY_TYPE = 3; /** * */ public static final String NULL_ENTITY_TYPE = "Null"; /** * */ public static final UUID NULL_ID = new UUID( 0, 0 ); private static final Logger logger = LoggerFactory.getLogger( ConnectionRefImpl.class ); public static final String CONNECTION_ENTITY_TYPE = "Connection"; public static final String CONNECTION_ENTITY_CONNECTION_TYPE = "connection"; private final EntityRef connectingEntity; private final List<ConnectedEntityRef> pairedConnections; private final ConnectedEntityRef connectedEntity; /** * */ public ConnectionRefImpl() { connectingEntity = SimpleEntityRef.ref(); pairedConnections = Collections.emptyList(); connectedEntity = new ConnectedEntityRefImpl(); } /** * @param connectingEntityType * @param connectingEntityId * @param connectionType * @param connectedEntityType * @param connectedEntityId */ public ConnectionRefImpl( String connectingEntityType, UUID connectingEntityId, String connectionType, String connectedEntityType, UUID connectedEntityId ) { connectingEntity = ref( connectingEntityType, connectingEntityId ); pairedConnections = Collections.emptyList(); connectedEntity = new ConnectedEntityRefImpl( connectionType, connectedEntityType, connectedEntityId ); } /** Create a connection from the source to the target entity */ public ConnectionRefImpl( EntityRef source, String connectionType, EntityRef target ) { this.connectingEntity = ref( source ); pairedConnections = Collections.emptyList(); this.connectedEntity = new ConnectedEntityRefImpl( connectionType, target ); } public ConnectionRefImpl( ConnectionRef connection ) { connectingEntity = connection.getSourceRefs(); List<ConnectedEntityRef> pc = connection.getPairedConnections(); if ( pc == null ) { pc = Collections.emptyList(); } pairedConnections = pc; connectedEntity = connection.getTargetRefs(); } public ConnectionRefImpl( EntityRef connectingEntity, ConnectedEntityRef... connections ) { this.connectingEntity = ref( connectingEntity ); ConnectedEntityRef ce = new ConnectedEntityRefImpl(); List<ConnectedEntityRef> pc = Collections.emptyList(); if ( connections.length > 0 ) { ce = connections[connections.length - 1]; if ( connections.length > 1 ) { pc = Arrays.asList( Arrays.copyOfRange( connections, 0, connections.length - 2 ) ); } } pairedConnections = pc; connectedEntity = ce; } public ConnectionRefImpl( ConnectionRef connection, ConnectedEntityRef... connections ) { if ( connection == null ) { throw new NullPointerException( "ConnectionImpl constructor \'connection\' cannot be null" ); } connectingEntity = connection.getSourceRefs(); if ( connections.length > 0 ) { pairedConnections = new ArrayList<ConnectedEntityRef>(); pairedConnections.addAll( connection.getPairedConnections() ); pairedConnections.add( connection.getTargetRefs() ); connectedEntity = connections[connections.length - 1]; if ( connections.length > 1 ) { pairedConnections .addAll( Arrays.asList( Arrays.copyOfRange( connections, 0, connections.length - 2 ) ) ); } } else { pairedConnections = new ArrayList<ConnectedEntityRef>(); connectedEntity = new ConnectedEntityRefImpl(); } } public ConnectionRefImpl( EntityRef connectingEntity, List<ConnectedEntityRef> pairedConnections, ConnectedEntityRef connectedEntity ) { this.connectingEntity = connectingEntity; this.pairedConnections = pairedConnections; this.connectedEntity = connectedEntity; } public UUID getSearchIndexId() { return null; } public String getSearchConnectionType() { return null; } public String getSearchResultType() { return null; } public String getSearchIndexName() { return null; } @Override public EntityRef getSourceRefs() { return connectingEntity; } /** * @return */ public String getConnectingEntityType() { if ( connectingEntity == null ) { return null; } return connectingEntity.getType(); } /** * @return */ public UUID getConnectingEntityId() { if ( connectingEntity == null ) { return null; } return connectingEntity.getUuid(); } @Override public List<ConnectedEntityRef> getPairedConnections() { return pairedConnections; } public ConnectedEntityRef getFirstPairedConnection() { ConnectedEntityRef pairedConnection = null; if ( ( pairedConnections != null ) && ( pairedConnections.size() > 0 ) ) { pairedConnection = pairedConnections.get( 0 ); } return pairedConnection; } public UUID getFirstPairedConnectedEntityId() { ConnectedEntityRef pairedConnection = getFirstPairedConnection(); if ( pairedConnection != null ) { return pairedConnection.getUuid(); } return null; } public String getFirstPairedConnectedEntityType() { ConnectedEntityRef pairedConnection = getFirstPairedConnection(); if ( pairedConnection != null ) { return pairedConnection.getType(); } return null; } public String getFirstPairedConnectionType() { ConnectedEntityRef pairedConnection = getFirstPairedConnection(); if ( pairedConnection != null ) { return pairedConnection.getConnectionType(); } return null; } @Override public ConnectedEntityRef getTargetRefs() { return connectedEntity; } /** * @return */ @Override public String getConnectionType() { if ( connectedEntity == null ) { return null; } return connectedEntity.getConnectionType(); } /** * @return */ public String getConnectedEntityType() { if ( connectedEntity == null ) { return null; } return connectedEntity.getType(); } /** * @return */ public UUID getConnectedEntityId() { return connectedEntity.getUuid(); } private UUID id; /** @return connection id */ @Override public UUID getUuid() { if ( id == null ) { List<ConnectedEntityRef> var = getPairedConnections(); id = getId( getSourceRefs(), getTargetRefs(), var.toArray(new ConnectedEntityRef[var.size()])); } return id; } @Override public String getType() { return CONNECTION_ENTITY_TYPE; } @Override public Id asId() { return new SimpleId(id, CONNECTION_ENTITY_TYPE ); } public UUID getIndexId() { return getIndexId( getSourceRefs(), getConnectionType(), getConnectedEntityType(), pairedConnections.toArray(new ConnectedEntityRef[pairedConnections.size()])); } public UUID getConnectingIndexId() { return getIndexId( getSourceRefs(), getConnectionType(), null, pairedConnections.toArray(new ConnectedEntityRef[pairedConnections.size()])); } public ConnectionRefImpl getConnectionToConnectionEntity() { return new ConnectionRefImpl( getSourceRefs(), new ConnectedEntityRefImpl( CONNECTION_ENTITY_CONNECTION_TYPE, CONNECTION_ENTITY_TYPE, getUuid() ) ); } /** @return index ids */ public UUID[] getIndexIds() { List<ConnectedEntityRef> var = getPairedConnections(); return getIndexIds( getSourceRefs(), getTargetRefs().getConnectionType(), getTargetRefs().getType(), var.toArray(new ConnectedEntityRef[var.size()])); } static String typeOrDefault( String type ) { if ( ( type == null ) || ( type.length() == 0 ) ) { return NULL_ENTITY_TYPE; } return type; } static UUID idOrDefault( UUID uuid ) { if ( uuid == null ) { return NULL_ID; } return uuid; } public static boolean connectionsNull( ConnectedEntityRef... pairedConnections ) { if ( ( pairedConnections == null ) || ( pairedConnections.length == 0 ) ) { return true; } for ( ConnectedEntityRef pairedConnection : pairedConnections ) { if ( pairedConnection == null || pairedConnection.getUuid() == null || pairedConnection.getUuid().equals( NULL_ID ) ) { return true; } } return false; } public static ConnectedEntityRef[] getConnections( ConnectedEntityRef... connections ) { return connections; } public static List<ConnectedEntityRef> getConnectionsList( ConnectedEntityRef... connections ) { return Arrays.asList( connections ); } /** @return connection id */ public static UUID getId( UUID connectingEntityId, String connectionType, UUID connectedEntityId ) { return getId( connectingEntityId, null, null, connectionType, connectedEntityId ); } /** * Connection id is constructed from packed structure of properties strings are truncated to 16 ascii bytes. * Connection id is now MD5'd into a UUID via UUID.nameUUIDFromBytes() so, technically string concatenation could be * used prior to MD5 * * @return connection id */ public static UUID getId( UUID connectingEntityId, String pairedConnectionType, UUID pairedConnectingEntityId, String connectionType, UUID connectedEntityId ) { EntityRef connectingEntity = ref( connectingEntityId ); ConnectedEntityRef[] pairedConnections = getConnections( new ConnectedEntityRefImpl( pairedConnectionType, null, pairedConnectingEntityId ) ); ConnectedEntityRef connectedEntity = new ConnectedEntityRefImpl( connectionType, null, connectedEntityId ); return getId( connectingEntity, connectedEntity, pairedConnections ); } public static UUID getId( EntityRef connectingEntity, ConnectedEntityRef connectedEntity, ConnectedEntityRef... pairedConnections ) { UUID uuid = null; try { if ( connectionsNull( pairedConnections ) && connectionsNull( connectedEntity ) ) { return connectingEntity.getUuid(); } ByteArrayOutputStream byteStream = new ByteArrayOutputStream( 16 + ( 32 * pairedConnections.length ) ); byteStream.write( uuidToBytesNullOk( connectingEntity.getUuid() ) ); for ( ConnectedEntityRef connection : pairedConnections ) { String connectionType = connection.getConnectionType(); UUID connectedEntityID = connection.getUuid(); byteStream.write( ascii( StringUtils.lowerCase( connectionType ) ) ); byteStream.write( uuidToBytesNullOk( connectedEntityID ) ); } String connectionType = connectedEntity.getConnectionType(); if ( connectionType == null ) { connectionType = NULL_ENTITY_TYPE; } UUID connectedEntityID = connectedEntity.getUuid(); byteStream.write( ascii( StringUtils.lowerCase( connectionType ) ) ); byteStream.write( uuidToBytesNullOk( connectedEntityID ) ); byte[] raw_id = byteStream.toByteArray(); // logger.info("raw connection index id: " + // Hex.encodeHexString(raw_id)); uuid = UUID.nameUUIDFromBytes( raw_id ); // logger.info("connection index uuid: " + uuid); } catch ( IOException e ) { logger.error( "Unable to create connection UUID", e ); } return uuid; } /** @return connection index id */ public static UUID getIndexId( UUID connectingEntityId, String connectionType, String connectedEntityType ) { return getIndexId( connectingEntityId, null, null, connectionType, connectedEntityType ); } /** @return connection index id */ public static UUID getIndexId( UUID connectingEntityId, String pairedConnectionType, UUID pairedConnectingEntityId, String connectionType, String connectedEntityType ) { EntityRef connectingEntity = ref( connectingEntityId ); ConnectedEntityRef[] pairedConnections = getConnections( new ConnectedEntityRefImpl( pairedConnectionType, null, pairedConnectingEntityId ) ); return getIndexId( connectingEntity, connectionType, connectedEntityType, pairedConnections ); } public static UUID getIndexId( EntityRef connectingEntity, String connectionType, String connectedEntityType, ConnectedEntityRef... pairedConnections ) { UUID uuid = null; try { if ( connectionsNull( pairedConnections ) && ( ( connectionType == null ) && ( connectedEntityType == null ) ) ) { return connectingEntity.getUuid(); } ByteArrayOutputStream byteStream = new ByteArrayOutputStream( 16 + ( 32 * pairedConnections.length ) ); byteStream.write( uuidToBytesNullOk( connectingEntity.getUuid() ) ); for ( ConnectedEntityRef connection : pairedConnections ) { String type = connection.getConnectionType(); UUID id = connection.getUuid(); byteStream.write( ascii( StringUtils.lowerCase( type ) ) ); byteStream.write( uuidToBytesNullOk( id ) ); } if ( connectionType == null ) { connectionType = NULL_ENTITY_TYPE; } if ( connectedEntityType == null ) { connectedEntityType = NULL_ENTITY_TYPE; } byteStream.write( ascii( StringUtils.lowerCase( connectionType ) ) ); byteStream.write( ascii( StringUtils.lowerCase( connectedEntityType ) ) ); byte[] raw_id = byteStream.toByteArray(); logger.info( "raw connection index id: {}", Hex.encodeHexString( raw_id ) ); uuid = UUID.nameUUIDFromBytes( raw_id ); logger.info( "connection index uuid: {}", uuid ); } catch ( IOException e ) { logger.error( "Unable to create connection index UUID", e ); } return uuid; } /** @return connection index id */ public static UUID getIndexId( int variant, UUID connectingEntityId, String pairedConnectionType, UUID pairedConnectingEntityId, String connectionType, String connectedEntityType ) { EntityRef connectingEntity = ref( connectingEntityId ); ConnectedEntityRef[] pairedConnections = getConnections( new ConnectedEntityRefImpl( pairedConnectionType, null, pairedConnectingEntityId ) ); return getIndexId( variant, connectingEntity, connectionType, connectedEntityType, pairedConnections ); } public static UUID getIndexId( int variant, EntityRef connectingEntity, String connectionType, String connectedEntityType, ConnectedEntityRef... pairedConnections ) { switch ( variant ) { case ALL: if ( connectionsNull( pairedConnections ) ) { return connectingEntity.getUuid(); } else { return getIndexId( connectingEntity, null, null, pairedConnections ); } case BY_ENTITY_TYPE: return getIndexId( connectingEntity, null, connectedEntityType, pairedConnections ); case BY_CONNECTION_TYPE: return getIndexId( connectingEntity, connectionType, null, pairedConnections ); case BY_CONNECTION_AND_ENTITY_TYPE: return getIndexId( connectingEntity, connectionType, connectedEntityType, pairedConnections ); } return connectingEntity.getUuid(); } /** @return index ids */ public static UUID[] getIndexIds( UUID connectingEntityId, String connectionType, String connectedEntityType ) { return getIndexIds( connectingEntityId, null, null, connectionType, connectedEntityType ); } /** @return index ids */ public static UUID[] getIndexIds( UUID connectingEntityId, String pairedConnectionType, UUID pairedConnectingEntityId, String connectionType, String connectedEntityType ) { UUID[] variants = new UUID[4]; for ( int i = 0; i < 4; i++ ) { variants[i] = getIndexId( i, connectingEntityId, pairedConnectionType, pairedConnectingEntityId, connectionType, connectedEntityType ); } return variants; } public static UUID[] getIndexIds( EntityRef connectingEntity, String connectionType, String connectedEntityType, ConnectedEntityRef... pairedConnections ) { UUID[] variants = new UUID[4]; for ( int i = 0; i < 4; i++ ) { variants[i] = getIndexId( i, connectingEntity, connectionType, connectedEntityType, pairedConnections ); } return variants; } }