diff --git a/.gitignore b/.gitignore index 8f242ff..38a19dd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ # Mobile Tools for Java (J2ME) .mtj.tmp/ +# vim swap files +*.swp + # Package Files # *.jar !gradle-wrapper.jar diff --git a/.gradle/6.3/executionHistory/executionHistory.lock b/.gradle/6.3/executionHistory/executionHistory.lock deleted file mode 100644 index a7604be..0000000 Binary files a/.gradle/6.3/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/6.3/fileChanges/last-build.bin b/.gradle/6.3/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/6.3/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/6.3/fileHashes/fileHashes.lock b/.gradle/6.3/fileHashes/fileHashes.lock deleted file mode 100644 index 2b22044..0000000 Binary files a/.gradle/6.3/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/6.3/gc.properties b/.gradle/6.3/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/6.5/executionHistory/executionHistory.bin b/.gradle/6.5/executionHistory/executionHistory.bin deleted file mode 100644 index ca60313..0000000 Binary files a/.gradle/6.5/executionHistory/executionHistory.bin and /dev/null differ diff --git a/.gradle/6.5/executionHistory/executionHistory.lock b/.gradle/6.5/executionHistory/executionHistory.lock deleted file mode 100644 index 7ae781a..0000000 Binary files a/.gradle/6.5/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/6.5/fileChanges/last-build.bin b/.gradle/6.5/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/6.5/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/6.5/fileHashes/fileHashes.bin b/.gradle/6.5/fileHashes/fileHashes.bin deleted file mode 100644 index adb536e..0000000 Binary files a/.gradle/6.5/fileHashes/fileHashes.bin and /dev/null differ diff --git a/.gradle/6.5/fileHashes/fileHashes.lock b/.gradle/6.5/fileHashes/fileHashes.lock deleted file mode 100644 index b89035b..0000000 Binary files a/.gradle/6.5/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/6.5/gc.properties b/.gradle/6.5/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index 125640c..0000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index b774acd..0000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Thu Oct 08 23:16:29 MSK 2020 -gradle.version=6.3 diff --git a/.gradle/checksums/checksums.lock b/.gradle/checksums/checksums.lock deleted file mode 100644 index 2cf264e..0000000 Binary files a/.gradle/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index c8b5c9a..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 87ca0fc..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index fdc392f..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index b0ec460..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index c29a8be..bbb4aac 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ -# Примеры для курса "Разработчик Java" в OTUS + +Разработайте такой функционал: метод класса можно пометить самодельной аннотацией @Log, например, так: -Группа 2020-09 +class TestLogging { @Log public void calculation(int param) {}; } -### Преподаватели -Сергей Петрелевич
-Стрекалов Павел
-Александр Оруджев
-Вячеслав Лапин
-Виталий Куценко
-Дмитрий Коган +При вызове этого метода "автомагически" в консоль должны логироваться значения параметров. Например так. -Студент: -Kashapov Renat (Кашапов Ренат)
-renat.kashapov@gmail.com \ No newline at end of file +class Demo { public void action() { new TestLogging().calculation(6); } } + +В консоле дожно быть: executed method: calculation, param: 6 + +Обратите внимание: явного вызова логирования быть не должно. + +Учтите, что аннотацию можно поставить, например, на такие методы: public void calculation(int param1) public void calculation(int param1, int param2) public void calculation(int param1, int param2, String param3) diff --git a/hw05-AOPprincipes/build.gradle b/hw05-AOPprincipes/build.gradle new file mode 100644 index 0000000..d252df3 --- /dev/null +++ b/hw05-AOPprincipes/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java' + id 'application' +} + +group 'ru.otus' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +// implementation 'com.google.guava:guava' + // https://mvnrepository.com/artifact/com.google.guava/guava + compile group: 'com.google.guava', name: 'guava', version: '30.0-jre' +// https://mvnrepository.com/artifact/cglib/cglib + compile group: 'cglib', name: 'cglib', version: '3.3.0' +// https://mvnrepository.com/artifact/org.ow2.asm/asm + compile group: 'org.ow2.asm', name: 'asm', version: '9.0' +// https://mvnrepository.com/artifact/org.ow2.asm/asm-util + compile group: 'org.ow2.asm', name: 'asm-util', version: '9.0' +} + +mainClassName = 'aop.App' diff --git a/hw05-AOPprincipes/src/main/java/aop/AOPClassLoader.java b/hw05-AOPprincipes/src/main/java/aop/AOPClassLoader.java new file mode 100644 index 0000000..8592640 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/AOPClassLoader.java @@ -0,0 +1,43 @@ +package aop; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import java.io.IOException; +import java.util.*; + +import static org.objectweb.asm.Opcodes.ASM9; + +public class AOPClassLoader extends ClassLoader { + protected Map loggedMethods = new HashMap<>(); + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class resultClass = null; + if (!name.startsWith("java.")) { + try { + ClassReader cr = new ClassReader(name); + ClassWriter cw = new ClassWriter(0); + ClassWriter cw1 = new ClassWriter(0); + byte[] b2; + AOPLoggedMethodsNamesClassVisitor aopLoggedMethodsNamesClassVisitor = new AOPLoggedMethodsNamesClassVisitor(ASM9, cw, loggedMethods); + cr.accept(aopLoggedMethodsNamesClassVisitor, 0); + b2 = cw.toByteArray(); + ClassReader c2 = new ClassReader(b2); + AOPLoggedMethodsCodeClassVisitor aopLoggedMethodsCodeClassVisitor = new AOPLoggedMethodsCodeClassVisitor(ASM9, cw1, loggedMethods); + c2.accept(aopLoggedMethodsCodeClassVisitor, 0); + b2 = cw1.toByteArray(); + resultClass = defineClass(name, b2, 0, b2.length); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + resultClass = super.loadClass(name, resolve); + } + return resultClass; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } +} \ No newline at end of file diff --git a/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsCodeClassVisitor.java b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsCodeClassVisitor.java new file mode 100644 index 0000000..36fcf53 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsCodeClassVisitor.java @@ -0,0 +1,25 @@ +package aop; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import java.util.Map; +import static org.objectweb.asm.Opcodes.ASM9; + +public class AOPLoggedMethodsCodeClassVisitor extends ClassVisitor { + + protected Map loggedMethods; + + public AOPLoggedMethodsCodeClassVisitor(int asmVersion, ClassVisitor classVisitor, Map loggedMethods) { + super(asmVersion, classVisitor); + this.loggedMethods = loggedMethods; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor superMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); + if (loggedMethods.get(name + descriptor) != null) { + return new AOPLoggedMethodsCodeMethodVisitor(ASM9, superMethodVisitor, loggedMethods.get(name + descriptor)); + } + return superMethodVisitor; + } +} diff --git a/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsCodeMethodVisitor.java b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsCodeMethodVisitor.java new file mode 100644 index 0000000..e6fc758 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsCodeMethodVisitor.java @@ -0,0 +1,38 @@ +package aop; + +import org.objectweb.asm.MethodVisitor; +import static org.objectweb.asm.Opcodes.*; +import java.util.Map; + +public class AOPLoggedMethodsCodeMethodVisitor extends MethodVisitor { + + protected SomeMethodAttributes someMethodAttributes; + + public AOPLoggedMethodsCodeMethodVisitor(int api, MethodVisitor methodVisitor, SomeMethodAttributes someMethodAttributes) { + super(api, methodVisitor); + this.someMethodAttributes = someMethodAttributes; + } + + @Override + public void visitCode() { + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("Method: \"" + someMethodAttributes.getName() + "\""); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + for (Map.Entry parameter : someMethodAttributes.getMethodVariablesHashMap().entrySet()) { + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("Argument \"" + parameter.getKey() + "\": "); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V", false); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(parameter.getValue().getType().getOpcode(ILOAD), parameter.getValue().getIndex()); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(" + transformToPrintlnArg(parameter.getValue().getType().getDescriptor()) + ")V", false); + } + } + + private String transformToPrintlnArg(String arg){ + if(arg.matches("[BSI]{1}")){ + return "I"; + } + return arg; + } +} diff --git a/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsNamesClassVisitor.java b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsNamesClassVisitor.java new file mode 100644 index 0000000..f2bc445 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsNamesClassVisitor.java @@ -0,0 +1,24 @@ +package aop; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; + +import java.util.Map; +import static org.objectweb.asm.Opcodes.*; + +public class AOPLoggedMethodsNamesClassVisitor extends ClassVisitor { + + protected Map loggedMethods; + + public AOPLoggedMethodsNamesClassVisitor(int asmVersion, ClassVisitor classVisitor, Map loggedMethods) { + super(asmVersion, classVisitor); + this.loggedMethods = loggedMethods; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor aopLoggedMethodsNamesMethodVisitor = new AOPLoggedMethodsNamesMethodVisitor(ASM9, super.visitMethod(access, name, descriptor, signature, exceptions), loggedMethods, new SomeMethodAttributes(descriptor, access, name, signature, exceptions, new MethodVariablesHashMap())); + return aopLoggedMethodsNamesMethodVisitor; + } + +} diff --git a/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsNamesMethodVisitor.java b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsNamesMethodVisitor.java new file mode 100644 index 0000000..90f6126 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/AOPLoggedMethodsNamesMethodVisitor.java @@ -0,0 +1,73 @@ +package aop; + +import org.objectweb.asm.*; +import java.util.Map; + +public class AOPLoggedMethodsNamesMethodVisitor extends MethodVisitor { + protected boolean isLogPresent; + protected SomeMethodAttributes someMethodAttributes; + protected Map loggedMethods; + + public AOPLoggedMethodsNamesMethodVisitor(int api, MethodVisitor methodVisitor, Map loggedMethods, SomeMethodAttributes someMethodAttributes) { + super(api, methodVisitor); + this.loggedMethods = loggedMethods; + this.someMethodAttributes = someMethodAttributes; + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + isLogPresent = Type.getDescriptor(Log.class).equals(descriptor); + if(isLogPresent){ + loggedMethods.put(someMethodAttributes.getName() + someMethodAttributes.getDescription(), someMethodAttributes); + } + return super.visitAnnotation(descriptor, visible); + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + mv.visitMaxs(maxStack + 3, maxLocals + 4); + } + + @Override + public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { + String methodParameters = someMethodAttributes.getDescription().replaceFirst(".*\\(","").replaceFirst("\\).*", ""); + if (isLogPresent & index > 0) { + someMethodAttributes.getMethodVariablesHashMap().put(name,new TypeIndex(index, parseParameters(methodParameters, index))); + } + super.visitLocalVariable(name, descriptor, signature, start, end, index); + } + + private Type parseParameters(String parameters, int index) { + if (index == 1) { + if (parameters.startsWith("L")) { + return Type.getType(parameters.substring(0,parameters.indexOf(';')+1)); + } else { + return Type.getType(parameters.substring(0, 1)); + } + } else { + if (parameters.startsWith("L")){ + return parseParameters(parameters.substring(parameters.indexOf(';')+1),index-1); + } else { + return parseParameters(parameters.substring(1),index-1); + } + } + } + + final protected class TypeIndex { + private int index; + private Type type; + + TypeIndex(int index, Type type) { + this.index = index; + this.type = type; + } + + protected int getIndex() { + return index; + } + + protected Type getType() { + return type; + } + } +} diff --git a/hw05-AOPprincipes/src/main/java/aop/App.java b/hw05-AOPprincipes/src/main/java/aop/App.java new file mode 100644 index 0000000..8dfb753 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/App.java @@ -0,0 +1,27 @@ +package aop; + +import java.lang.reflect.Method; + +public class App { + static public void main(String ... args) { + var classLoaderClass = App.class.getClassLoader().getClass(); + if( !classLoaderClass.getName().equals(AOPClassLoader.class.getName())) { //иначе сравнивать классы невозможно, так как это различные объекты + AOPClassLoader aopClassLoader = new AOPClassLoader(); + try { + Class loadClass = aopClassLoader.loadClass("aop.App", false); + Method method = loadClass.getMethod("main", new Class[]{String[].class}); + method.invoke(null, new Object[]{args}); + } catch (Exception e) { + e.printStackTrace(); + } + } else{// классы с AOP писать в этом блоке. + try { + new UsefulImpl().sayHelloTo("bb"); + new UsefulImpl().sayHelloTo("cc", (short) 23); + } + catch (Exception e){ + e.printStackTrace(); + } + } + } +} diff --git a/hw05-AOPprincipes/src/main/java/aop/Log.java b/hw05-AOPprincipes/src/main/java/aop/Log.java new file mode 100644 index 0000000..691d2fe --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/Log.java @@ -0,0 +1,8 @@ +package aop; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Log { +} diff --git a/hw05-AOPprincipes/src/main/java/aop/MethodVariablesHashMap.java b/hw05-AOPprincipes/src/main/java/aop/MethodVariablesHashMap.java new file mode 100644 index 0000000..f5a8622 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/MethodVariablesHashMap.java @@ -0,0 +1,15 @@ +package aop; + +import java.util.HashMap; + +public class MethodVariablesHashMap extends HashMap { + @Override + public AOPLoggedMethodsNamesMethodVisitor.TypeIndex put(String name, AOPLoggedMethodsNamesMethodVisitor.TypeIndex typeIndex) { + for (AOPLoggedMethodsNamesMethodVisitor.TypeIndex existingTypeIndex : this.values()) { + if (typeIndex.getIndex() == existingTypeIndex.getIndex()) { + throw new IllegalArgumentException(); + } + } + return super.put(name, typeIndex); + } +} diff --git a/hw05-AOPprincipes/src/main/java/aop/SomeMethodAttributes.java b/hw05-AOPprincipes/src/main/java/aop/SomeMethodAttributes.java new file mode 100644 index 0000000..1d74930 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/SomeMethodAttributes.java @@ -0,0 +1,44 @@ +package aop; + +public class SomeMethodAttributes { + private String description; + private int access; + private String name; + private String signature; + private String[] exceptions; + private MethodVariablesHashMap methodVariablesHashMap; + + SomeMethodAttributes(String description, int access, String name, String signature, String[] exceptions, + MethodVariablesHashMap methodVariablesHashMap) { + this.description = description; + this.access = access; + this.name = name; + this.signature = signature; + this.exceptions = exceptions; + this.methodVariablesHashMap = methodVariablesHashMap; + } + + public String getDescription() { + return this.description; + } + + public int getAccess() { + return this.access; + } + + public String getName() { + return this.name; + } + + public String getSignature() { + return this.signature; + } + + public String[] getExceptions() { + return this.exceptions; + } + + public MethodVariablesHashMap getMethodVariablesHashMap(){ + return this.methodVariablesHashMap; + } +} diff --git a/hw05-AOPprincipes/src/main/java/aop/Useful.java b/hw05-AOPprincipes/src/main/java/aop/Useful.java new file mode 100644 index 0000000..88debc4 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/Useful.java @@ -0,0 +1,8 @@ +package aop; + +public interface Useful { + public void sayHelloTo(); + public void sayHelloTo(String name); + public void sayHelloTo(String name, double howMany); +} + diff --git a/hw05-AOPprincipes/src/main/java/aop/UsefulImpl.java b/hw05-AOPprincipes/src/main/java/aop/UsefulImpl.java new file mode 100644 index 0000000..5c17e91 --- /dev/null +++ b/hw05-AOPprincipes/src/main/java/aop/UsefulImpl.java @@ -0,0 +1,24 @@ +package aop; + +public class UsefulImpl implements Useful { + public UsefulImpl() { + } + + @Override + public void sayHelloTo() { +// return "Hello!"; + } + + @Log + @Override + public void sayHelloTo(String name) { +// return "Hello, " + name +"!"; + } + + @Log + @Override + public void sayHelloTo(String name, double howMany) { +// return "Hello, " + name + "! " + howMany + " times."; + } + +} diff --git a/settings.gradle b/settings.gradle index 9a32c0b..5b43c31 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ rootProject.name = 'otus_hw' +include 'hw05-AOPprincipes' diff --git a/src/main/java/A.java b/src/main/java/A.java deleted file mode 100644 index 6be5562..0000000 --- a/src/main/java/A.java +++ /dev/null @@ -1,5 +0,0 @@ -public class A { - public static void main(String ... args){ - System.out.println("Hello, gradle + git!"); - } -}