Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
3319b28b9c
|
@ -216,6 +216,13 @@
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
<version>4.3.4.RELEASE</version>
|
<version>4.3.4.RELEASE</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun</groupId>
|
||||||
|
<artifactId>tools</artifactId>
|
||||||
|
<version>1.8.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${java.home}/../lib/tools.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -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,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,141 @@
|
||||||
|
package com.baeldung.string.formatter;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class StringFormatterExampleTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenFormatSpecifierForCalendar_thenGotExpected() {
|
||||||
|
//Syntax of Format Specifiers for Date/Time Representation
|
||||||
|
Calendar c = new GregorianCalendar(2017, 11, 10);
|
||||||
|
String s = String.format("The date is: %1$tm %1$te,%1$tY", c);
|
||||||
|
|
||||||
|
assertEquals("The date is: 12 10,2017", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenGeneralConversion_thenConvertedString() {
|
||||||
|
//General Conversions
|
||||||
|
String s = String.format("The correct answer is %s", false);
|
||||||
|
assertEquals("The correct answer is false", s);
|
||||||
|
|
||||||
|
s = String.format("The correct answer is %b", null);
|
||||||
|
assertEquals("The correct answer is false", s);
|
||||||
|
|
||||||
|
s = String.format("The correct answer is %B", true);
|
||||||
|
assertEquals("The correct answer is TRUE", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenCharConversion_thenConvertedString() {
|
||||||
|
//Character Conversions
|
||||||
|
String s = String.format("The correct answer is %c", 'a');
|
||||||
|
assertEquals("The correct answer is a", s);
|
||||||
|
|
||||||
|
s = String.format("The correct answer is %c", null);
|
||||||
|
assertEquals("The correct answer is null", s);
|
||||||
|
|
||||||
|
s = String.format("The correct answer is %C", 'b');
|
||||||
|
assertEquals("The correct answer is B", s);
|
||||||
|
|
||||||
|
s = String.format("The valid unicode character: %c", 0x0400);
|
||||||
|
assertTrue(Character.isValidCodePoint(0x0400));
|
||||||
|
assertEquals("The valid unicode character: Ѐ", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = java.util.IllegalFormatCodePointException.class)
|
||||||
|
public void givenString_whenIllegalCodePointForConversion_thenError() {
|
||||||
|
String s = String.format("The valid unicode character: %c", 0x11FFFF);
|
||||||
|
assertFalse(Character.isValidCodePoint(0x11FFFF));
|
||||||
|
assertEquals("The valid unicode character: Ā", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenNumericIntegralConversion_thenConvertedString() {
|
||||||
|
//Numeric Integral Conversions
|
||||||
|
String s = String.format("The number 25 in decimal = %d", 25);
|
||||||
|
assertEquals("The number 25 in decimal = 25", s);
|
||||||
|
|
||||||
|
s = String.format("The number 25 in octal = %o", 25);
|
||||||
|
assertEquals("The number 25 in octal = 31", s);
|
||||||
|
|
||||||
|
s = String.format("The number 25 in hexadecimal = %x", 25);
|
||||||
|
assertEquals("The number 25 in hexadecimal = 19", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenNumericFloatingConversion_thenConvertedString() {
|
||||||
|
//Numeric Floating-point Conversions
|
||||||
|
String s = String.format("The computerized scientific format of 10000.00 "
|
||||||
|
+ "= %e", 10000.00);
|
||||||
|
assertEquals("The computerized scientific format of 10000.00 = 1.000000e+04", s);
|
||||||
|
|
||||||
|
s = String.format("The decimal format of 10.019 = %f", 10.019);
|
||||||
|
assertEquals("The decimal format of 10.019 = 10.019000", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenLineSeparatorConversion_thenConvertedString() {
|
||||||
|
//Line Separator Conversion
|
||||||
|
String s = String.format("First Line %nSecond Line");
|
||||||
|
assertEquals("First Line \n"
|
||||||
|
+ "Second Line", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenSpecifyFlag_thenGotFormattedString() {
|
||||||
|
//Without left-justified flag
|
||||||
|
String s = String.format("Without left justified flag: %5d", 25);
|
||||||
|
assertEquals("Without left justified flag: 25", s);
|
||||||
|
|
||||||
|
//Using left-justified flag
|
||||||
|
s = String.format("With left justified flag: %-5d", 25);
|
||||||
|
assertEquals("With left justified flag: 25 ", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenSpecifyPrecision_thenGotExpected() {
|
||||||
|
|
||||||
|
//Precision
|
||||||
|
String s = String.format("Output of 25.09878 with Precision 2: %.2f", 25.09878);
|
||||||
|
assertEquals("Output of 25.09878 with Precision 2: 25.10", s);
|
||||||
|
|
||||||
|
s = String.format("Output of general conversion type with Precision 2: %.2b", true);
|
||||||
|
assertEquals("Output of general conversion type with Precision 2: tr", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenSpecifyArgumentIndex_thenGotExpected() {
|
||||||
|
Calendar c = new GregorianCalendar(2017, 11, 10);
|
||||||
|
//Argument_Index
|
||||||
|
String s = String.format("The date is: %1$tm %1$te,%1$tY", c);
|
||||||
|
assertEquals("The date is: 12 10,2017", s);
|
||||||
|
|
||||||
|
s = String.format("The date is: %1$tm %<te,%<tY", c);
|
||||||
|
assertEquals("The date is: 12 10,2017", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAppendable_whenCreateFormatter_thenFormatterWorksOnAppendable() {
|
||||||
|
//Using String Formatter with Appendable
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Formatter formatter = new Formatter(sb);
|
||||||
|
formatter.format("I am writting to a %1$s Instance.", sb.getClass());
|
||||||
|
|
||||||
|
assertEquals("I am writting to a class java.lang.StringBuilder Instance.", sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenNoArguments_thenExpected() {
|
||||||
|
//Using String Formatter without arguments
|
||||||
|
String s = String.format("John scored 90%% in Fall semester");
|
||||||
|
assertEquals("John scored 90% in Fall semester", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,32 +16,29 @@ import wildfly.beans.UserBeanLocal;
|
||||||
* Servlet implementation class TestEJBServlet
|
* Servlet implementation class TestEJBServlet
|
||||||
*/
|
*/
|
||||||
public class TestEJBServlet extends HttpServlet {
|
public class TestEJBServlet extends HttpServlet {
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@EJB
|
@EJB
|
||||||
private UserBeanLocal userBean;
|
private UserBeanLocal userBean;
|
||||||
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
throws ServletException, IOException {
|
List<User> users = userBean.getUsers();
|
||||||
List<User> users = userBean.getUsers();
|
|
||||||
|
|
||||||
PrintWriter out = response.getWriter();
|
PrintWriter out = response.getWriter();
|
||||||
|
|
||||||
out.println("<html>");
|
out.println("<html>");
|
||||||
out.println("<head><title>Users</title></head>");
|
out.println("<head><title>Users</title></head>");
|
||||||
out.println("<body>");
|
out.println("<body>");
|
||||||
out.println("<center><h1>List of users:</h1>");
|
out.println("<center><h1>List of users:</h1>");
|
||||||
out.println("<table border=\"1\" align=\"center\" style=\"width:50%\">");
|
out.println("<table border=\"1\" align=\"center\" style=\"width:50%\">");
|
||||||
for (User user : users) {
|
for (User user : users) {
|
||||||
out.println("<tr>");
|
out.println("<tr>");
|
||||||
out.print("<td>" + user.getUsername() + "</td>");
|
out.print("<td>" + user.getUsername() + "</td>");
|
||||||
out.print("<td>" + user.getEmail() + "</td>");
|
out.print("<td>" + user.getEmail() + "</td>");
|
||||||
out.println("</tr>");
|
out.println("</tr>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
throws ServletException, IOException {
|
doGet(request, response);
|
||||||
doGet(request, response);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
## Relevant articles:
|
## Relevant articles:
|
||||||
|
|
||||||
- [Dynamic Mapping with Hibernate](http://www.baeldung.com/hibernate-dynamic-mapping)
|
- [Dynamic Mapping with Hibernate](http://www.baeldung.com/hibernate-dynamic-mapping)
|
||||||
|
- [An Overview of Identifiers in Hibernate](http://www.baeldung.com/hibernate-identifiers)
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<!-- com.baeldung/osgi-intro-sample-activator/1.0-SNAPSHOT -->
|
||||||
|
<parent>
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<!-- Please, note this is not the usual 'jar'. -->
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<artifactId>osgi-intro-sample-activator</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${project.version}</Bundle-Version>
|
||||||
|
|
||||||
|
<!-- Qualified name of the class that exposes the activator iface. -->
|
||||||
|
<Bundle-Activator>com.baeldung.osgi.sample.activator.HelloWorld</Bundle-Activator>
|
||||||
|
|
||||||
|
<!-- One important thing to note:
|
||||||
|
since you are not exporting the package "com.baeldung.osgi.sample.activator",
|
||||||
|
you should at least add it to the Private-Package instruction.
|
||||||
|
Otherwise, the classes inside the package will not be copied to your bundle,
|
||||||
|
as the default value of this instruction is empty. -->
|
||||||
|
|
||||||
|
<Private-Package>com.baeldung.osgi.sample.activator</Private-Package>
|
||||||
|
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.osgi.sample.activator;
|
||||||
|
|
||||||
|
import org.osgi.framework.BundleActivator;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
|
public class HelloWorld implements BundleActivator {
|
||||||
|
|
||||||
|
public void start(BundleContext ctx) {
|
||||||
|
System.out.println("Hello World.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(BundleContext bundleContext) {
|
||||||
|
System.out.println("Goodbye World.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<!-- mvn:com.baeldung/osgi-intro-sample-client/1.0-SNAPSHOT -->
|
||||||
|
<artifactId>osgi-intro-sample-client</artifactId>
|
||||||
|
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>osgi-intro-sample-service</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${project.version}</Bundle-Version>
|
||||||
|
<Bundle-Activator>com.baeldung.osgi.sample.client.Client</Bundle-Activator>
|
||||||
|
|
||||||
|
<Private-Package>com.baeldung.osgi.sample.client</Private-Package>
|
||||||
|
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.baeldung.osgi.sample.client;
|
||||||
|
|
||||||
|
import com.baeldung.osgi.sample.service.definition.Greeter;
|
||||||
|
import org.osgi.framework.*;
|
||||||
|
|
||||||
|
public class Client implements BundleActivator, ServiceListener {
|
||||||
|
|
||||||
|
private BundleContext ctx;
|
||||||
|
private ServiceReference serviceReference;
|
||||||
|
|
||||||
|
public void start(BundleContext ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
try {
|
||||||
|
ctx.addServiceListener(this, "(objectclass=" + Greeter.class.getName() + ")");
|
||||||
|
} catch (InvalidSyntaxException ise) {
|
||||||
|
ise.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(BundleContext bundleContext) {
|
||||||
|
if (serviceReference != null) {
|
||||||
|
ctx.ungetService(serviceReference);
|
||||||
|
}
|
||||||
|
this.ctx = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serviceChanged(ServiceEvent serviceEvent) {
|
||||||
|
int type = serviceEvent.getType();
|
||||||
|
switch (type) {
|
||||||
|
case (ServiceEvent.REGISTERED):
|
||||||
|
System.out.println("Notification of service registered.");
|
||||||
|
serviceReference = serviceEvent.getServiceReference();
|
||||||
|
Greeter service = (Greeter) (ctx.getService(serviceReference));
|
||||||
|
System.out.println(service.sayHiTo("John"));
|
||||||
|
break;
|
||||||
|
case (ServiceEvent.UNREGISTERING):
|
||||||
|
System.out.println("Notification of service unregistered.");
|
||||||
|
ctx.ungetService(serviceEvent.getServiceReference());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<!-- mvn:com.baeldung/osgi-intro-sample-service/1.0-SNAPSHOT -->
|
||||||
|
<parent>
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>osgi-intro-sample-service</artifactId>
|
||||||
|
|
||||||
|
<!-- Please, note this is not the usual 'jar'. -->
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${project.version}</Bundle-Version>
|
||||||
|
<Bundle-Activator>com.baeldung.osgi.sample.service.implementation.GreeterImpl</Bundle-Activator>
|
||||||
|
<Private-Package>com.baeldung.osgi.sample.service.implementation</Private-Package>
|
||||||
|
<Export-Package>com.baeldung.osgi.sample.service.definition</Export-Package>
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.osgi.sample.service.definition;
|
||||||
|
|
||||||
|
public interface Greeter {
|
||||||
|
|
||||||
|
public String sayHiTo(String name);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.osgi.sample.service.implementation;
|
||||||
|
|
||||||
|
import com.baeldung.osgi.sample.service.definition.Greeter;
|
||||||
|
import org.osgi.framework.BundleActivator;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
import org.osgi.framework.ServiceReference;
|
||||||
|
import org.osgi.framework.ServiceRegistration;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
public class GreeterImpl implements Greeter, BundleActivator {
|
||||||
|
|
||||||
|
private ServiceReference<Greeter> reference;
|
||||||
|
private ServiceRegistration<Greeter> registration;
|
||||||
|
|
||||||
|
@Override public String sayHiTo(String name) {
|
||||||
|
return "Hello " + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void start(BundleContext context) throws Exception {
|
||||||
|
System.out.println("Registering service.");
|
||||||
|
registration = context.registerService(Greeter.class, new GreeterImpl(), new Hashtable<String, String>());
|
||||||
|
reference = registration.getReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void stop(BundleContext context) throws Exception {
|
||||||
|
System.out.println("Unregistering service.");
|
||||||
|
registration.unregister();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<modules>
|
||||||
|
<module>osgi-intro-sample-activator</module>
|
||||||
|
<module>osgi-intro-sample-service</module>
|
||||||
|
<module>osgi-intro-sample-client</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../..</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-client</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-service</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-gxyz</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-mapquest</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>3.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.json</groupId>
|
||||||
|
<artifactId>javax.json-api</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish</groupId>
|
||||||
|
<artifactId>javax.json</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
<version>5.0.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<version>1.4.0</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,91 @@
|
||||||
|
OSGi
|
||||||
|
====
|
||||||
|
|
||||||
|
Info
|
||||||
|
---
|
||||||
|
|
||||||
|
com.baeldung.osgi
|
||||||
|
com.baeldung.osgi.sample.activator
|
||||||
|
|
||||||
|
Apache Felix
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
### Start
|
||||||
|
|
||||||
|
Download Apache Felix Framework Distribution
|
||||||
|
from <https://felix.apache.org/downloads.cgi>
|
||||||
|
org.apache.felix.main.distribution-5.6.8
|
||||||
|
|
||||||
|
No! The Apache Karaf container is best.
|
||||||
|
Download it from: <https://karaf.apache.org/download.html>
|
||||||
|
|
||||||
|
Download a binary distribution and unzip wherever you prefer.
|
||||||
|
|
||||||
|
Then run
|
||||||
|
|
||||||
|
bin\karaf.bat start
|
||||||
|
|
||||||
|
|
||||||
|
Unzip, pay attention to the files not being clipped(!).
|
||||||
|
|
||||||
|
system:exit
|
||||||
|
|
||||||
|
exit!
|
||||||
|
|
||||||
|
shutdown -h
|
||||||
|
|
||||||
|
or `^D`
|
||||||
|
|
||||||
|
### clean start
|
||||||
|
|
||||||
|
full clean, remove "data directory "
|
||||||
|
|
||||||
|
or...
|
||||||
|
|
||||||
|
bin\karaf.bat clean
|
||||||
|
|
||||||
|
bin\start.bat clean
|
||||||
|
|
||||||
|
### run mode
|
||||||
|
|
||||||
|
can be launched in
|
||||||
|
|
||||||
|
- the "regular" mode starts Apache Karaf in foreground, including the shell console.
|
||||||
|
- the "server" mode starts Apache Karaf in foreground, without the shell console.
|
||||||
|
- the "background" mode starts Apache Karaf in background.
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
https://karaf.apache.org/manual/latest/#_log
|
||||||
|
|
||||||
|
can be logged to console
|
||||||
|
|
||||||
|
|
||||||
|
### Bundle deploy
|
||||||
|
|
||||||
|
bundle:install mvn:com.baeldung/osgi-intro-sample-activator/1.0-SNAPSHOT
|
||||||
|
|
||||||
|
install mvn:com.baeldung/osgi-intro-sample-service/1.0-SNAPSHOT
|
||||||
|
install mvn:com.baeldung/osgi-intro-sample-client/1.0-SNAPSHOT
|
||||||
|
|
||||||
|
Eclipse's Equinox
|
||||||
|
====
|
||||||
|
|
||||||
|
Eclipse's OSGi platform
|
||||||
|
http://www.eclipse.org/equinox/
|
||||||
|
|
||||||
|
http://www.eclipse.org/equinox/documents/quickstart-framework.php
|
||||||
|
|
||||||
|
click on "download"
|
||||||
|
|
||||||
|
Latest Release
|
||||||
|
Oxygen.1 Wed, 6 Sep 2017 -- 17:00 (-0400)
|
||||||
|
|
||||||
|
org.eclipse.osgi_3.12.1.v20170821-1548.jar
|
||||||
|
|
||||||
|
= = NOT GOOD = =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<!-- com.baeldung/osgi-intro-sample-activator/1.0-SNAPSHOT -->
|
||||||
|
<parent>
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<!-- Please, note this is not the usual 'jar'. -->
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<artifactId>osgi-intro-sample-activator</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${project.version}</Bundle-Version>
|
||||||
|
|
||||||
|
<!-- Qualified name of the class that exposes the activator iface. -->
|
||||||
|
<Bundle-Activator>com.baeldung.osgi.sample.activator.HelloWorld</Bundle-Activator>
|
||||||
|
|
||||||
|
<!-- One important thing to note:
|
||||||
|
since you are not exporting the package "com.baeldung.osgi.sample.activator",
|
||||||
|
you should at least add it to the Private-Package instruction.
|
||||||
|
Otherwise, the classes inside the package will not be copied to your bundle,
|
||||||
|
as the default value of this instruction is empty. -->
|
||||||
|
|
||||||
|
<Private-Package>com.baeldung.osgi.sample.activator</Private-Package>
|
||||||
|
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.osgi.sample.activator;
|
||||||
|
|
||||||
|
import org.osgi.framework.BundleActivator;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
|
public class HelloWorld implements BundleActivator {
|
||||||
|
|
||||||
|
public void start(BundleContext ctx) {
|
||||||
|
System.out.println("Hello World.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(BundleContext bundleContext) {
|
||||||
|
System.out.println("Goodbye World.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<!-- mvn:com.baeldung/osgi-intro-sample-client/1.0-SNAPSHOT -->
|
||||||
|
<artifactId>osgi-intro-sample-client</artifactId>
|
||||||
|
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>osgi-intro-sample-service</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${project.version}</Bundle-Version>
|
||||||
|
<Bundle-Activator>com.baeldung.osgi.sample.client.Client</Bundle-Activator>
|
||||||
|
|
||||||
|
<Private-Package>com.baeldung.osgi.sample.client</Private-Package>
|
||||||
|
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.baeldung.osgi.sample.client;
|
||||||
|
|
||||||
|
import com.baeldung.osgi.sample.service.definition.Greeter;
|
||||||
|
import org.osgi.framework.*;
|
||||||
|
|
||||||
|
public class Client implements BundleActivator, ServiceListener {
|
||||||
|
|
||||||
|
private BundleContext ctx;
|
||||||
|
private ServiceReference serviceReference;
|
||||||
|
|
||||||
|
public void start(BundleContext ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
try {
|
||||||
|
ctx.addServiceListener(this, "(objectclass=" + Greeter.class.getName() + ")");
|
||||||
|
} catch (InvalidSyntaxException ise) {
|
||||||
|
ise.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(BundleContext bundleContext) {
|
||||||
|
if (serviceReference != null) {
|
||||||
|
ctx.ungetService(serviceReference);
|
||||||
|
}
|
||||||
|
this.ctx = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serviceChanged(ServiceEvent serviceEvent) {
|
||||||
|
int type = serviceEvent.getType();
|
||||||
|
switch (type) {
|
||||||
|
case (ServiceEvent.REGISTERED):
|
||||||
|
System.out.println("Notification of service registered.");
|
||||||
|
serviceReference = serviceEvent.getServiceReference();
|
||||||
|
Greeter service = (Greeter) (ctx.getService(serviceReference));
|
||||||
|
System.out.println(service.sayHiTo("John"));
|
||||||
|
break;
|
||||||
|
case (ServiceEvent.UNREGISTERING):
|
||||||
|
System.out.println("Notification of service unregistered.");
|
||||||
|
ctx.ungetService(serviceEvent.getServiceReference());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<!-- mvn:com.baeldung/osgi-intro-sample-service/1.0-SNAPSHOT -->
|
||||||
|
<parent>
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>osgi-intro-sample-service</artifactId>
|
||||||
|
|
||||||
|
<!-- Please, note this is not the usual 'jar'. -->
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${project.version}</Bundle-Version>
|
||||||
|
<Bundle-Activator>com.baeldung.osgi.sample.service.implementation.GreeterImpl</Bundle-Activator>
|
||||||
|
<Private-Package>com.baeldung.osgi.sample.service.implementation</Private-Package>
|
||||||
|
<Export-Package>com.baeldung.osgi.sample.service.definition</Export-Package>
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.osgi.sample.service.definition;
|
||||||
|
|
||||||
|
public interface Greeter {
|
||||||
|
|
||||||
|
public String sayHiTo(String name);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.osgi.sample.service.implementation;
|
||||||
|
|
||||||
|
import com.baeldung.osgi.sample.service.definition.Greeter;
|
||||||
|
import org.osgi.framework.BundleActivator;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
import org.osgi.framework.ServiceReference;
|
||||||
|
import org.osgi.framework.ServiceRegistration;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
public class GreeterImpl implements Greeter, BundleActivator {
|
||||||
|
|
||||||
|
private ServiceReference<Greeter> reference;
|
||||||
|
private ServiceRegistration<Greeter> registration;
|
||||||
|
|
||||||
|
@Override public String sayHiTo(String name) {
|
||||||
|
return "Hello " + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void start(BundleContext context) throws Exception {
|
||||||
|
System.out.println("Registering service.");
|
||||||
|
registration = context.registerService(Greeter.class, new GreeterImpl(), new Hashtable<String, String>());
|
||||||
|
reference = registration.getReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void stop(BundleContext context) throws Exception {
|
||||||
|
System.out.println("Unregistering service.");
|
||||||
|
registration.unregister();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>osgi-intro</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<modules>
|
||||||
|
<module>osgi-intro-sample-activator</module>
|
||||||
|
<module>osgi-intro-sample-service</module>
|
||||||
|
<module>osgi-intro-sample-client</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>..</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-client</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-service</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-gxyz</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>osgi-intro-mapquest</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>3.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.json</groupId>
|
||||||
|
<artifactId>javax.json-api</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish</groupId>
|
||||||
|
<artifactId>javax.json</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
<version>5.0.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<version>1.4.0</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
24
pom.xml
24
pom.xml
|
@ -62,10 +62,10 @@
|
||||||
<module>feign</module>
|
<module>feign</module>
|
||||||
<module>flyway</module>
|
<module>flyway</module>
|
||||||
|
|
||||||
<!-- <module>gatling</module> --> <!-- not meant to run as part of the standard build -->
|
<!-- <module>testing-modules/gatling</module> --> <!-- not meant to run as part of the standard build -->
|
||||||
|
|
||||||
<module>geotools</module>
|
<module>geotools</module>
|
||||||
<module>groovy-spock</module>
|
<module>testing-modules/groovy-spock</module>
|
||||||
<module>gson</module>
|
<module>gson</module>
|
||||||
<module>guava</module>
|
<module>guava</module>
|
||||||
<module>guava18</module>
|
<module>guava18</module>
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
<module>json-path</module>
|
<module>json-path</module>
|
||||||
<module>json</module>
|
<module>json</module>
|
||||||
<module>jsoup</module>
|
<module>jsoup</module>
|
||||||
<module>junit5</module>
|
<module>testing-modules/junit-5</module>
|
||||||
<module>jws</module>
|
<module>jws</module>
|
||||||
|
|
||||||
<module>libraries</module>
|
<module>libraries</module>
|
||||||
|
@ -112,9 +112,9 @@
|
||||||
<module>mapstruct</module>
|
<module>mapstruct</module>
|
||||||
<module>metrics</module>
|
<module>metrics</module>
|
||||||
<module>mesos-marathon</module>
|
<module>mesos-marathon</module>
|
||||||
<module>mockito</module>
|
<module>testing-modules/mockito</module>
|
||||||
<module>mockito2</module>
|
<module>testing-modules/mockito-2</module>
|
||||||
<module>mocks</module>
|
<module>testing-modules/mocks</module>
|
||||||
<module>mustache</module>
|
<module>mustache</module>
|
||||||
<module>noexception</module>
|
<module>noexception</module>
|
||||||
|
|
||||||
|
@ -129,13 +129,13 @@
|
||||||
<!-- <module>raml</module> -->
|
<!-- <module>raml</module> -->
|
||||||
<module>reactor-core</module>
|
<module>reactor-core</module>
|
||||||
<module>persistence-modules/redis</module>
|
<module>persistence-modules/redis</module>
|
||||||
<module>rest-assured</module>
|
<module>testing-modules/rest-assured</module>
|
||||||
<module>rest-testing</module>
|
<module>testing-modules/rest-testing</module>
|
||||||
<module>resteasy</module>
|
<module>resteasy</module>
|
||||||
<module>rxjava</module>
|
<module>rxjava</module>
|
||||||
<module>spring-swagger-codegen</module>
|
<module>spring-swagger-codegen</module>
|
||||||
|
|
||||||
<module>selenium-junit-testng</module>
|
<module>testing-modules/selenium-junit-testng</module>
|
||||||
<module>persistence-modules/solr</module>
|
<module>persistence-modules/solr</module>
|
||||||
<module>spark-java</module>
|
<module>spark-java</module>
|
||||||
<!-- <module>spring-5</module>-->
|
<!-- <module>spring-5</module>-->
|
||||||
|
@ -235,8 +235,8 @@
|
||||||
<module>spring-rest-embedded-tomcat</module>
|
<module>spring-rest-embedded-tomcat</module>
|
||||||
|
|
||||||
|
|
||||||
<module>testing</module>
|
<module>testing-modules/testing</module>
|
||||||
<module>testng</module>
|
<module>testing-modules/testng</module>
|
||||||
|
|
||||||
<module>video-tutorials</module>
|
<module>video-tutorials</module>
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@
|
||||||
<module>drools</module>
|
<module>drools</module>
|
||||||
<module>persistence-modules/liquibase</module>
|
<module>persistence-modules/liquibase</module>
|
||||||
<module>spring-boot-property-exp</module>
|
<module>spring-boot-property-exp</module>
|
||||||
<module>mockserver</module>
|
<module>testing-modules/mockserver</module>
|
||||||
<module>undertow</module>
|
<module>undertow</module>
|
||||||
<module>vertx-and-rxjava</module>
|
<module>vertx-and-rxjava</module>
|
||||||
<module>saas</module>
|
<module>saas</module>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.baeldung.sqlfiles;
|
||||||
|
|
||||||
|
import static javax.persistence.GenerationType.IDENTITY;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Country {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = IDENTITY)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
INSERT INTO country (name) VALUES ('India');
|
||||||
|
INSERT INTO country (name) VALUES ('Brazil');
|
||||||
|
INSERT INTO country (name) VALUES ('USA');
|
||||||
|
INSERT INTO country (name) VALUES ('Italy');
|
||||||
|
COMMIT;
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE country (
|
||||||
|
id INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
name VARCHAR(128) NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
|
@ -0,0 +1 @@
|
||||||
|
spring.jpa.hibernate.ddl-auto=none
|
|
@ -122,6 +122,25 @@
|
||||||
<artifactId>jstl-api</artifactId>
|
<artifactId>jstl-api</artifactId>
|
||||||
<version>${jstl.version}</version>
|
<version>${jstl.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-acl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context-support</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.ehcache</groupId>
|
||||||
|
<artifactId>ehcache-core</artifactId>
|
||||||
|
<version>2.6.11</version>
|
||||||
|
<type>jar</type>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package org.baeldung.acl.config;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.cache.ehcache.EhCacheFactoryBean;
|
||||||
|
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||||
|
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||||
|
import org.springframework.security.acls.AclPermissionCacheOptimizer;
|
||||||
|
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||||
|
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
|
||||||
|
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
|
||||||
|
import org.springframework.security.acls.domain.ConsoleAuditLogger;
|
||||||
|
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||||
|
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
|
||||||
|
import org.springframework.security.acls.jdbc.BasicLookupStrategy;
|
||||||
|
import org.springframework.security.acls.jdbc.JdbcMutableAclService;
|
||||||
|
import org.springframework.security.acls.jdbc.LookupStrategy;
|
||||||
|
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
public class ACLContext {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
DataSource dataSource;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EhCacheBasedAclCache aclCache() {
|
||||||
|
return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EhCacheFactoryBean aclEhCacheFactoryBean() {
|
||||||
|
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
|
||||||
|
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
|
||||||
|
ehCacheFactoryBean.setCacheName("aclCache");
|
||||||
|
return ehCacheFactoryBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EhCacheManagerFactoryBean aclCacheManager() {
|
||||||
|
return new EhCacheManagerFactoryBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PermissionGrantingStrategy permissionGrantingStrategy() {
|
||||||
|
return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AclAuthorizationStrategy aclAuthorizationStrategy() {
|
||||||
|
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
|
||||||
|
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||||
|
AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
|
||||||
|
expressionHandler.setPermissionEvaluator(permissionEvaluator);
|
||||||
|
expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
|
||||||
|
return expressionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LookupStrategy lookupStrategy() {
|
||||||
|
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JdbcMutableAclService aclService() {
|
||||||
|
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.baeldung.acl.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||||
|
public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MethodSecurityExpressionHandler createExpressionHandler() {
|
||||||
|
return defaultMethodSecurityExpressionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.baeldung.acl.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableTransactionManagement
|
||||||
|
@EnableJpaRepositories(basePackages = "org.baeldung.acl.persistence.dao")
|
||||||
|
@PropertySource("classpath:org.baeldung.acl.datasource.properties")
|
||||||
|
@EntityScan(basePackages={ "org.baeldung.acl.persistence.entity" })
|
||||||
|
public class JPAPersistenceConfig {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.baeldung.acl.persistence.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.baeldung.acl.persistence.entity.NoticeMessage;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
|
import org.springframework.security.access.prepost.PostFilter;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
|
||||||
|
public interface NoticeMessageRepository extends JpaRepository<NoticeMessage, Long>{
|
||||||
|
|
||||||
|
@PostFilter("hasPermission(filterObject, 'READ')")
|
||||||
|
List<NoticeMessage> findAll();
|
||||||
|
|
||||||
|
@PostAuthorize("hasPermission(returnObject, 'READ')")
|
||||||
|
NoticeMessage findById(Integer id);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
|
||||||
|
NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.baeldung.acl.persistence.entity;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name="system_message")
|
||||||
|
public class NoticeMessage {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column
|
||||||
|
private Integer id;
|
||||||
|
@Column
|
||||||
|
private String content;
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
INSERT INTO system_message(id,content) VALUES (1,'First Level Message');
|
||||||
|
INSERT INTO system_message(id,content) VALUES (2,'Second Level Message');
|
||||||
|
INSERT INTO system_message(id,content) VALUES (3,'Third Level Message');
|
||||||
|
|
||||||
|
INSERT INTO acl_class (id, class) VALUES
|
||||||
|
(1, 'org.baeldung.acl.persistence.entity.NoticeMessage');
|
||||||
|
|
||||||
|
INSERT INTO acl_sid (id, principal, sid) VALUES
|
||||||
|
(1, 1, 'manager'),
|
||||||
|
(2, 1, 'hr'),
|
||||||
|
(3, 1, 'admin'),
|
||||||
|
(4, 0, 'ROLE_EDITOR');
|
||||||
|
|
||||||
|
INSERT INTO acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) VALUES
|
||||||
|
(1, 1, 1, NULL, 3, 0),
|
||||||
|
(2, 1, 2, NULL, 3, 0),
|
||||||
|
(3, 1, 3, NULL, 3, 0)
|
||||||
|
;
|
||||||
|
|
||||||
|
INSERT INTO acl_entry (id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure) VALUES
|
||||||
|
(1, 1, 1, 1, 1, 1, 1, 1),
|
||||||
|
(2, 1, 2, 1, 2, 1, 1, 1),
|
||||||
|
(3, 1, 3, 4, 1, 1, 1, 1),
|
||||||
|
(4, 2, 1, 2, 1, 1, 1, 1),
|
||||||
|
(5, 2, 2, 4, 1, 1, 1, 1),
|
||||||
|
(6, 3, 1, 4, 1, 1, 1, 1),
|
||||||
|
(7, 3, 2, 4, 2, 1, 1, 1)
|
||||||
|
;
|
|
@ -0,0 +1,58 @@
|
||||||
|
create table system_message (id integer not null, content varchar(255), primary key (id));
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS acl_sid (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
principal tinyint(1) NOT NULL,
|
||||||
|
sid varchar(100) NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY unique_uk_1 (sid,principal)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS acl_class (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
class varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY unique_uk_2 (class)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS acl_entry (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
acl_object_identity bigint(20) NOT NULL,
|
||||||
|
ace_order int(11) NOT NULL,
|
||||||
|
sid bigint(20) NOT NULL,
|
||||||
|
mask int(11) NOT NULL,
|
||||||
|
granting tinyint(1) NOT NULL,
|
||||||
|
audit_success tinyint(1) NOT NULL,
|
||||||
|
audit_failure tinyint(1) NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY unique_uk_4 (acl_object_identity,ace_order)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS acl_object_identity (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
object_id_class bigint(20) NOT NULL,
|
||||||
|
object_id_identity bigint(20) NOT NULL,
|
||||||
|
parent_object bigint(20) DEFAULT NULL,
|
||||||
|
owner_sid bigint(20) DEFAULT NULL,
|
||||||
|
entries_inheriting tinyint(1) NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY unique_uk_3 (object_id_class,object_id_identity)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE acl_entry
|
||||||
|
ADD FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id);
|
||||||
|
|
||||||
|
ALTER TABLE acl_entry
|
||||||
|
ADD FOREIGN KEY (sid) REFERENCES acl_sid(id);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Constraints for table acl_object_identity
|
||||||
|
--
|
||||||
|
ALTER TABLE acl_object_identity
|
||||||
|
ADD FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id);
|
||||||
|
|
||||||
|
ALTER TABLE acl_object_identity
|
||||||
|
ADD FOREIGN KEY (object_id_class) REFERENCES acl_class (id);
|
||||||
|
|
||||||
|
ALTER TABLE acl_object_identity
|
||||||
|
ADD FOREIGN KEY (owner_sid) REFERENCES acl_sid (id);
|
|
@ -0,0 +1,12 @@
|
||||||
|
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
|
||||||
|
spring.datasource.username=sa
|
||||||
|
spring.datasource.password=
|
||||||
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||||
|
|
||||||
|
spring.h2.console.path=/myconsole
|
||||||
|
spring.h2.console.enabled=true
|
||||||
|
spring.datasource.initialize=true
|
||||||
|
spring.datasource.schema=classpath:acl-schema.sql
|
||||||
|
spring.datasource.data=classpath:acl-data.sql
|
|
@ -0,0 +1,119 @@
|
||||||
|
package org.baeldung.acl;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.baeldung.acl.persistence.dao.NoticeMessageRepository;
|
||||||
|
import org.baeldung.acl.persistence.entity.NoticeMessage;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
|
import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.TestExecutionListeners;
|
||||||
|
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||||
|
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||||
|
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||||
|
import org.springframework.test.context.web.ServletTestExecutionListener;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration
|
||||||
|
@TestExecutionListeners(listeners={ServletTestExecutionListener.class,
|
||||||
|
DependencyInjectionTestExecutionListener.class,
|
||||||
|
DirtiesContextTestExecutionListener.class,
|
||||||
|
TransactionalTestExecutionListener.class,
|
||||||
|
WithSecurityContextTestExecutionListener.class})
|
||||||
|
public class SpringAclTest extends AbstractJUnit4SpringContextTests{
|
||||||
|
|
||||||
|
private static Integer FIRST_MESSAGE_ID = 1;
|
||||||
|
private static Integer SECOND_MESSAGE_ID = 2;
|
||||||
|
private static Integer THIRD_MESSAGE_ID = 3;
|
||||||
|
private static String EDITTED_CONTENT = "EDITED";
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan("org.baeldung.acl.*")
|
||||||
|
public static class SpringConfig {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
NoticeMessageRepository repo;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username="manager")
|
||||||
|
public void givenUsernameManager_whenFindAllMessage_thenReturnFirstMessage(){
|
||||||
|
List<NoticeMessage> details = repo.findAll();
|
||||||
|
assertNotNull(details);
|
||||||
|
assertEquals(1,details.size());
|
||||||
|
assertEquals(FIRST_MESSAGE_ID,details.get(0).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username="manager")
|
||||||
|
public void givenUsernameManager_whenFindFirstMessageByIdAndUpdateFirstMessageContent_thenOK(){
|
||||||
|
NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID);
|
||||||
|
assertNotNull(firstMessage);
|
||||||
|
assertEquals(FIRST_MESSAGE_ID,firstMessage.getId());
|
||||||
|
|
||||||
|
firstMessage.setContent(EDITTED_CONTENT);
|
||||||
|
repo.save(firstMessage);
|
||||||
|
|
||||||
|
NoticeMessage editedFirstMessage = repo.findById(FIRST_MESSAGE_ID);
|
||||||
|
assertNotNull(editedFirstMessage);
|
||||||
|
assertEquals(FIRST_MESSAGE_ID,editedFirstMessage.getId());
|
||||||
|
assertEquals(EDITTED_CONTENT,editedFirstMessage.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username="hr")
|
||||||
|
public void givenUsernameHr_whenFindMessageById2_thenOK(){
|
||||||
|
NoticeMessage secondMessage = repo.findById(SECOND_MESSAGE_ID);
|
||||||
|
assertNotNull(secondMessage);
|
||||||
|
assertEquals(SECOND_MESSAGE_ID,secondMessage.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=AccessDeniedException.class)
|
||||||
|
@WithMockUser(username="hr")
|
||||||
|
public void givenUsernameHr_whenUpdateMessageWithId2_thenFail(){
|
||||||
|
NoticeMessage secondMessage = new NoticeMessage();
|
||||||
|
secondMessage.setId(SECOND_MESSAGE_ID);
|
||||||
|
secondMessage.setContent(EDITTED_CONTENT);
|
||||||
|
repo.save(secondMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(roles={"EDITOR"})
|
||||||
|
public void givenRoleEditor_whenFindAllMessage_thenReturnThreeMessage(){
|
||||||
|
List<NoticeMessage> details = repo.findAll();
|
||||||
|
assertNotNull(details);
|
||||||
|
assertEquals(3,details.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(roles={"EDITOR"})
|
||||||
|
public void givenRoleEditor_whenUpdateThirdMessage_thenOK(){
|
||||||
|
NoticeMessage thirdMessage = new NoticeMessage();
|
||||||
|
thirdMessage.setId(THIRD_MESSAGE_ID);
|
||||||
|
thirdMessage.setContent(EDITTED_CONTENT);
|
||||||
|
repo.save(thirdMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=AccessDeniedException.class)
|
||||||
|
@WithMockUser(roles={"EDITOR"})
|
||||||
|
public void givenRoleEditor_whenFindFirstMessageByIdAndUpdateFirstMessageContent_thenFail(){
|
||||||
|
NoticeMessage firstMessage = repo.findById(FIRST_MESSAGE_ID);
|
||||||
|
assertNotNull(firstMessage);
|
||||||
|
assertEquals(FIRST_MESSAGE_ID,firstMessage.getId());
|
||||||
|
firstMessage.setContent(EDITTED_CONTENT);
|
||||||
|
repo.save(firstMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>parent-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<build>
|
<build>
|
|
@ -4,16 +4,17 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<!-- NOT NEEDED - JSP <groupId>com.baeldung</groupId>-->
|
<!-- NOT NEEDED - JSP <groupId>com.baeldung</groupId>-->
|
||||||
<artifactId>junit5</artifactId>
|
<artifactId>junit-5</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>junit5</name>
|
<name>junit-5</name>
|
||||||
<description>Intro to JUnit 5</description>
|
<description>Intro to JUnit 5</description>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>parent-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue