Skip to content

Commit

Permalink
Separate MapperContext into interface and implementation
Browse files Browse the repository at this point in the history
This allows us to hide the details of how contexts are mutated, which
are irrelevant to end users.

In the generated code, Mapper implementations use DefaultMapperContext,
but DAO implementations only need MapperContext because they don't
mutate it.
  • Loading branch information
olim7t committed Jun 21, 2019
1 parent 526f1ff commit 38cb3f3
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 65 deletions.
Expand Up @@ -17,7 +17,7 @@

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.mapper.MapperBuilder;
import com.datastax.oss.driver.api.mapper.MapperContext;
import com.datastax.oss.driver.internal.mapper.DefaultMapperContext;
import com.datastax.oss.driver.internal.mapper.processor.GeneratedNames;
import com.datastax.oss.driver.internal.mapper.processor.ProcessorContext;
import com.datastax.oss.driver.internal.mapper.processor.SingleFileCodeGenerator;
Expand Down Expand Up @@ -70,7 +70,7 @@ protected JavaFile.Builder getContents() {
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(ClassName.get(interfaceElement))
.addStatement("$1T context = new $1T(session)", MapperContext.class)
.addStatement("$1T context = new $1T(session)", DefaultMapperContext.class)
.addStatement(
"return new $T(context)",
GeneratedNames.mapperImplementation(interfaceElement))
Expand Down
Expand Up @@ -15,8 +15,8 @@
*/
package com.datastax.oss.driver.internal.mapper.processor.mapper;

import com.datastax.oss.driver.api.mapper.MapperContext;
import com.datastax.oss.driver.internal.mapper.DaoCacheKey;
import com.datastax.oss.driver.internal.mapper.DefaultMapperContext;
import com.datastax.oss.driver.internal.mapper.processor.GeneratedNames;
import com.datastax.oss.driver.internal.mapper.processor.MethodGenerator;
import com.datastax.oss.driver.internal.mapper.processor.ProcessorContext;
Expand Down Expand Up @@ -117,7 +117,7 @@ protected JavaFile.Builder getContents() {
MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);

GeneratedCodePatterns.addFinalFieldAndConstructorArgument(
ClassName.get(MapperContext.class), "context", classContents, constructorContents);
ClassName.get(DefaultMapperContext.class), "context", classContents, constructorContents);

// Add all the fields that were requested by DAO method generators:
for (DaoSimpleField field : daoSimpleFields) {
Expand Down
Expand Up @@ -20,67 +20,29 @@
import com.datastax.oss.driver.api.mapper.entity.naming.NameConverter;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* A runtime context that gets passed from the mapper to DAO components to share global resources
* and configuration.
*/
public class MapperContext {

private final CqlSession session;
private final CqlIdentifier keyspaceId;
private final CqlIdentifier tableId;
private final ConcurrentMap<Class<? extends NameConverter>, NameConverter> nameConverterCache;

public MapperContext(@NonNull CqlSession session) {
this(session, null, null, new ConcurrentHashMap<>());
}

private MapperContext(
CqlSession session,
CqlIdentifier keyspaceId,
CqlIdentifier tableId,
ConcurrentMap<Class<? extends NameConverter>, NameConverter> nameConverterCache) {
this.session = session;
this.keyspaceId = keyspaceId;
this.tableId = tableId;
this.nameConverterCache = nameConverterCache;
}

public MapperContext withKeyspaceAndTable(
@Nullable CqlIdentifier newKeyspaceId, @Nullable CqlIdentifier newTableId) {
return (Objects.equals(newKeyspaceId, this.keyspaceId)
&& Objects.equals(newTableId, this.tableId))
? this
: new MapperContext(session, newKeyspaceId, newTableId, nameConverterCache);
}
public interface MapperContext {

@NonNull
public CqlSession getSession() {
return session;
}
CqlSession getSession();

/**
* If this context belongs to a DAO that was built with a keyspace-parameterized mapper method,
* the value of that parameter. Otherwise null.
*/
@Nullable
public CqlIdentifier getKeyspaceId() {
return keyspaceId;
}
CqlIdentifier getKeyspaceId();

/**
* If this context belongs to a DAO that was built with a table-parameterized mapper method, the
* value of that parameter. Otherwise null.
*/
@Nullable
public CqlIdentifier getTableId() {
return tableId;
}
CqlIdentifier getTableId();

/**
* Returns an instance of the given converter class.
Expand All @@ -89,23 +51,5 @@ public CqlIdentifier getTableId() {
* exists yet for this mapper, a new instance is built by looking for a public no-arg constructor.
*/
@NonNull
public NameConverter getNameConverter(Class<? extends NameConverter> converterClass) {
return nameConverterCache.computeIfAbsent(converterClass, MapperContext::buildNameConverter);
}

private static NameConverter buildNameConverter(Class<? extends NameConverter> converterClass) {
try {
return converterClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException
| IllegalAccessException
| NoSuchMethodException
| InvocationTargetException e) {
throw new IllegalStateException(
String.format(
"Error while building an instance of %s. "
+ "%s implementations must have a public no-arg constructor",
converterClass, NameConverter.class.getSimpleName()),
e);
}
}
NameConverter getNameConverter(Class<? extends NameConverter> converterClass);
}
@@ -0,0 +1,99 @@
/*
* Copyright 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.oss.driver.internal.mapper;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.mapper.MapperContext;
import com.datastax.oss.driver.api.mapper.entity.naming.NameConverter;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class DefaultMapperContext implements MapperContext {

private final CqlSession session;
private final CqlIdentifier keyspaceId;
private final CqlIdentifier tableId;
private final ConcurrentMap<Class<? extends NameConverter>, NameConverter> nameConverterCache;

public DefaultMapperContext(@NonNull CqlSession session) {
this(session, null, null, new ConcurrentHashMap<>());
}

private DefaultMapperContext(
CqlSession session,
CqlIdentifier keyspaceId,
CqlIdentifier tableId,
ConcurrentMap<Class<? extends NameConverter>, NameConverter> nameConverterCache) {
this.session = session;
this.keyspaceId = keyspaceId;
this.tableId = tableId;
this.nameConverterCache = nameConverterCache;
}

public DefaultMapperContext withKeyspaceAndTable(
@Nullable CqlIdentifier newKeyspaceId, @Nullable CqlIdentifier newTableId) {
return (Objects.equals(newKeyspaceId, this.keyspaceId)
&& Objects.equals(newTableId, this.tableId))
? this
: new DefaultMapperContext(session, newKeyspaceId, newTableId, nameConverterCache);
}

@NonNull
@Override
public CqlSession getSession() {
return session;
}

@Nullable
@Override
public CqlIdentifier getKeyspaceId() {
return keyspaceId;
}

@Nullable
@Override
public CqlIdentifier getTableId() {
return tableId;
}

@NonNull
@Override
public NameConverter getNameConverter(Class<? extends NameConverter> converterClass) {
return nameConverterCache.computeIfAbsent(
converterClass, DefaultMapperContext::buildNameConverter);
}

private static NameConverter buildNameConverter(Class<? extends NameConverter> converterClass) {
try {
return converterClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException
| IllegalAccessException
| NoSuchMethodException
| InvocationTargetException e) {
throw new IllegalStateException(
String.format(
"Error while building an instance of %s. "
+ "%s implementations must have a public no-arg constructor",
converterClass, NameConverter.class.getSimpleName()),
e);
}
}
}

0 comments on commit 38cb3f3

Please sign in to comment.