How to use java properties file in OSGI Declarative Services Annotations - java

I'm trying to use bndtools to create my OSGI program. Here is my previous code, and it can work well with the felix console.
package com.buaa.ate.service.data.command;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.apache.felix.service.command.CommandProcessor;
import com.buaa.ate.service.api.data.Publisher;
#Component(
service=PublishCommand.class,
property={
CommandProcessor.COMMAND_SCOPE + ":String=example",
CommandProcessor.COMMAND_FUNCTION + ":String=publish",
}
)
public class PublishCommand {
private Publisher publishSvc;
#Reference
public void setPublisher(Publisher publishSvc) {
this.publishSvc = publishSvc;
}
public void publish(String content) {
publishSvc.start();
long result = publishSvc.publish(content);
System.out.println(result);
publishSvc.stop();
}
}
Now, I want to change the annotation like this:
package com.buaa.ate.service.data.command;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.apache.felix.service.command.CommandProcessor;
import com.buaa.ate.service.api.data.Publisher;
#Component(
service=PublishCommand.class,
properties="com/buaa/ate/service/data/command/config.properties"
)
public class PublishCommand {
private Publisher publishSvc;
#Reference
public void setPublisher(Publisher publishSvc) {
this.publishSvc = publishSvc;
}
public void publish(String content) {
publishSvc.start();
long result = publishSvc.publish(content);
System.out.println(result);
publishSvc.stop();
}
}
And this is my properties file:
config.properties
It's content like this:
osgi.command.scope\:String:example
osgi.command.function\:String:publish
When I run the program, input the command 'publish something', and then the problem happens:
'gogo: CommandNotFoundException: Command not found: publish'
So, what should I do to fix the problem?

Well, I just realize that it's so easy to fix the problem. This is a part of the osgi javadoc:
property
public abstract java.lang.String[] property
Properties for this Component.
Each property string is specified as "key=value". The type of the property value can be specified in the key as key:type=value. The type must be one of the property types supported by the type attribute of the property element of a Component Description.
To specify a property with multiple values, use multiple key, value pairs. For example, "foo=bar", "foo=baz".
See Also:
"The property element of a Component Description."
Default:{}
So I add the 'type' property to config.properties, and then the code can work well. Here is the current properties file:
current properties file
And it's content like this:
osgi.command.scope=example
osgi.command.scope\:type:String
osgi.command.function=publish
osgi.command.function\:type:String
The program can work well now.

Related

Select classes with given annotation

I have a bunch of source files for java classes. I want to find those classes which are annotated by a given annotation class. The names of those classes should be written to a service provider list file.
Is there any machinery I could use to help me with this task? Or do I have to implement this myself from scratch?
If I had to do this myself, there are several approaches I can think of.
Write an Ant Task in Java. Have it create a ClassLoader using a suitable (probably configurable) class path. Use that loader to (attempt to) load the classes matching the input files, in order to inspect their annotations. Requires annotation retention at runtime, and full initialization of all involved classes and their dependencies.
Use javap to inspect the classes. Since I don't know of a programmatic interface to javap (do you?), this probably means iterating over the files and running a new process for each of them, then massaging the created output in a suitable way. Perhaps a <scriptdef>-ed task could be used for this. This would work with class-file annotation retention, and require no initialization.
Use an annotation processor to collect the information at compile-time. This should be able to work with sourcecode-only retention. But I have no experience writing or using annotation compilers, so I'm not sure this will work, and will need a lot of research to figure out some of the details. In particular how to activate the task for use by ant (Java 6 annotation processing configuration with Ant gives some pointers on this, as does What is the default annotation processors discovery process?) and when to create the output file (in each round, or only in the last round).
Which of these do you think has the greatest chances of success? Can you suggest code samples for one of these, which might be close to what I want and which I could adapt appropriately?
Encouraged by Thomas' comment, I gave approach 3 a try and got the following annotation processor working reasonably well:
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.tools.StandardLocation;
#SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AnnotationServiceProcessor extends AbstractProcessor {
// Map name of the annotation to name of the corresponding service interface
private static Map<String, String> annotationToServiceMap = new HashMap<>();
static {
// Adapt this to your use, or make it configurable somehow
annotationToServiceMap.put("Annotation1", "Service1");
annotationToServiceMap.put("Annotation2", "Service2");
}
#Override public Set<String> getSupportedAnnotationTypes() {
return annotationToServiceMap.keySet();
}
// Map name of the annotation to list of names
// of the classes which carry that annotation
private Map<String, List<String>> classLists;
#Override public void init(ProcessingEnvironment env) {
super.init(env);
classLists = new HashMap<>();
for (String ann: getSupportedAnnotationTypes())
classLists.put(ann, new ArrayList<String>());
}
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
for (TypeElement ann: annotations) {
List<String> classes =
classLists.get(ann.getQualifiedName().toString());
for (Element elt: env.getElementsAnnotatedWith(ann)) {
QualifiedNameable qn = (QualifiedNameable)elt;
classes.add(qn.getQualifiedName().toString());
}
}
if (env.processingOver()) { // Only write results at the end
for (String ann: getSupportedAnnotationTypes()) {
try {
write(ann, classLists.get(ann));
} catch (IOException e) {
throw new RuntimeException(e); // UGLY!
}
}
}
return true;
}
// Write the service file for each annotation we found
private void write(String ann, List<String> classes) throws IOException {
if (classes.isEmpty())
return;
String service = annotationToServiceMap.get(ann);
Writer w = processingEnv.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT,
"", "META-INF/services/" + service)
.openWriter();
classes.sort(null); // Make the processing order irrelevant
for (String cls: classes) {
w.write(cls);
w.write('\n');
}
w.close();
}
}
So far I've hooked this up to ant using <compilerarg>s from https://stackoverflow.com/a/3644624/1468366. I'll try something better and if I succeed will edit this post to include some ant snippet.

Intellij Completion Contributor

I am developing a plugin for intellij and I want to add custom suggestions to xml editor based on a xsd. Up to now I can get required suggestions from xsd file.
I have implemented a completion contributor for xml as follows
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.xml.XmlElementType;
import com.intellij.util.ProcessingContext;
import com.intellij.lang.xml.*;
import org.jetbrains.annotations.NotNull;
public class SimpleCompletionContributor extends CompletionContributor {
public SimpleCompletionContributor() {
extend(CompletionType.BASIC,PlatformPatterns.psiElement(XmlElementType.XML_ATTRIBUTE_VALUE).withLanguage(XMLLanguage.INSTANCE),
new CompletionProvider<CompletionParameters>() {
public void addCompletions(#NotNull CompletionParameters parameters,
ProcessingContext context,
#NotNull CompletionResultSet resultSet) {
resultSet.addElement(LookupElementBuilder.create("Hello"));
}
}
);
}
}
but this did not provide any suggestion. but when I implement custom language it works. My objective is to view the context of the cursor position and provide suggestion based on it. as an example when user starts a tag on xml file plugin should provide attributes as code completion. I'm new to this Custom language.
So can anyone help me with this completion contributor?
finally i found a way to solve this problem
here is my code
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
public class ScalaXMLCompletionContributor extends CompletionContributor {
public ScalaXMLCompletionContributor() {
final RelativeNodes rlt = new RelativeNodes();//this is a class to get siblings and children from a sample xml file generated by a given xsd
/*if the parameter position is an xml attribute provide attributes using given xsd*/
extend(CompletionType.BASIC,
PlatformPatterns.psiElement(), new CompletionProvider<CompletionParameters>() {
public void addCompletions(#NotNull CompletionParameters parameters,//completion parameters contain details of the curser position
ProcessingContext context,
#NotNull CompletionResultSet resultSet) {//result set contains completion details to suggest
if (parameters.getPosition().getContext().toString() == "XmlAttribute") {//check whether scala text editors position is an xml attribute position eg: <name |
try {
String[] suggestions = rlt.getAttribute(parameters.getPosition().getParent().getParent().getFirstChild().getNextSibling().getText().replaceFirst("IntellijIdeaRulezzz", ""));//extract text from completion parameter and get required suggestions from RelativeNodes
int i = 0;
do {
resultSet.addElement(LookupElementBuilder.create(suggestions[i]));//add suggestions to resultset to suggest in editor
i++;
} while (suggestions[i] != null);
} catch (NullPointerException e) {
}
}
}
}
);
}
}
in this case we can obtain cursor position and tokens related to curser position by completion parameters and we can inject suggestions using cpmpletion resultset. this can be implemented in scala language too.
to register completion contributor in plugin xml
<extensions defaultExtensionNs="com.intellij">
<completion.contributor language="Scala" implementationClass="com.hsr.ScalaXMLCompletionContributor"/>
</extensions>
JavaDoc for com.intellij.codeInsight.completion.CompletionContributor contains FAQ.
The last question addresses debugging not working completion.
In my case issue was language="Java", whereas all caps expected.

HippoCMS component for custom document type not calling document bean

I've created a compound document type called SampleCaps in a HippoCMS 7.9 site and set out to build a template for it. In the process, I added hst:sitemap nodes, a pair of nested hst:pages nodes, and an hst:templates node. I also added the appropriate type property to hippo:namespaces/barcom/SampleCaps.
Finally, I created a Component and a Bean to expose the document data to the template, adapting the steps presented in part 2 of the Hippo Video Trails.
To my frustration, while the Component loads properly the Bean is never loaded (or at least, its getter is never called.) My component and bean are as follows:
site/src/main/java/com/footech/barcom/components/SampleCapsComponent.java:
package com.footech.barcom.components;
import com.footech.barcom.beans.SampleCapsDocument;
import org.hippoecm.hst.content.beans.standard.HippoBean;
import org.hippoecm.hst.component.support.bean.BaseHstComponent;
import org.hippoecm.hst.core.component.HstComponentException;
import org.hippoecm.hst.core.component.HstRequest;
import org.hippoecm.hst.core.component.HstResponse;
public class SampleCapsComponent extends BaseHstComponent {
#Override
public void doBeforeRender(final HstRequest request, final HstResponse response) throws HstComponentException {
SampleCapsDocument document = request.getRequestContext().getContentBean();
request.setAttribute("document", document);
System.out.println("Ping"); /* prints "Ping" to console */
}
}
site/src/main/java/com/footech/barcom/beans/SampleCapsDocument.java:
package com.footech.barcom.beans;
import java.util.Calendar;
import org.hippoecm.hst.content.beans.Node;
import org.hippoecm.hst.content.beans.standard.HippoHtml;
import org.onehippo.cms7.essentials.dashboard.annotations.HippoEssentialsGenerated;
#HippoEssentialsGenerated(internalName = "barcom:SampleCapsdocument")
#Node(jcrType = "barcom:SampleCapsdocument")
public class SampleCapsDocument extends BaseDocument {
#HippoEssentialsGenerated(internalName = "barcom:title")
public String getTitle() {
System.out.println("Pong"); /* This never triggers */
return getProperty("barcom:title");
}
}
To my understanding, the annotation #Node(jcrType = "barcom:SampleCapsdocument") in SampleCapsComponent.java should hint to the compiler that the content node should be wrapped with the SampleCapsDocument bean - this does not appear to be the case, as the debug console prints Ping but not Pong. What am I doing wrong?
you'll need to call document.getTitle() because values are lazy loaded.

Merge into Java code

Using a sample from xSocket which will run xSocketHandler as a new process, I want to customize and moving all of these code into other java file, can I copy public class xSocketDataHandler implements IDataHandler and paste into different filename say main.java?
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import org.xsocket.*;
import org.xsocket.connection.*;
public class xSocketDataHandler implements IDataHandler
{
public boolean onData(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException
{
try
{
String data = nbc.readStringByDelimiter("\0");
//nbc.write("Reply" + data + "\0");
nbc.write("+A4\0");
if(data.equalsIgnoreCase("SHUTDOWN"))
xSocketServer.shutdownServer();
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
return true;
}
}
No, you can't do that without reducing the visibility of xSocketDataHandler to default. If you don't want to do that, your file name should be xSocketDataHandler.java
You must be having class xSocketDataHandler in a file of the same name already since it is public. You could move other non public classes in this file to Main.java instead.
A public class will need to be in a file named according to the class, so in this case it would be xSocketDataHandler.java.
Convention is also to name java classes starting with an upper-case letter, so it would be public class XSocketDataHandler and file XSocketDataHandler.java. This isn't required, though.

renaming DLL functions in JNA using StdCallFunctionMapper

I'm trying to use JNA with a DLL in Windows, so far I was able to successfully call a function called c_aa_find_devices(). But all the functions start with c_aa and I would like to rename it to find_devices().
From what I gather the way to do this is with StdCallFunctionMapper but I can't find the documentation of how to use it in an example (i.e. how to map a DLL function by name or by ordinal to a desired name in the wrapped Java library interface). Any suggestions on where the docs are?
A complete working example, using a function mapper.
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.win32.StdCallFunctionMapper;
import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class JnaTest {
static {
Map options = new HashMap();
options.
put(
Library.OPTION_FUNCTION_MAPPER,
new StdCallFunctionMapper() {
HashMap<String, String> map = new HashMap() {
{
put("testMethod", "testMethod#0");
}
};
#Override
public String getFunctionName(NativeLibrary library, Method method) {
String methodName = method.getName();
return map.get(methodName);
}
}
);
File LIB_FILE = new File("test.dll");
Native.register(NativeLibrary.getInstance(LIB_FILE.getAbsolutePath(), options));
}
private static native int testMethod();
public static void main(String[] args) {
testMethod(); // call the native method in the loaded dll with the function name testMethod#0
}
}
Using StdCallMapper won't do good - it is supposed to map werid windows std lib names that have embedded total byte lenght of parameters embedded as part of the name. Since it is done to std lib only (just guessing on that, but 99% you'r functions are not the case).
If your dll uses some common prefix on all functions you need just to use something like:
class Mapper implements FunctionMapper{
public String getFunctionName(NativeLibrary library, Method method) {
return GenieConnector.FUNCTION_PREFIX + method.getName();
}
}
Where GenieConnector.FUNCTION_PREFIX is that common prefix. Bear in mind that i implement FunctionMapper, not extend StdCallMapper
From the documentation you need to provide a FunctionMapper in the original call to loadLibrary that converts the name. However you also need to keep the standard call mapping so try something like the following:
Map options = new HashMap();
options.
put(
Library.OPTION_FUNCTION_MAPPER,
new StdCallFunctionWrapper() {
public String getFunctionName(NativeLibrary library, Method method) {
if (method.getName().equals("findDevices")
method.setName("c_aa_find_devices");
// do any others
return super.getFunctionName(library, method);
}
}
);
Native.loadLibrary(..., ..., options);
All JNA documentation is located at the primary web page, the JavaDoc overview, and the JavaDocs themselves.
The example above is the right idea, in that you need to tweak the function name returned by the generic StdCallFunctionMapper (assuming you're using the stdcall calling convention). However, Method.setName() doesn't exist and you wouldn't want to call it if it did. You'll need to get the String result and replace the Java function name within it with the target native name, e.g.
name = super.getFunctionName();
name = name.replace("find_devices", "c_aa_find_devices");
More generically, you can simply tack on a "c_aa_" prefix to the returned name (or after any leading underscore), since stdcall decorations are at the end of the name.

Categories