[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [eclipselink-users] programmatic customization of mapping meta-data at runtime
|
Hi,
I am also trying to achieve the same for long but with no success.
Say for example there is an entity deifined like the following
public class Issue implements Serializable{
@Id
@GeneratedValue
private int id;
@SuppressWarnings("unused")
@Version
private int version;
private String comments;
private String condition;
private int copies = 1;
private int issueNum;
private double pricePaid;
@Column(name="TITLE_ID")
private int titleId;
private BigDecimal value;
On this entity I want to add a new field “Priority” ( free field)
I tried using DescriptorCustomizer and SessionCustomizer callbacks to
customize entity: -
I create free fields like
String klassName = klass.getSimpleName();
InstantClassLoader icl = new
// Add new field to the class Issue
InstantClassLoader(klassName,"priority");
icl.setBasePackageNameToStartScan("org.eclipse.persistence.example.jpa.coherence.comics.model.annotated");
Object retObj = null;
try {
retObj = icl.loadClass(klassName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return retObj;
My InstantClassLoader is based on ASM framework and adding fields like
public Class<?> addFieldToClass(String className, String fieldName) {
Resource[] rs = null;
try {
rs = DiscoverUtils.resolveResource(className);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (rs.length == 0) {
String pkg = DiscoverUtils.discoverClass(className.split("\\.")[0],
basePackageNameToStartScan);
System.err.println(pkg);
String resPath = ClassUtils.convertClassNameToResourcePath(pkg);
System.err.println(resPath);
return addFieldToClass(resPath + ".class", fieldName);
}
Assert.isTrue(rs.length == 1, "More one " + className + " exists");
InputStream is = null;
try {
is = rs[0].getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
Assert.notNull(is, "Not able create input stream for resource "
+ className);
byte[] b;
String klassName = className.split("\\.")[0];
Assert.hasLength(klassName, "Wrong class name " + className);
// adapts the class on the fly
try {
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
CustomClassAdapter cca = new CustomClassAdapter(cw);
ClassVisitor cv = new ClassAdapter(cca);
cca.visitCode(fieldName, klassName);
cr.accept(cv, 0);
b = cw.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
// TODO remove : just for debugging
try {
FileOutputStream fos = new FileOutputStream(rs[0].getURI()
.getPath()
+ ".adapted");
fos.write(b);
fos.close();
FileOutputStream fos1 = new FileOutputStream(rs[0].getURI()
.getPath()
+ ".adapted1");
fos1.write(is.read(new byte[99999]));
fos1.close();
} catch (Exception e) {
e.printStackTrace();
}
Class<?> klass = defineClass(ClassUtils
.convertResourcePathToClassName(klassName), b, 0, b.length);
return klass;
}
public static class CustomClassAdapter extends ClassAdapter implements
Opcodes {
private String owner;
private ClassVisitor cv;
public CustomClassAdapter(ClassVisitor cv) {
super(cv);
this.cv = cv;
}
public void visitCode(String fieldName, String klassName) {
String desc = "Ljava/lang/String;";
Type t = Type.getType(desc);
int size = t.getSize();
String fDesc = "Ljava/lang/String;";
FieldVisitor fv = cv.visitField(Opcodes.ACC_PRIVATE, fieldName,
fDesc, null, null);
fv.visitEnd();
// generates getter method
String gDesc = "()" + desc;
MethodVisitor gv = cv.visitMethod(Opcodes.ACC_PUBLIC, "get"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1, fieldName.length()), gDesc, null,
null);
gv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
"Ljava/io/PrintStream;");
gv.visitLdcInsn("get" + fieldName + " called");
gv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V");
gv.visitVarInsn(Opcodes.ALOAD, 0);
gv.visitFieldInsn(Opcodes.GETFIELD, klassName, fieldName, desc);
gv.visitInsn(t.getOpcode(Opcodes.IRETURN));
gv.visitMaxs(size + 1, size + 1);
gv.visitEnd();
// generates setter method
String sDesc = "(" + desc + ")V";
MethodVisitor sv = cv.visitMethod(Opcodes.ACC_PUBLIC, "set"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1, fieldName.length()), sDesc, null,
null);
sv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
"Ljava/io/PrintStream;");
sv.visitLdcInsn("set" + fieldName + " called");
sv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V");
sv.visitVarInsn(Opcodes.ALOAD, 0);
sv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), 1);
sv.visitFieldInsn(Opcodes.PUTFIELD, klassName, fieldName, desc);
sv.visitInsn(Opcodes.RETURN);
sv.visitMaxs(1 + size, 1 + size);
sv.visitEnd();
}
@Override
public AnnotationVisitor visitAnnotation(String param, boolean visible) {
return super.visitAnnotation(param, visible);
}
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
FieldVisitor fv = super.visitField(access, name, desc, signature, value);
CustomFieldVisitor cfv = new CustomFieldVisitor(fv);
return cfv ;
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature,
final String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
exceptions);
return new MethodAdapter(mv) {
public AnnotationVisitor visitParameterAnnotation(
final int parameter, final String desc,
final boolean visible) {
AnnotationVisitor av;
av = mv.visitParameterAnnotation(parameter, desc, visible);
return av;
}
};
}
}
public static class CustomFieldVisitor implements FieldVisitor {
private FieldVisitor fv;
public CustomFieldVisitor(FieldVisitor fv) {
this.fv = fv;
}
@Override
public AnnotationVisitor visitAnnotation(String dec, boolean visible) {
return fv.visitAnnotation(dec, visible);
}
@Override
public void visitAttribute(Attribute attr) {
fv.visitAttribute(attr);
}
@Override
public void visitEnd() {
fv.visitEnd();
}
}
}
Olaf Otto wrote:
>
> Hi Tom,
>
> I am aware of the Descriptor customizers, but as far as i know these are
> invoked after the pre-deployment.
> Currently, the classes modified by the framework cause the meta-data
> processing to fail during processORMetadata as they contain
> non-serializable fields that cannot be persisted (some of them reference
> custom non-entity types that cannot be serialized).
>
> Thanks and regards,
> Olaf
>
> Tom Ware wrote:
>> Hi Olaf,
>>
>> There is a model built by our meta data processing that can be
>> changed. In essence, we build a bunch of instances of a class called
>> ClassDescriptor. These ClassDescriptors hold all the mappings we
>> use. We have a mechanism called a DescriptorCustomizer that provides
>> a callback that will allow you to make changes to any of your
>> descriptors. Here is some documentation to get you started.
>>
>> http://wiki.eclipse.org/Configuring_a_Descriptor_(ELUG)#Configuring_a_Descriptor_Customizer_Class
>>
>>
>> Although, DescriptorCustomizers are invoked after our meta data
>> processing has executed, I believe you should be able to use them.
>> The idea would be that you allow metadata processing to complete and
>> then customize the descriptors as you see fit. Is there any
>> particular reason why you would like to see the customization
>> mechanism fire before the metadata is processed?
>>
>>
>> -Tom
>>
>> Olaf Otto wrote:
>>> Hi Tom,
>>>
>>> Thank you for your reply.
>>>
>>> Tom Ware wrote:
>>>> Hi Olaf,
>>>>
>>>> What kinds of things do you want to do in your meta-data
>>>> modification?
>>> I think it will mostly be modifying the mapping type of certain
>>> fields (for example changing it from basic to N:N or a custom
>>> mapping), excluding fields etc.
>>>>
>>>> Depending on what you want to do, there may be several ways to
>>>> make modifications to the metadata.
>>>>
>>>> 1. There is a Customization mechanism that allows you to customize
>>>> the mappings that are created by our meta-data processing
>>> Very interesting - which specific mechanism are you referring to?
>>>> 2. There is an event mechanism the provides you with callbacks at
>>>> certain stages of our initialization
>>> I have seen the event mechanism - but will this actually work before
>>> the meta data of the entities has been processed?
>>>> 3. You can specify your mappings in XML. (with the option of having
>>>> the XML be the only source of mappings)
>>>>
>>> I know, but this is not an option, since this would require custom
>>> mappings for framework users affecting fields they cannot see.
>>> Furthermore, the fields are subject to a lot of changes when altering
>>> classes. Basically, i must identify the generated fields by a certain
>>> prefix and handle them with regard to their content.
>>>> You should also be aware that some performance features in
>>>> EclipseLink are enabled by byte-code weaving.
>>>>
>>> Indeed, i have seen that using the eclipse agent has a lot of
>>> advantages. But there are a lot of complications with java agents and
>>> the framework i don't wish to address by now.
>>>> -Tom
>>>>
>>> Regards,
>>> Olaf
>>>
>>>> Olaf Otto wrote:
>>>>> Hello eclipse users!
>>>>>
>>>>> I have a framework that makes massive use of aspect-oriented
>>>>> programming and code generation. This includes a significant amount
>>>>> of additional fields being created in various classes (of a project
>>>>> using the framework, not in the framework itself) by a java agent
>>>>> at class loading time. These fields are primarily generated for
>>>>> internal framework purposes and are thus invisible to a framework
>>>>> user.
>>>>>
>>>>> Most of these fields are references to non-serializable resp.
>>>>> custom types and must be handled in a non-standard fashion for
>>>>> persistence.
>>>>>
>>>>> Now here is the question: Is there a way to hook into the meta-data
>>>>> processing occurring during predeployment? More precisely, i would
>>>>> like to access and modify the persistence meta-data of entities
>>>>> before processor.processORMMetadata() is invoked in the
>>>>> processORMetadata of the PersistenceUnitProcessor class.
>>>>>
>>>>> Thank you and regards,
>>>>> Olaf
>>>>>
>>>>> _______________________________________________
>>>>> eclipselink-users mailing list
>>>>> eclipselink-users@xxxxxxxxxxx
>>>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>> _______________________________________________
>>>> eclipselink-users mailing list
>>>> eclipselink-users@xxxxxxxxxxx
>>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>>
>>> _______________________________________________
>>> eclipselink-users mailing list
>>> eclipselink-users@xxxxxxxxxxx
>>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>> _______________________________________________
>> eclipselink-users mailing list
>> eclipselink-users@xxxxxxxxxxx
>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>
> _______________________________________________
> eclipselink-users mailing list
> eclipselink-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>
>
--
View this message in context: http://www.nabble.com/programmatic-customization-of-mapping-meta-data-at-runtime-tp20505808p20521046.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.