Skip to content

Instantly share code, notes, and snippets.

@amirmv2006
Created June 20, 2021 08:04
Show Gist options
  • Save amirmv2006/96b44ad700f022b6a35637a4df6bc6c4 to your computer and use it in GitHub Desktop.
Save amirmv2006/96b44ad700f022b6a35637a4df6bc6c4 to your computer and use it in GitHub Desktop.
Firestore Documents Component Processor
/*
* Copyright 2021 the original author or authors.
*
* 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
*
* https://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 ir.amv.enterprise.locorepo.common.gcp.adapter;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.nativex.domain.reflect.FieldDescriptor;
import org.springframework.nativex.hint.AccessBits;
import org.springframework.nativex.type.AccessDescriptor;
import org.springframework.nativex.type.ComponentProcessor;
import org.springframework.nativex.type.Field;
import org.springframework.nativex.type.NativeContext;
import org.springframework.nativex.type.Type;
import org.springframework.nativex.type.TypeProcessor;
/**
* Inspired by org.springframework.data.JpaComponentProcessor
*/
public class FirestoreEntityProcessor implements ComponentProcessor {
private static Log logger = LogFactory.getLog(FirestoreEntityProcessor.class);
private final TypeProcessor typeProcessor = new TypeProcessor(
(type, context) -> {
if (type.isPartOfDomain("sun.") || type.isPartOfDomain("jdk.")) {
return false;
}
return true;
},
this::registerTypeInConfiguration,
this::registerAnnotationInConfiguration
).named("FirestoreEntityProcessor");
@Override
public boolean handle(NativeContext imageContext, String componentType,
List<String> classifiers) {
logger.info("checking whether type is firestore entity: " + componentType + ", classifiers: " + classifiers);
return classifiers.contains("com.google.cloud.spring.data.firestore.Document")
|| classifiers.contains("javax.amir.spring.SpringIndexed");
}
@Override
public void process(NativeContext imageContext, String componentType, List<String> classifiers) {
Type domainType = imageContext.getTypeSystem().resolveName(componentType);
typeProcessor.use(imageContext).toProcessType(domainType);
}
private void registerAnnotationInConfiguration(Type annotation, NativeContext context) {
logger.info(String.format(
"FirestoreEntityProcessor: adding reflection configuration AccessBits.ANNOTATION for annotation %s.",
annotation.getDottedName()));
context.addReflectiveAccess(annotation.getDottedName(), AccessBits.ANNOTATION);
}
private void registerTypeInConfiguration(Type type, NativeContext context) {
AccessDescriptor accessDescriptor = new AccessDescriptor(AccessBits.FULL_REFLECTION,
Collections.emptyList(), fieldDescriptorsForType(type, context));
logger.info(String.format("FirestoreEntityProcessor: adding reflection configuration type %s - %s",
type.getDottedName(), accessDescriptor));
context.addReflectiveAccess(type.getDottedName(), accessDescriptor);
}
private List<FieldDescriptor> fieldDescriptorsForType(Type type, NativeContext context) {
if (type.isPartOfDomain("java.")) { // other well known domains ?
logger.info(String.format("FirestoreEntityProcessor: skipping field inspection for type %s.",
type.getDottedName()));
return Collections.emptyList();
}
return type.getFields()
.stream()
.filter(Field::isFinal)
.map(field -> {
logger.info(String.format(
"FirestoreEntityProcessor: detected final field %s for type %s. Setting allowWrite=true.",
field.getName(), type.getDottedName()));
return new FieldDescriptor(field.getName(), true, true);
})
.collect(Collectors.toList());
}
}
@amirmv2006
Copy link
Author

this class will be detected using SPI meaning the FQN name of the class should be put into a file named org.springframework.nativex.type.ComponentProcessor in resources/META-INF/services.
The module containing this code should be on the classpath of spring aot build. for example, when using maven:

<plugin>
  <groupId>org.springframework.experimental</groupId>
  <artifactId>spring-aot-maven-plugin</artifactId>
  <version>${spring-native.version}</version>
  <dependencies>
    <dependency>
      <groupId>ir.amv.enterprise.laas.webapp.libs</groupId>
      <artifactId>gcp-adapter</artifactId>
      <version>${project.version}</version>
    </dependency>
  </dependencies>
  <executions>...</executions>
</plugin>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment