package org.springframework.roo.addon.solr;
import static org.springframework.roo.model.JdkJavaType.CALENDAR;
import static org.springframework.roo.model.JdkJavaType.COLLECTION;
import static org.springframework.roo.model.JpaJavaType.POST_PERSIST;
import static org.springframework.roo.model.JpaJavaType.POST_UPDATE;
import static org.springframework.roo.model.JpaJavaType.PRE_REMOVE;
import static org.springframework.roo.model.SpringJavaType.ASYNC;
import static org.springframework.roo.model.SpringJavaType.AUTOWIRED;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
/**
* Metadata for {@link RooSolrSearchable}.
*
* @author Stefan Schmidt
* @since 1.1
*/
public class SolrMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {
private static final String PROVIDES_TYPE_STRING = SolrMetadata.class
.getName();
private static final String PROVIDES_TYPE = MetadataIdentificationUtils
.create(PROVIDES_TYPE_STRING);
private static final JavaType SOLR_INPUT_DOCUMENT = new JavaType(
"org.apache.solr.common.SolrInputDocument");
private static final JavaType SOLR_QUERY = new JavaType(
"org.apache.solr.client.solrj.SolrQuery");
private static final JavaType SOLR_QUERY_RESPONSE = new JavaType(
"org.apache.solr.client.solrj.response.QueryResponse");
private static final JavaType SOLR_SERVER = new JavaType(
"org.apache.solr.client.solrj.SolrServer");
public static String createIdentifier(final JavaType javaType,
final LogicalPath path) {
return PhysicalTypeIdentifierNamingUtils.createIdentifier(
PROVIDES_TYPE_STRING, javaType, path);
}
public static JavaType getJavaType(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getJavaType(
PROVIDES_TYPE_STRING, metadataIdentificationString);
}
public static String getMetadataIdentiferType() {
return PROVIDES_TYPE;
}
public static LogicalPath getPath(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
public static boolean isValid(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
private SolrSearchAnnotationValues annotationValues;
private String beanPlural;
private String javaBeanFieldName;
public SolrMetadata(final String identifier, final JavaType aspectName,
final SolrSearchAnnotationValues annotationValues,
final PhysicalTypeMetadata governorPhysicalTypeMetadata,
final MethodMetadata identifierAccessor,
final FieldMetadata versionField,
final Map<MethodMetadata, FieldMetadata> accessorDetails,
final String javaTypePlural) {
super(identifier, aspectName, governorPhysicalTypeMetadata);
Validate.notNull(annotationValues,
"Solr search annotation values required");
Validate.isTrue(isValid(identifier), "Metadata identification string '"
+ identifier + "' is invalid");
Validate.notNull(identifierAccessor,
"Persistence identifier method required");
Validate.notNull(accessorDetails, "Public accessors requred");
Validate.notBlank(javaTypePlural,
"Plural representation of java type required");
if (!isValid()) {
return;
}
javaBeanFieldName = JavaSymbolName.getReservedWordSafeName(destination)
.getSymbolName();
this.annotationValues = annotationValues;
beanPlural = javaTypePlural;
if (Modifier.isAbstract(governorTypeDetails.getModifier())) {
valid = false;
return;
}
builder.addField(getSolrServerField());
if (StringUtils.isNotBlank(annotationValues.getSimpleSearchMethod())) {
builder.addMethod(getSimpleSearchMethod());
}
if (StringUtils.isNotBlank(annotationValues.getSearchMethod())) {
builder.addMethod(getSearchMethod());
}
if (StringUtils.isNotBlank(annotationValues.getIndexMethod())) {
builder.addMethod(getIndexEntityMethod());
builder.addMethod(getIndexEntitiesMethod(accessorDetails,
identifierAccessor, versionField));
}
if (StringUtils.isNotBlank(annotationValues.getDeleteIndexMethod())) {
builder.addMethod(getDeleteIndexMethod(identifierAccessor));
}
if (StringUtils.isNotBlank(annotationValues
.getPostPersistOrUpdateMethod())) {
builder.addMethod(getPostPersistOrUpdateMethod());
}
if (StringUtils.isNotBlank(annotationValues.getPreRemoveMethod())) {
builder.addMethod(getPreRemoveMethod());
}
builder.addMethod(getSolrServerMethod());
// Create a representation of the desired output ITD
itdTypeDetails = builder.build();
}
public SolrSearchAnnotationValues getAnnotationValues() {
return annotationValues;
}
private MethodMetadataBuilder getDeleteIndexMethod(
final MethodMetadata identifierAccessor) {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getDeleteIndexMethod());
final JavaType parameterType = destination;
if (governorHasMethod(methodName, parameterType)) {
return null;
}
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName(javaBeanFieldName));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine(getSimpleName(SOLR_SERVER)
+ " solrServer = solrServer();");
bodyBuilder.appendFormalLine("try {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("solrServer.deleteById(\""
+ destination.getSimpleTypeName().toLowerCase() + "_\" + "
+ javaBeanFieldName + "."
+ identifierAccessor.getMethodName().getSymbolName() + "());");
bodyBuilder.appendFormalLine("solrServer.commit();");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("} catch (Exception e) {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("e.printStackTrace();");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
JavaType.VOID_PRIMITIVE,
AnnotatedJavaType.convertFromJavaTypes(parameterType),
parameterNames, bodyBuilder);
methodBuilder.addAnnotation(new AnnotationMetadataBuilder(ASYNC));
return methodBuilder;
}
private MethodMetadataBuilder getIndexEntitiesMethod(
final Map<MethodMetadata, FieldMetadata> accessorDetails,
final MethodMetadata identifierAccessor,
final FieldMetadata versionField) {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getIndexMethod() + beanPlural);
final JavaType parameterType = new JavaType(
COLLECTION.getFullyQualifiedTypeName(), 0, DataType.TYPE, null,
Arrays.asList(destination));
if (governorHasMethod(methodName, parameterType)) {
return null;
}
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName(beanPlural.toLowerCase()));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
final String sid = getSimpleName(SOLR_INPUT_DOCUMENT);
final List<JavaType> sidTypeParams = Arrays.asList(SOLR_INPUT_DOCUMENT);
String listVar = "documents";
if (listVar.equals(beanPlural.toLowerCase())) {
listVar += "_";
}
bodyBuilder.appendFormalLine(getSimpleName(new JavaType(List.class
.getName(), 0, DataType.TYPE, null, sidTypeParams))
+ " "
+ listVar
+ " = new "
+ getSimpleName(new JavaType(ArrayList.class.getName(), 0,
DataType.TYPE, null, sidTypeParams)) + "();");
bodyBuilder.appendFormalLine("for (" + destination.getSimpleTypeName()
+ " " + javaBeanFieldName + " : " + beanPlural.toLowerCase()
+ ") {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine(sid + " sid = new " + sid + "();");
bodyBuilder.appendFormalLine("sid.addField(\"id\", \""
+ destination.getSimpleTypeName().toLowerCase() + "_\" + "
+ javaBeanFieldName + "." + identifierAccessor.getMethodName()
+ "());");
final StringBuilder textField = new StringBuilder("new StringBuilder()");
for (final Entry<MethodMetadata, FieldMetadata> entry : accessorDetails
.entrySet()) {
final FieldMetadata field = entry.getValue();
if (versionField != null
&& field.getFieldName().equals(versionField.getFieldName())) {
continue;
}
if (field.getFieldType().isCommonCollectionType()) {
continue;
}
if (!textField.toString().endsWith("StringBuilder()")) {
textField.append(".append(\" \")");
}
final JavaSymbolName accessorMethod = entry.getKey()
.getMethodName();
if (field.getFieldType().equals(CALENDAR)) {
textField.append(".append(").append(javaBeanFieldName)
.append(".").append(accessorMethod)
.append("().getTime()").append(")");
}
else {
textField.append(".append(").append(javaBeanFieldName)
.append(".").append(accessorMethod).append("()")
.append(")");
}
String fieldName = javaBeanFieldName
+ "."
+ field.getFieldName().getSymbolName().toLowerCase()
+ SolrUtils
.getSolrDynamicFieldPostFix(field.getFieldType());
for (final AnnotationMetadata annotation : field.getAnnotations()) {
if (annotation.getAnnotationType()
.equals(new JavaType(
"org.apache.solr.client.solrj.beans.Field"))) {
final AnnotationAttributeValue<?> value = annotation
.getAttribute(new JavaSymbolName("value"));
if (value != null) {
fieldName = value.getValue().toString();
}
}
}
if (field.getFieldType().equals(CALENDAR)) {
bodyBuilder.appendFormalLine("sid.addField(\"" + fieldName
+ "\", " + javaBeanFieldName + "."
+ accessorMethod.getSymbolName() + "().getTime());");
}
else {
bodyBuilder.appendFormalLine("sid.addField(\"" + fieldName
+ "\", " + javaBeanFieldName + "."
+ accessorMethod.getSymbolName() + "());");
}
}
bodyBuilder
.appendFormalLine("// Add summary field to allow searching documents for objects of this type");
bodyBuilder.appendFormalLine("sid.addField(\""
+ destination.getSimpleTypeName().toLowerCase()
+ "_solrsummary_t\", " + textField.toString() + ");");
bodyBuilder.appendFormalLine(listVar + ".add(sid);");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
bodyBuilder.appendFormalLine("try {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine(getSimpleName(SOLR_SERVER)
+ " solrServer = solrServer();");
bodyBuilder.appendFormalLine("solrServer.add(" + listVar + ");");
bodyBuilder.appendFormalLine("solrServer.commit();");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("} catch (Exception e) {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("e.printStackTrace();");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
JavaType.VOID_PRIMITIVE,
AnnotatedJavaType.convertFromJavaTypes(parameterType),
parameterNames, bodyBuilder);
methodBuilder.addAnnotation(new AnnotationMetadataBuilder(ASYNC));
return methodBuilder;
}
private MethodMetadataBuilder getIndexEntityMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getIndexMethod()
+ destination.getSimpleTypeName());
final JavaType parameterType = destination;
if (governorHasMethod(methodName, parameterType)) {
return null;
}
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
final JavaType listType = JavaType.getInstance(List.class.getName(), 0,
DataType.TYPE, null, parameterType);
final JavaType arrayListType = JavaType.getInstance(
ArrayList.class.getName(), 0, DataType.TYPE, null,
parameterType);
bodyBuilder.appendFormalLine(getSimpleName(listType) + " "
+ beanPlural.toLowerCase() + " = new "
+ getSimpleName(arrayListType) + "();");
bodyBuilder.appendFormalLine(beanPlural.toLowerCase() + ".add("
+ javaBeanFieldName + ");");
bodyBuilder.appendFormalLine(annotationValues.getIndexMethod()
+ beanPlural + "(" + beanPlural.toLowerCase() + ");");
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName(javaBeanFieldName));
return new MethodMetadataBuilder(getId(), Modifier.PUBLIC
| Modifier.STATIC, methodName, JavaType.VOID_PRIMITIVE,
AnnotatedJavaType.convertFromJavaTypes(parameterType),
parameterNames, bodyBuilder);
}
private MethodMetadataBuilder getPostPersistOrUpdateMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getPostPersistOrUpdateMethod());
if (governorHasMethod(methodName)) {
return null;
}
final List<AnnotationMetadataBuilder> annotations = Arrays.asList(
new AnnotationMetadataBuilder(POST_UPDATE),
new AnnotationMetadataBuilder(POST_PERSIST));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine(annotationValues.getIndexMethod()
+ destination.getSimpleTypeName() + "(this);");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PRIVATE, methodName, JavaType.VOID_PRIMITIVE,
bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getPreRemoveMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getPreRemoveMethod());
if (governorHasMethod(methodName)) {
return null;
}
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(new AnnotationMetadataBuilder(PRE_REMOVE));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine(annotationValues.getDeleteIndexMethod()
+ "(this);");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PRIVATE, methodName, JavaType.VOID_PRIMITIVE,
bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getSearchMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getSearchMethod());
final JavaType parameterType = SOLR_QUERY;
if (governorHasMethod(methodName, parameterType)) {
return null;
}
final JavaType queryResponse = SOLR_QUERY_RESPONSE;
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName("query"));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("try {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("return solrServer().query(query);");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("} catch (Exception e) {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("e.printStackTrace();");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
bodyBuilder.appendFormalLine("return new "
+ getSimpleName(queryResponse) + "();");
return new MethodMetadataBuilder(getId(), Modifier.PUBLIC
| Modifier.STATIC, methodName, queryResponse,
AnnotatedJavaType.convertFromJavaTypes(parameterType),
parameterNames, bodyBuilder);
}
private String getSimpleName(final JavaType type) {
return type.getNameIncludingTypeParameters(false,
builder.getImportRegistrationResolver());
}
private MethodMetadataBuilder getSimpleSearchMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
annotationValues.getSimpleSearchMethod());
final JavaType parameterType = JavaType.STRING;
if (governorHasMethod(methodName, parameterType)) {
return null;
}
final JavaType queryResponse = SOLR_QUERY_RESPONSE;
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName("queryString"));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("String searchString = \""
+ destination.getSimpleTypeName()
+ "_solrsummary_t:\" + queryString;");
bodyBuilder
.appendFormalLine("return search(new SolrQuery(searchString.toLowerCase()));");
return new MethodMetadataBuilder(getId(), Modifier.PUBLIC
| Modifier.STATIC, methodName, queryResponse,
AnnotatedJavaType.convertFromJavaTypes(parameterType),
parameterNames, bodyBuilder);
}
private FieldMetadataBuilder getSolrServerField() {
final JavaSymbolName fieldName = new JavaSymbolName("solrServer");
if (governorTypeDetails.getDeclaredField(fieldName) != null) {
return null;
}
return new FieldMetadataBuilder(getId(), Modifier.TRANSIENT,
Arrays.asList(new AnnotationMetadataBuilder(AUTOWIRED)),
fieldName, SOLR_SERVER);
}
private MethodMetadataBuilder getSolrServerMethod() {
final JavaSymbolName methodName = new JavaSymbolName("solrServer");
if (governorHasMethod(methodName)) {
return null;
}
final JavaType returnType = SOLR_SERVER;
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine(getSimpleName(returnType)
+ " _solrServer = new " + destination.getSimpleTypeName()
+ "().solrServer;");
bodyBuilder
.appendFormalLine("if (_solrServer == null) throw new IllegalStateException(\"Solr server has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)\");");
bodyBuilder.appendFormalLine("return _solrServer;");
return new MethodMetadataBuilder(getId(), Modifier.PUBLIC
| Modifier.STATIC, methodName, returnType, bodyBuilder);
}
@Override
public String toString() {
final ToStringBuilder builder = new ToStringBuilder(this);
builder.append("identifier", getId());
builder.append("valid", valid);
builder.append("aspectName", aspectName);
builder.append("destinationType", destination);
builder.append("governor", governorPhysicalTypeMetadata.getId());
builder.append("itdTypeDetails", itdTypeDetails);
return builder.toString();
}
}