[BAEL-9555] - Created a core-java-modules folder
This commit is contained in:
13
core-java-modules/core-java-sun/src/main/java/com/baeldung/.gitignore
vendored
Normal file
13
core-java-modules/core-java-sun/src/main/java/com/baeldung/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.class
|
||||
|
||||
#folders#
|
||||
/target
|
||||
/neoDb*
|
||||
/data
|
||||
/src/main/webapp/WEB-INF/classes
|
||||
*/META-INF/*
|
||||
|
||||
# Packaged files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
@@ -0,0 +1 @@
|
||||
### Relevant Articles:
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface Positive {
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import com.sun.source.util.*;
|
||||
import com.sun.tools.javac.api.BasicJavacTask;
|
||||
import com.sun.tools.javac.code.TypeTag;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.TreeMaker;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
import com.sun.tools.javac.util.Names;
|
||||
|
||||
import javax.tools.JavaCompiler;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.sun.tools.javac.util.List.nil;
|
||||
|
||||
/**
|
||||
* A {@link JavaCompiler javac} plugin which inserts {@code >= 0} checks into resulting {@code *.class} files
|
||||
* for numeric method parameters marked by {@link Positive}
|
||||
*/
|
||||
public class SampleJavacPlugin implements Plugin {
|
||||
|
||||
public static final String NAME = "MyPlugin";
|
||||
|
||||
private static Set<String> TARGET_TYPES = new HashSet<>(Arrays.asList(
|
||||
// Use only primitive types for simplicity
|
||||
byte.class.getName(), short.class.getName(), char.class.getName(),
|
||||
int.class.getName(), long.class.getName(), float.class.getName(), double.class.getName()));
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JavacTask task, String... args) {
|
||||
Context context = ((BasicJavacTask) task).getContext();
|
||||
task.addTaskListener(new TaskListener() {
|
||||
@Override
|
||||
public void started(TaskEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished(TaskEvent e) {
|
||||
if (e.getKind() != TaskEvent.Kind.PARSE) {
|
||||
return;
|
||||
}
|
||||
e.getCompilationUnit()
|
||||
.accept(new TreeScanner<Void, Void>() {
|
||||
@Override
|
||||
public Void visitMethod(MethodTree method, Void v) {
|
||||
List<VariableTree> parametersToInstrument = method.getParameters()
|
||||
.stream()
|
||||
.filter(SampleJavacPlugin.this::shouldInstrument)
|
||||
.collect(Collectors.toList());
|
||||
if (!parametersToInstrument.isEmpty()) {
|
||||
// There is a possible case that more than one argument is marked by @Positive,
|
||||
// as the checks are added to the method's body beginning, we process parameters RTL
|
||||
// to ensure correct order.
|
||||
Collections.reverse(parametersToInstrument);
|
||||
parametersToInstrument.forEach(p -> addCheck(method, p, context));
|
||||
}
|
||||
// There is a possible case that there is a nested class declared in a method's body,
|
||||
// hence, we want to proceed with method body AST as well.
|
||||
return super.visitMethod(method, v);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean shouldInstrument(VariableTree parameter) {
|
||||
return TARGET_TYPES.contains(parameter.getType().toString())
|
||||
&& parameter.getModifiers().getAnnotations()
|
||||
.stream()
|
||||
.anyMatch(a -> Positive.class.getSimpleName().equals(a.getAnnotationType().toString()));
|
||||
}
|
||||
|
||||
private void addCheck(MethodTree method, VariableTree parameter, Context context) {
|
||||
JCTree.JCIf check = createCheck(parameter, context);
|
||||
JCTree.JCBlock body = (JCTree.JCBlock) method.getBody();
|
||||
body.stats = body.stats.prepend(check);
|
||||
}
|
||||
|
||||
private static JCTree.JCIf createCheck(VariableTree parameter, Context context) {
|
||||
TreeMaker factory = TreeMaker.instance(context);
|
||||
Names symbolsTable = Names.instance(context);
|
||||
|
||||
return factory.at(((JCTree) parameter).pos)
|
||||
.If(factory.Parens(createIfCondition(factory, symbolsTable, parameter)),
|
||||
createIfBlock(factory, symbolsTable, parameter),
|
||||
null);
|
||||
}
|
||||
|
||||
private static JCTree.JCBinary createIfCondition(TreeMaker factory, Names symbolsTable, VariableTree parameter) {
|
||||
Name parameterId = symbolsTable.fromString(parameter.getName().toString());
|
||||
return factory.Binary(JCTree.Tag.LE,
|
||||
factory.Ident(parameterId),
|
||||
factory.Literal(TypeTag.INT, 0));
|
||||
}
|
||||
|
||||
private static JCTree.JCBlock createIfBlock(TreeMaker factory, Names symbolsTable, VariableTree parameter) {
|
||||
String parameterName = parameter.getName().toString();
|
||||
Name parameterId = symbolsTable.fromString(parameterName);
|
||||
|
||||
String errorMessagePrefix = String.format("Argument '%s' of type %s is marked by @%s but got '",
|
||||
parameterName, parameter.getType(), Positive.class.getSimpleName());
|
||||
String errorMessageSuffix = "' for it";
|
||||
|
||||
return factory.Block(0, com.sun.tools.javac.util.List.of(
|
||||
factory.Throw(
|
||||
factory.NewClass(null, nil(),
|
||||
factory.Ident(symbolsTable.fromString(IllegalArgumentException.class.getSimpleName())),
|
||||
com.sun.tools.javac.util.List.of(factory.Binary(JCTree.Tag.PLUS,
|
||||
factory.Binary(JCTree.Tag.PLUS, factory.Literal(TypeTag.CLASS, errorMessagePrefix),
|
||||
factory.Ident(parameterId)),
|
||||
factory.Literal(TypeTag.CLASS, errorMessageSuffix))), null))));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.baeldung.javac.SampleJavacPlugin
|
||||
@@ -0,0 +1,6 @@
|
||||
log4j.rootLogger=DEBUG, A1
|
||||
|
||||
log4j.appender.A1=org.apache.log4j.ConsoleAppender
|
||||
|
||||
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="WARN" />
|
||||
<logger name="org.springframework.transaction" level="WARN" />
|
||||
|
||||
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
|
||||
<logger name="org.springframework.web.servlet.mvc" level="WARN" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SampleJavacPluginIntegrationTest {
|
||||
|
||||
private static final String CLASS_TEMPLATE =
|
||||
"package com.baeldung.javac;\n" +
|
||||
"\n" +
|
||||
"public class Test {\n" +
|
||||
" public static %1$s service(@Positive %1$s i) {\n" +
|
||||
" return i;\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"";
|
||||
|
||||
private TestCompiler compiler = new TestCompiler();
|
||||
private TestRunner runner = new TestRunner();
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void givenInt_whenNegative_thenThrowsException() throws Throwable {
|
||||
compileAndRun(double.class,-1);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void givenInt_whenZero_thenThrowsException() throws Throwable {
|
||||
compileAndRun(int.class,0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenInt_whenPositive_thenSuccess() throws Throwable {
|
||||
assertEquals(1, compileAndRun(int.class, 1));
|
||||
}
|
||||
|
||||
private Object compileAndRun(Class<?> argumentType, Object argument) throws Throwable {
|
||||
String qualifiedClassName = "com.baeldung.javac.Test";
|
||||
byte[] byteCode = compiler.compile(qualifiedClassName, String.format(CLASS_TEMPLATE, argumentType.getName()));
|
||||
return runner.run(byteCode, qualifiedClassName, "service", new Class[] {argumentType}, argument);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
|
||||
/** Holds compiled byte code in a byte array */
|
||||
public class SimpleClassFile extends SimpleJavaFileObject {
|
||||
|
||||
private ByteArrayOutputStream out;
|
||||
|
||||
public SimpleClassFile(URI uri) {
|
||||
super(uri, Kind.CLASS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
return out = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
public byte[] getCompiledBinaries() {
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Adapts {@link SimpleClassFile} to the {@link JavaCompiler} */
|
||||
public class SimpleFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
|
||||
|
||||
private final List<SimpleClassFile> compiled = new ArrayList<>();
|
||||
|
||||
public SimpleFileManager(StandardJavaFileManager delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(Location location,
|
||||
String className,
|
||||
JavaFileObject.Kind kind,
|
||||
FileObject sibling)
|
||||
{
|
||||
SimpleClassFile result = new SimpleClassFile(URI.create("string://" + className));
|
||||
compiled.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return compiled binaries processed by the current class
|
||||
*/
|
||||
public List<SimpleClassFile> getCompiled() {
|
||||
return compiled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.net.URI;
|
||||
|
||||
/** Exposes given test source to the compiler. */
|
||||
public class SimpleSourceFile extends SimpleJavaFileObject {
|
||||
|
||||
private final String content;
|
||||
|
||||
public SimpleSourceFile(String qualifiedClassName, String testSource) {
|
||||
super(URI.create(String.format("file://%s%s",
|
||||
qualifiedClassName.replaceAll("\\.", "/"),
|
||||
Kind.SOURCE.extension)),
|
||||
Kind.SOURCE);
|
||||
content = testSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
public class TestCompiler {
|
||||
public byte[] compile(String qualifiedClassName, String testSource) {
|
||||
StringWriter output = new StringWriter();
|
||||
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
SimpleFileManager fileManager = new SimpleFileManager(compiler.getStandardFileManager(
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
List<SimpleSourceFile> compilationUnits = singletonList(new SimpleSourceFile(qualifiedClassName, testSource));
|
||||
List<String> arguments = new ArrayList<>();
|
||||
arguments.addAll(asList("-classpath", System.getProperty("java.class.path"),
|
||||
"-Xplugin:" + SampleJavacPlugin.NAME));
|
||||
JavaCompiler.CompilationTask task = compiler.getTask(output,
|
||||
fileManager,
|
||||
null,
|
||||
arguments,
|
||||
null,
|
||||
compilationUnits);
|
||||
task.call();
|
||||
return fileManager.getCompiled().iterator().next().getCompiledBinaries();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.baeldung.javac;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class TestRunner {
|
||||
|
||||
public Object run(byte[] byteCode,
|
||||
String qualifiedClassName,
|
||||
String methodName,
|
||||
Class<?>[] argumentTypes,
|
||||
Object... args)
|
||||
throws Throwable
|
||||
{
|
||||
ClassLoader classLoader = new ClassLoader() {
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
return defineClass(name, byteCode, 0, byteCode.length);
|
||||
}
|
||||
};
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = classLoader.loadClass(qualifiedClassName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Can't load compiled test class", e);
|
||||
}
|
||||
|
||||
Method method;
|
||||
try {
|
||||
method = clazz.getMethod(methodName, argumentTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Can't find the 'main()' method in the compiled test class", e);
|
||||
}
|
||||
|
||||
try {
|
||||
return method.invoke(null, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.baeldung.unsafe;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
class CASCounter {
|
||||
private final Unsafe unsafe;
|
||||
private volatile long counter = 0;
|
||||
private long offset;
|
||||
|
||||
private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {
|
||||
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (Unsafe) f.get(null);
|
||||
}
|
||||
|
||||
public CASCounter() throws Exception {
|
||||
unsafe = getUnsafe();
|
||||
offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
|
||||
}
|
||||
|
||||
public void increment() {
|
||||
long before = counter;
|
||||
while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
|
||||
before = counter;
|
||||
}
|
||||
}
|
||||
|
||||
public long getCounter() {
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.baeldung.unsafe;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
class OffHeapArray {
|
||||
private final static int BYTE = 1;
|
||||
private long size;
|
||||
private long address;
|
||||
|
||||
private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {
|
||||
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (Unsafe) f.get(null);
|
||||
}
|
||||
|
||||
OffHeapArray(long size) throws NoSuchFieldException, IllegalAccessException {
|
||||
this.size = size;
|
||||
address = getUnsafe().allocateMemory(size * BYTE);
|
||||
}
|
||||
|
||||
public void set(long i, byte value) throws NoSuchFieldException, IllegalAccessException {
|
||||
getUnsafe().putByte(address + i * BYTE, value);
|
||||
}
|
||||
|
||||
public int get(long idx) throws NoSuchFieldException, IllegalAccessException {
|
||||
return getUnsafe().getByte(address + idx * BYTE);
|
||||
}
|
||||
|
||||
public long size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
void freeMemory() throws NoSuchFieldException, IllegalAccessException {
|
||||
getUnsafe().freeMemory(address);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.baeldung.unsafe;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
|
||||
public class UnsafeUnitTest {
|
||||
|
||||
private Unsafe unsafe;
|
||||
|
||||
@Before
|
||||
public void setup() throws NoSuchFieldException, IllegalAccessException {
|
||||
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
unsafe = (Unsafe) f.get(null);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void givenClass_whenInitializeIt_thenShouldHaveDifferentStateWhenUseUnsafe() throws IllegalAccessException, InstantiationException {
|
||||
//when
|
||||
InitializationOrdering o1 = new InitializationOrdering();
|
||||
assertEquals(o1.getA(), 1);
|
||||
|
||||
//when
|
||||
InitializationOrdering o3 = (InitializationOrdering) unsafe.allocateInstance(InitializationOrdering.class);
|
||||
assertEquals(o3.getA(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPrivateMethod_whenUsingUnsafe_thenCanModifyPrivateField() throws NoSuchFieldException {
|
||||
//given
|
||||
SecretHolder secretHolder = new SecretHolder();
|
||||
|
||||
//when
|
||||
Field f = secretHolder.getClass().getDeclaredField("SECRET_VALUE");
|
||||
unsafe.putInt(secretHolder, unsafe.objectFieldOffset(f), 1);
|
||||
|
||||
//then
|
||||
assertTrue(secretHolder.secretIsDisclosed());
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void givenUnsafeThrowException_whenThrowCheckedException_thenNotNeedToCatchIt() {
|
||||
unsafe.throwException(new IOException());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // Uncomment for local
|
||||
public void givenArrayBiggerThatMaxInt_whenAllocateItOffHeapMemory_thenSuccess() throws NoSuchFieldException, IllegalAccessException {
|
||||
//given
|
||||
long SUPER_SIZE = (long) Integer.MAX_VALUE * 2;
|
||||
OffHeapArray array = new OffHeapArray(SUPER_SIZE);
|
||||
|
||||
//when
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
array.set((long) Integer.MAX_VALUE + i, (byte) 3);
|
||||
sum += array.get((long) Integer.MAX_VALUE + i);
|
||||
}
|
||||
|
||||
long arraySize = array.size();
|
||||
|
||||
array.freeMemory();
|
||||
|
||||
//then
|
||||
assertEquals(arraySize, SUPER_SIZE);
|
||||
assertEquals(sum, 300);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenUnsafeCompareAndSwap_whenUseIt_thenCounterYildCorrectLockFreeResults() throws Exception {
|
||||
//given
|
||||
int NUM_OF_THREADS = 1_000;
|
||||
int NUM_OF_INCREMENTS = 10_000;
|
||||
ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS);
|
||||
CASCounter casCounter = new CASCounter();
|
||||
|
||||
//when
|
||||
IntStream.rangeClosed(0, NUM_OF_THREADS - 1)
|
||||
.forEach(i -> service.submit(() -> IntStream
|
||||
.rangeClosed(0, NUM_OF_INCREMENTS - 1)
|
||||
.forEach(j -> casCounter.increment())));
|
||||
|
||||
service.shutdown();
|
||||
service.awaitTermination(1, TimeUnit.MINUTES);
|
||||
|
||||
//then
|
||||
assertEquals(NUM_OF_INCREMENTS * NUM_OF_THREADS, casCounter.getCounter());
|
||||
|
||||
}
|
||||
|
||||
class InitializationOrdering {
|
||||
private long a;
|
||||
|
||||
public InitializationOrdering() {
|
||||
this.a = 1;
|
||||
}
|
||||
|
||||
public long getA() {
|
||||
return this.a;
|
||||
}
|
||||
}
|
||||
|
||||
class SecretHolder {
|
||||
private int SECRET_VALUE = 0;
|
||||
|
||||
public boolean secretIsDisclosed() {
|
||||
return SECRET_VALUE == 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
13
core-java-modules/core-java-sun/src/test/resources/.gitignore
vendored
Normal file
13
core-java-modules/core-java-sun/src/test/resources/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.class
|
||||
|
||||
#folders#
|
||||
/target
|
||||
/neoDb*
|
||||
/data
|
||||
/src/main/webapp/WEB-INF/classes
|
||||
*/META-INF/*
|
||||
|
||||
# Packaged files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
Reference in New Issue
Block a user