All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.driver.mapping.MappingManager Maven / Gradle / Ivy

There is a newer version: 3.11.5
Show newest version
/*
 * Copyright (C) 2012-2017 DataStax Inc.
 *
 * 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 com.datastax.driver.mapping;

import com.datastax.driver.core.*;
import com.datastax.driver.mapping.annotations.Accessor;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.UDT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Mapping manager from which to obtain entity mappers.
 */
public class MappingManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(MappingManager.class);

    private final Session session;
    private final MappingConfiguration configuration;
    final int protocolVersionAsInt;

    private final ConcurrentHashMap, Mapper> mappers = new ConcurrentHashMap, Mapper>();
    private final ConcurrentHashMap, MappedUDTCodec> udtCodecs = new ConcurrentHashMap, MappedUDTCodec>();
    private final ConcurrentHashMap, Object> accessors = new ConcurrentHashMap, Object>();


    /**
     * Creates a new {@code MappingManager} using the provided {@code Session} with default
     * {@code MapperConfiguration}.
     * 

* Note that this constructor forces the initialization of the session (see * {@link #MappingManager(Session, ProtocolVersion)} if that is a problem for you). * * @param session the {@code Session} to use. */ public MappingManager(Session session) { this(session, getProtocolVersion(session)); } private static ProtocolVersion getProtocolVersion(Session session) { session.init(); return session.getCluster().getConfiguration().getProtocolOptions().getProtocolVersion(); } /** * Creates a new {@code MappingManager} using the provided {@code Session} with default * {@code MapperConfiguration}. *

* This constructor is only provided for backward compatibility: before 2.1.7, {@code MappingManager} could be * built from an uninitialized session; since 2.1.7, the mapper needs to know the active protocol version to * adapt its internal requests, so {@link #MappingManager(Session)} will now initialize the session if needed. * If you rely on the session not being initialized, use this constructor and provide the version manually. * * @param session the {@code Session} to use. * @param protocolVersion the protocol version that will be used with this session. * @since 2.1.7 */ public MappingManager(Session session, ProtocolVersion protocolVersion) { this(session, MappingConfiguration.builder().build(), protocolVersion); } /** * Creates a new {@code MappingManager} using the provided {@code Session} with custom configuration * to be inherited to each instantiated mapper. *

* Note that this constructor forces the initialization of the session (see * {@link #MappingManager(Session, ProtocolVersion)} if that is a problem for you). * * @param session the {@code Session} to use. * @param configuration the {@code MapperConfiguration} to use be used as default for instantiated mappers. */ public MappingManager(Session session, MappingConfiguration configuration) { this(session, configuration, getProtocolVersion(session)); } /** * Creates a new {@code MappingManager} using the provided {@code Session} with default * {@code MapperConfiguration}. *

* This constructor is only provided for backward compatibility: before 2.1.7, {@code MappingManager} could be * built from an uninitialized session; since 2.1.7, the mapper needs to know the active protocol version to * adapt its internal requests, so {@link #MappingManager(Session)} will now initialize the session if needed. * If you rely on the session not being initialized, use this constructor and provide the version manually. * * @param session the {@code Session} to use. * @param configuration the {@code MapperConfiguration} to use be used as default for instantiated mappers. * @param protocolVersion the protocol version that will be used with this session. */ public MappingManager(Session session, MappingConfiguration configuration, ProtocolVersion protocolVersion) { this.session = session; this.configuration = configuration; // This is not strictly correct in clusters with mixed C* node versions, which typically can occur when upgrading to // a major version in Cassandra that has a protocol change. // But mappers need to make a decision early so that generated queries are compatible, and we don't know in advance // which nodes might join the cluster later. // At least if protocol >=2 we know there won't be any 1.2 nodes ever. this.protocolVersionAsInt = protocolVersion.toInt(); session.getCluster().register(new SchemaChangeListenerBase() { @Override public void onTableRemoved(TableMetadata table) { synchronized (mappers) { Iterator> it = mappers.values().iterator(); while (it.hasNext()) { Mapper mapper = it.next(); if (mapper.getTableMetadata().equals(table)) { LOGGER.error("Table {} has been removed; existing mappers for @Entity annotated {} will not work anymore", table.getName(), mapper.getMappedClass()); it.remove(); } } } } @Override public void onTableChanged(TableMetadata current, TableMetadata previous) { synchronized (mappers) { Iterator> it = mappers.values().iterator(); while (it.hasNext()) { Mapper mapper = it.next(); if (mapper.getTableMetadata().equals(previous)) { LOGGER.warn("Table {} has been altered; existing mappers for @Entity annotated {} might not work properly anymore", previous.getName(), mapper.getMappedClass()); it.remove(); } } } } @Override public void onUserTypeRemoved(UserType type) { synchronized (udtCodecs) { Iterator> it = udtCodecs.values().iterator(); while (it.hasNext()) { MappedUDTCodec codec = it.next(); if (type.equals(codec.getCqlType())) { LOGGER.error("User type {} has been removed; existing mappers for @UDT annotated {} will not work anymore", type, codec.getUdtClass()); it.remove(); } } } } @Override public void onUserTypeChanged(UserType current, UserType previous) { synchronized (udtCodecs) { Set> deletedCodecs = new HashSet>(); Iterator> it = udtCodecs.values().iterator(); while (it.hasNext()) { MappedUDTCodec codec = it.next(); if (previous.equals(codec.getCqlType())) { LOGGER.warn("User type {} has been altered; existing mappers for @UDT annotated {} might not work properly anymore", previous, codec.getUdtClass()); deletedCodecs.add(codec); it.remove(); } } for (MappedUDTCodec deletedCodec : deletedCodecs) { // try to register an updated version of the previous codec try { getUDTCodec(deletedCodec.getUdtClass()); } catch (Exception e) { LOGGER.error("Could not update mapping for @UDT annotated " + deletedCodec.getUdtClass(), e); } } } } }); } /** * The underlying {@link Session} used by this manager. *

* Note that you can get obtain the {@link Cluster} object corresponding * to that session using {@code getSession().getCluster()}. *

* It is inadvisable to close the returned Session while this manager and * its mappers are in use. * * @return the underlying session used by this manager. */ public Session getSession() { return session; } /** * Returns the {@link MappingConfiguration configuration} used by this manager. * * @return the {@link MappingConfiguration configuration} used by this manager. */ public MappingConfiguration getConfiguration() { return configuration; } /** * Creates a {@code Mapper} for the provided class (that must be annotated by a * {@link Table} annotation). *

* The {@code MappingManager} only ever keeps one Mapper for each class, and so calling this * method multiple times on the same class will always return the same object. *

* If the type of any field in the class is an {@link UDT}-annotated classes, a codec for that * class will automatically be created and registered with the underlying {@code Cluster}. * This works recursively with UDTs nested in other UDTs or in collections. * * @param the type of the class to map. * @param klass the (annotated) class for which to return the mapper. * @return the {@code Mapper} object for class {@code klass}. */ public Mapper mapper(Class klass) { return getMapper(klass); } /** * Creates a {@code TypeCodec} for the provided class (that must be annotated by * a {@link UDT} annotation). *

* This method also registers the codec against the underlying {@code Cluster}. * In addition, the codecs for any nested UDTs will also be created and registered. *

* You don't need to call this method explicitly if you already call {@link #mapper(Class)} * for a class that references this UDT class (creating a mapper will automatically * process all UDTs that it uses). * * @param the type of the class to map. * @param klass the (annotated) class for which to return the codec. * @return the codec that maps the provided class to the corresponding user-defined type. */ public TypeCodec udtCodec(Class klass) { return getUDTCodec(klass); } /** * Creates an accessor object based on the provided interface (that must be annotated by * a {@link Accessor} annotation). *

* The {@code MappingManager} only ever keep one Accessor for each class, and so calling this * method multiple time on the same class will always return the same object. * * @param the type of the accessor class. * @param klass the (annotated) class for which to create an accessor object. * @return the accessor object for class {@code klass}. */ public T createAccessor(Class klass) { return getAccessor(klass); } @SuppressWarnings("unchecked") private Mapper getMapper(Class klass) { Mapper mapper = (Mapper) mappers.get(klass); if (mapper == null) { EntityMapper entityMapper = AnnotationParser.parseEntity(klass, this); mapper = new Mapper(this, klass, entityMapper); Mapper old = (Mapper) mappers.putIfAbsent(klass, mapper); if (old != null) { mapper = old; } } return mapper; } @SuppressWarnings("unchecked") TypeCodec getUDTCodec(Class mappedClass) { MappedUDTCodec codec = (MappedUDTCodec) udtCodecs.get(mappedClass); if (codec == null) { codec = AnnotationParser.parseUDT(mappedClass, this); session.getCluster().getConfiguration().getCodecRegistry().register(codec); MappedUDTCodec old = (MappedUDTCodec) udtCodecs.putIfAbsent(mappedClass, codec); if (old != null) { codec = old; } } return codec; } @SuppressWarnings("unchecked") private T getAccessor(Class klass) { T accessor = (T) accessors.get(klass); if (accessor == null) { AccessorMapper mapper = AnnotationParser.parseAccessor(klass, this); mapper.prepare(this); accessor = mapper.createProxy(); T old = (T) accessors.putIfAbsent(klass, accessor); if (old != null) { accessor = old; } } return accessor; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy