This is an automated email from the ASF dual-hosted git repository. struberg pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/openjpa.git
commit eabceb69f43f28e2ac2698bc40e1044dd3f80331 Author: Mark Struberg <strub...@apache.org> AuthorDate: Mon Jul 17 23:01:16 2023 +0200 OPENJPA-2911 inline ASM adapter handling --- .../org/apache/openjpa/enhance/AsmAdaptor.java | 79 ------- .../openjpa/enhance/PCClassFileTransformer.java | 45 +++- .../org/apache/openjpa/enhance/asm/AsmSpi.java | 42 ---- .../org/apache/openjpa/enhance/asm/AsmSpi9.java | 185 ---------------- .../org/apache/openjpa/util/asm/AsmHelper.java | 27 +++ .../org/apache/openjpa/enhance/TestAsmAdaptor.java | 243 --------------------- 6 files changed, 71 insertions(+), 550 deletions(-) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java deleted file mode 100644 index 86d28181b..000000000 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.openjpa.enhance; - -import org.apache.openjpa.enhance.asm.AsmSpi; -import org.apache.openjpa.enhance.asm.AsmSpi9; -import serp.bytecode.BCClass; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ServiceLoader; -import java.util.stream.StreamSupport; - - -/** - * Use ASM to add required StackMapTable attribute to the byte code generated by - * Serp. - */ -public final class AsmAdaptor { - private final static AsmSpi impl; - static { - impl = StreamSupport.stream(ServiceLoader.load(AsmSpi.class).spliterator(), false).min((a, b) -> { - final int v1; - try { - v1 = Integer.parseInt(a.getClass().getName().replace("AsmSpi", "")); - } catch (final Exception e) { - // not matching our default naming so an user impl so let's use it - return -1; - } - final int v2; - try { - v2 = Integer.parseInt(b.getClass().getName().replace("AsmSpi", "")); - } catch (final Exception e) { - // not matching our default naming so an user impl so let's use it - return 1; - } - return v2 - v1; // reverse since we want the higher - }).orElseGet(AsmSpi9::new); - } - - @SuppressWarnings("deprecation") - public static void write(BCClass bc) throws IOException { - impl.write(bc); - } - - public static void write(BCClass bc, File outFile) throws IOException { - impl.write(bc, outFile); - } - - public static void write(BCClass bc, OutputStream os) throws IOException { - impl.write(bc, os); - } - - public static byte[] toByteArray(BCClass bc, byte[] returnBytes) throws IOException { - return impl.toByteArray(bc, returnBytes); - } - - public static boolean isEnhanced(final byte[] b) - { - return impl.isEnhanced(b); - } -} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java index 246083dff..f46663f31 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java @@ -34,8 +34,12 @@ import org.apache.openjpa.meta.MetaDataRepository; import org.apache.openjpa.util.GeneralException; import org.apache.openjpa.util.asm.AsmHelper; import org.apache.openjpa.util.asm.ClassNodeTracker; +import org.apache.xbean.asm9.ClassReader; +import org.apache.xbean.asm9.ClassVisitor; +import org.apache.xbean.asm9.Opcodes; import serp.bytecode.Project; +import static java.util.Arrays.asList; /** @@ -221,7 +225,46 @@ public class PCClassFileTransformer * {@link PersistenceCapable}. */ private static boolean isEnhanced(byte[] b) { - return AsmAdaptor.isEnhanced(b); + if (b == null) + { + return false; + } + final ClassReader cr = new ClassReader(b); + try + { + cr.accept(new ClassVisitor(Opcodes.ASM9) + { + @Override + public void visit(final int i, final int i1, + final String name, final String s, + final String parent, final String[] interfaces) + { + boolean enhanced = interfaces != null && interfaces.length > 0 && + asList(interfaces).contains("org/apache/openjpa/enhance/PersistenceCapable"); + if (!enhanced && name != null && parent != null && + !"java/lang/Object".equals(parent) && !name.equals(parent)) { + enhanced = isEnhanced(AsmHelper.getClassBytes(parent)); + } + throw new EnhancedStatusException(enhanced); + } + }, 0); + return false; + } catch (final EnhancedStatusException e) { + return e.status; + } catch (final Exception e) { + return false; + } + } + + + private static class EnhancedStatusException extends RuntimeException { + + private static final long serialVersionUID = 1L; + private final boolean status; + + private EnhancedStatusException(final boolean status) { + this.status = status; + } } public static class Reentrant extends PCClassFileTransformer { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java deleted file mode 100644 index 26af22ee0..000000000 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.openjpa.enhance.asm; - -import serp.bytecode.BCClass; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Enables to abstract ASM usage for the runtime (not for tests). - * - * It is recommended to name the impl "AsmSpi{asmVersion}" to let the spi loader sort the impl properly. - */ -public interface AsmSpi { - void write(BCClass bc) throws IOException; - - void write(BCClass bc, File outFile) throws IOException; - - void write(BCClass bc, OutputStream os) throws IOException; - - byte[] toByteArray(BCClass bc, byte[] returnBytes) throws IOException; - - boolean isEnhanced(byte[] b); -} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java deleted file mode 100644 index a3e887b27..000000000 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.openjpa.enhance.asm; - -import org.apache.xbean.asm9.ClassReader; -import org.apache.xbean.asm9.ClassVisitor; -import org.apache.xbean.asm9.ClassWriter; -import org.apache.xbean.asm9.Opcodes; -import serp.bytecode.BCClass; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URLDecoder; - -import static java.util.Arrays.asList; - -public class AsmSpi9 implements AsmSpi { - private static final int Java7_MajorVersion = 51; - - @SuppressWarnings("deprecation") - @Override - public void write(BCClass bc) throws IOException { - if (bc.getMajorVersion() < Java7_MajorVersion) { - bc.write(); - } else { - String name = bc.getName(); - int dotIndex = name.lastIndexOf('.') + 1; - name = name.substring(dotIndex); - Class<?> type = bc.getType(); - - OutputStream out = new FileOutputStream( - URLDecoder.decode(type.getResource(name + ".class").getFile())); - try { - writeJava7(bc, out); - } finally { - out.flush(); - out.close(); - } - } - } - - @Override - public void write(BCClass bc, File outFile) throws IOException { - if (bc.getMajorVersion() < Java7_MajorVersion) { - bc.write(outFile); - } else { - OutputStream out = new FileOutputStream(outFile); - try { - writeJava7(bc, out); - } finally { - out.flush(); - out.close(); - } - } - } - - @Override - public void write(BCClass bc, OutputStream os) throws IOException { - if (bc.getMajorVersion() < Java7_MajorVersion) { - bc.write(os); - } - else { - try { - writeJava7(bc, os); - } finally { - os.flush(); - os.close(); - } - } - } - - @Override - public byte[] toByteArray(BCClass bc, byte[] returnBytes) throws IOException { - if (bc.getMajorVersion() >= Java7_MajorVersion) { - returnBytes = toJava7ByteArray(bc, returnBytes); - } - return returnBytes; - } - - private void writeJava7(BCClass bc, OutputStream out) throws IOException { - byte[] java7Bytes = toJava7ByteArray(bc, bc.toByteArray()); - out.write(java7Bytes); - } - - private byte[] toJava7ByteArray(BCClass bc, byte[] classBytes) throws IOException { - ByteArrayInputStream bais = new ByteArrayInputStream(classBytes); - BufferedInputStream bis = new BufferedInputStream(bais); - - ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, bc.getClassLoader()); - ClassReader cr = new ClassReader(bis); - cr.accept(cw, 0); - return cw.toByteArray(); - } - - public boolean isEnhanced(final byte[] b) - { - if (b == null) - { - return false; - } - final ClassReader cr = new ClassReader(b); - try - { - cr.accept(new ClassVisitor(Opcodes.ASM8) - { - @Override - public void visit(final int i, final int i1, - final String name, final String s, - final String parent, final String[] interfaces) - { - boolean enhanced = interfaces != null && interfaces.length > 0 && - asList(interfaces).contains("org/apache/openjpa/enhance/PersistenceCapable"); - if (!enhanced && name != null && parent != null && - !"java/lang/Object".equals(parent) && !name.equals(parent)) { - enhanced = isEnhanced(bytes(parent)); - } - throw new EnhancedStatusException(enhanced); - } - }, 0); - return false; - } catch (final EnhancedStatusException e) { - return e.status; - } catch (final Exception e) { - return false; - } - } - - private byte[] bytes(final String type) - { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); - final InputStream stream = Thread.currentThread().getContextClassLoader() - .getResourceAsStream(type + ".class"); - if (stream == null) { - return null; - } - try { - int c; - byte[] buffer = new byte[1024]; - while ((c = stream.read(buffer)) >= 0) { - baos.write(buffer, 0, c); - } - } catch (IOException e) { - return null; - } finally { - try { - stream.close(); - } catch (IOException e) { - // no-op - } - } - return baos.toByteArray(); - } - - private static class EnhancedStatusException extends RuntimeException { - - private static final long serialVersionUID = 1L; - private final boolean status; - - private EnhancedStatusException(final boolean status) { - this.status = status; - } - } -} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java index d86bb6b85..86e48a868 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java @@ -17,6 +17,7 @@ package org.apache.openjpa.util.asm; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Array; @@ -110,6 +111,32 @@ public final class AsmHelper { return cwt; } + + public static byte[] getClassBytes(final String typeName) + { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); + final InputStream stream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(typeName + ".class"); + if (stream == null) { + return null; + } + try { + int c; + byte[] buffer = new byte[1024]; + while ((c = stream.read(buffer)) >= 0) { + baos.write(buffer, 0, c); + } + } catch (IOException e) { + return null; + } finally { + try { + stream.close(); + } catch (IOException e) { + // no-op + } + } + return baos.toByteArray(); + } /** * Create a byte[] of that class represented by the ClassNodeTracker */ diff --git a/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java b/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java deleted file mode 100644 index ebe8be60b..000000000 --- a/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.openjpa.enhance; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.apache.openjpa.util.QueryException; -import org.apache.openjpa.util.asm.AsmHelper; -import org.apache.xbean.asm9.tree.ClassNode; -import org.junit.Test; - -public class TestAsmAdaptor -{ - @Test - public void testAsmHelper() throws Exception { - final ClassNode classNode = AsmHelper.readClassNode(QueryException.class); - assertNotNull(classNode); - } - @Test - public void isEnhanced() - { - assertTrue(AsmAdaptor.isEnhanced(bytes(Enhanced.class))); - assertTrue(AsmAdaptor.isEnhanced(bytes(TransitivelyEnhanced.class))); - assertFalse(AsmAdaptor.isEnhanced(bytes(NotEnhanced.class))); - } - - private byte[] bytes(final Class<?> type) - { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); - try (InputStream stream = Thread.currentThread().getContextClassLoader() - .getResourceAsStream(type.getName().replace('.', '/') + ".class")) { - int c; - byte[] buffer = new byte[1024]; - while ((c = stream.read(buffer)) >= 0) { - baos.write(buffer, 0, c); - } - } - catch (IOException e) { - fail(e.getMessage()); - } - // no-op - return baos.toByteArray(); - } - - public static class NotEnhanced - { - } - - public static class TransitivelyEnhanced extends Enhanced - { - } - - public static class Enhanced implements PersistenceCapable // just a mock for the test - { - @Override - public int pcGetEnhancementContractVersion() - { - return 0; - } - - @Override - public Object pcGetGenericContext() - { - return null; - } - - @Override - public StateManager pcGetStateManager() - { - return null; - } - - @Override - public void pcReplaceStateManager(StateManager sm) - { - - } - - @Override - public void pcProvideField(int fieldIndex) - { - - } - - @Override - public void pcProvideFields(int[] fieldIndices) - { - - } - - @Override - public void pcReplaceField(int fieldIndex) - { - - } - - @Override - public void pcReplaceFields(int[] fieldIndex) - { - - } - - @Override - public void pcCopyFields(Object fromObject, int[] fields) - { - - } - - @Override - public void pcDirty(String fieldName) - { - - } - - @Override - public Object pcFetchObjectId() - { - return null; - } - - @Override - public Object pcGetVersion() - { - return null; - } - - @Override - public boolean pcIsDirty() - { - return false; - } - - @Override - public boolean pcIsTransactional() - { - return false; - } - - @Override - public boolean pcIsPersistent() - { - return false; - } - - @Override - public boolean pcIsNew() - { - return false; - } - - @Override - public boolean pcIsDeleted() - { - return false; - } - - @Override - public Boolean pcIsDetached() - { - return null; - } - - @Override - public PersistenceCapable pcNewInstance(StateManager sm, boolean clear) - { - return null; - } - - @Override - public PersistenceCapable pcNewInstance(StateManager sm, Object obj, boolean clear) - { - return null; - } - - @Override - public Object pcNewObjectIdInstance() - { - return null; - } - - @Override - public Object pcNewObjectIdInstance(Object obj) - { - return null; - } - - @Override - public void pcCopyKeyFieldsToObjectId(Object obj) - { - - } - - @Override - public void pcCopyKeyFieldsToObjectId(FieldSupplier supplier, Object obj) - { - - } - - @Override - public void pcCopyKeyFieldsFromObjectId(FieldConsumer consumer, Object obj) - { - - } - - @Override - public Object pcGetDetachedState() - { - return null; - } - - @Override - public void pcSetDetachedState(Object state) - { - - } - } -} - - -