How to programmatically new a java class which implements sepecified interface in eclipse plugin development - eclipse

Friends,
Now we are developing a eclipse plugin, it contains a action to generated a service interface and it's impl stub.
Now the interface is generated, I want to use eclipse JDT to create a java class which implements sepecified interface, but don't know how.
The info we have:
the interface name, the impl class name, the packagename, the java project contains them.
Thanks in advance for your kindly help.

A quick scan of how the new class wizard does it, it seems that there is no public easy to use API for this. You can have a look at org.eclipse.jdt.ui.wizards.NewTypeWizardPage.createType(IProgressMonitor) method to see how JDT itself creates new classes.
It should be possible to extend the org.eclipse.jdt.ui.wizards.NewTypeWizardPage, so you can leverage the createType() method.

Probably the minimal steps you would have to do is simply generate source content into the correctly placed IFile. ex:
public Object execute(ExecutionEvent event) throws ExecutionException {
final String PACKAGE_PATH = "z.ex/src/z/ex/go";
final String CONTENT = "package z.ex.go;\n"
+ "public class RunAway {\npublic static void main(String[] args) {\n"
+ "System.out.println(\"Run Away\");\n}\n}\n";
final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
final IResource packageResource = root.findMember(PACKAGE_PATH);
if (packageResource instanceof IFolder) {
IFolder packageFolder = (IFolder) packageResource;
final IFile file = packageFolder.getFile("RunAway.java");
try {
if (!file.exists()) {
file.create(new ByteArrayInputStream(CONTENT.getBytes()),
true, new NullProgressMonitor());
} else {
file.setContents(
new ByteArrayInputStream(CONTENT.getBytes()),
IFile.FORCE | IFile.KEEP_HISTORY,
new NullProgressMonitor());
}
} catch (CoreException e) {
e.printStackTrace();
}
}
return null;
}
See AbstractNewClassWizard for a smaller example that is similar to NewTypeWizardPage and uses some of the JDT APIs.

You can use the new class wizard to create classes.
This will prompt the user for the class name, et cetera. You can initialize the values of the wizard page. Below I am setting the source folder only (and tell the wizard that it cannot be changed, thus the second false parameter). You might want to set the interface and possible the package as well.
OpenNewClassWizardAction wizard = new OpenNewClassWizardAction();
wizard.setOpenEditorOnFinish(false);
NewClassWizardPage page = new NewClassWizardPage();
page.setPackageFragmentRoot(sourceFolder, false);
wizard.setConfiguredWizardPage(page);
wizard.run();
return (IType) wizard.getCreatedElement();
Hope that helps!

Create the entire java file using the JDT - AST. First create the AST and then write it to a java file. It might look as hefty work, but its the best one. You will have complete control.

Related

Where to place Rythm template files

I am having a weird problem with Rythm templates. Currently, I have these templates placed under
myPrj/src/main/java/resources/templates folder.
And all the Java source code is under myPrj/src/main/java folder.
When I try to render, sometimes Rythm is generating the XML file and sometimes I get the file name as is.
I have the home.template set to "templates" folder:
params.put("home.template", "templates");
String myTemplateString = Rythm.render("MyTemplate.xml", parameters);
Looks like Rythm is not able to locate MyTemplate.xml and resulting in emitting MyTemplate.xml as the output.
Can you please help me on how to solve this problem?? In addition, would appreciate if you can guide me on what should be the appropriate location to place these templates.
home.template is the configuration key to initialize template engine, not the parameter to render your template.
My implementation of your app looks like
public class App {
private static RythmEngine engine;
private static void echo(String msg, Object ... args) {
System.out.println(String.format(msg, args));
}
private static void init() {
echo("initializing rythmengine");
Map<String, Object> conf = new HashMap<String, Object>();
conf.put("home.template", "templates");
engine = new RythmEngine(conf);
echo("engine initialized");
}
private static void render() {
Map<String, Object> params = new HashMap<String, Object>();
params.put("foo", "FOO");
params.put("bar", "BAR");
String result = engine.render("MyTemplate.xml", params);
echo(result);
}
private static void doJob() {
echo("start doing real job now...");
render();
}
public static void main( String[] args ) {
init();
doJob();
}
}
The complete sample code could be found at https://github.com/greenlaw110/Rythm/tree/master/samples/demo_fo_SO_150529. Download the sample code and run mvn compile exec:java to see the result
It seems your problem lies within the path for the home.template. The example on their website might help.
If I'm not mistaken, you should use params.put("home.template", "resources/templates"); rather than params.put("home.template", "templates");.
Generally speaking, this kind of behaviour takes place any time Rythm can't find the template. I found it is best to check both, the path and file name. If necessary, simply use an absolute path to your template to make sure it points to the right directory. After you got the right path, you might want to change it back to be relative.

JBehave doesn't recognize steps, but does load the steps file

With a different test runner (the annotation based one) the steps get picked up and run. The annotation based approach doesn't seem to support a steps factory though, so I swapped models. Now, it will load the steps class (some visible things happen when the constructor is called) but it won't recognize any of the steps inside it. Any ideas? Here's my embedder class:
public class LoginAcceptanceFull extends JUnitStories {
private final CrossReference xref = new CrossReference();
public LoginAcceptanceFull() {
configuredEmbedder().embedderControls().doGenerateViewAfterStories(true)
.doIgnoreFailureInStories(true).doIgnoreFailureInView(true).useThreads(2)
.useStoryTimeoutInSecs(60);
}
#Override
public Configuration configuration() {
Class<? extends Embeddable> embeddableClass = this.getClass();
Properties viewResources = new Properties();
viewResources.put("decorateNonHtml", "true");
// Start from default ParameterConverters instance
ParameterConverters parameterConverters = new ParameterConverters();
// factory to allow parameter conversion and loading from external
// resources (used by StoryParser too)
ExamplesTableFactory examplesTableFactory = new ExamplesTableFactory(new LocalizedKeywords(),
new LoadFromClasspath(embeddableClass), parameterConverters);
// add custom converters
parameterConverters.addConverters(new DateConverter(new SimpleDateFormat("yyyy-MM-dd")),
new ExamplesTableConverter(examplesTableFactory));
return new MostUsefulConfiguration()
.useStoryControls(new StoryControls().doDryRun(false).doSkipScenariosAfterFailure(false))
.useStoryLoader(new LoadFromURL())
.useStoryParser(new RegexStoryParser(examplesTableFactory))
.useStoryPathResolver(new UnderscoredCamelCaseResolver())
.useStoryReporterBuilder(
new StoryReporterBuilder()
.withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
.withDefaultFormats().withPathResolver(new ResolveToPackagedName())
.withViewResources(viewResources).withFormats(org.jbehave.core.reporters.Format.HTML,
org.jbehave.core.reporters.Format.TXT, org.jbehave.core.reporters.Format.XML)
.withFailureTrace(true).withFailureTraceCompression(true).withCrossReference(xref))
.useParameterConverters(parameterConverters)
.useStepPatternParser(new RegexPrefixCapturingPatternParser("%"))
.useStepMonitor(xref.getStepMonitor());
}
#Override
public InjectableStepsFactory stepsFactory(){
return new InstanceStepsFactory(configuration(), new LoginSteps());
}
#Override
protected List<String> storyPaths(){
String codeLocation = org.jbehave.core.io.CodeLocations.codeLocationFromClass(this.getClass()).getFile();
return new StoryFinder().findPaths(codeLocation, asList("**/login_trial.story"),
asList(""), "file:" + codeLocation);
}
}
I found it. Right here was the culprit:
.useStepPatternParser(new RegexPrefixCapturingPatternParser("%"))
It was causing JBehave to not recognize the #Given annotations and so JBehave assumed everything needed a step and listed them all as pending (or skipped them because they were missing the #Given step). Once I pulled that part of the configuration everything was cool.

P2.inf: refer update sites based on location

I have developed an RCP application and I am providing p2 update functionality.
In my p2.inf i am providing the url for update site. I don't want user to add any site so I have disabled an option to add a site. My p2.inf looks like:
instructions.configure=\
addRepository(type:0,location:http${#58}//blrupdates.com/Updates);\
addRepository(type:1,location:http${#58}//blrupdates.com/Updates);
if user location is bangalore, RCP application should go to brlupdates.com, if user location is chennai then RCP application should look for update at chnupdates.com.
how to add another repository location in p2.inf?
-Priyank
You need to add the repository programatically. The following solution is taken from here. Another solution, that I'm not sure it would work is to use redirection. You define one p2 repository and then you redirect the user to the location based repository.
import org.eclipse.equinox.internal.p2.ui.model.MetadataRepositoryElement;
import org.eclipse.equinox.internal.p2.ui.model.ElementUtils;
#SuppressWarnings("restriction")
public class P2Util {
private static String UPDATE_SITE = "http://www.example.com/update_site";
public static void setRepositories() throws InvocationTargetException {
try {
final MetadataRepositoryElement element = new MetadataRepositoryElement(null, new URI(UPDATE_SITE), true);
ElementUtils.updateRepositoryUsingElements(new MetadataRepositoryElement[] {element}, null);
} catch (URISyntaxException e) {
e.printStackTrace();
throw new InvocationTargetException(e);
}
}
}

How can I use properties taken from a file in the code of an eclipse PDE fragment/plugin project?

I have created an eclipse plugin project and a corresponding fragment project which I use for junit tests.
In the fragment I specify the plugin project as the "Host plugin". Further I specify the following on the build.properties pane:
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
my.properties
where my.properties is a file located at the root of the fragment project. I have then written a test where I try to load the my.properties file like this:
Properties properties = new Properties();
InputStream istream = this.getClass().getClassLoader()
.getResourceAsStream("my.properties");
try {
properties.load(istream);
} catch (IOException e) {
e.printStackTrace();
}
but istream is null and the test fails with a NullPointerException when calling load in the try block.
I have tried to do the same thing in the host plugin and there it works fine. Any ideas about why I can't read resouces in my PDE fragment when using Junit?
Try using Bundle#getEntry. If your plug-in has an Activator, you get a BundleContext object when your plugin is started (use Bundle-ActivationPolicy: lazy in your manifest). You can get the Bundle object from the BundleContext:
public class Activator implements BundleActivator {
private static Bundle bundle;
public static Bundle getBundle() {
return myBundle;
}
public void start(BundleContext context) throws Exception {
bundle = context.getBundle();
}
}
...
URL url = Activator.getBundle().getEntry("my.properties");
InputStream stream = url.openStream();
properties.load(stream);
One problem you MIGHT be having is that
InputStream istream = this.getClass().getClassLoader().
getResourceAsStream("my.properties");
behaves differently in two situations where "this" is located in a different package. Since you did not append "/" to the beginning, java will automatically start looking at the package root instead of the classpath root for the resource. If the code in your plug-in project and your fragment project exist in different packages, you have a problem.
Andrew Niefer has pointed the direction, but the solution is wrong. That is one that works:
1) Add super(); to the your Activator constructor.
2) Put this into the constructor of your plugin:
Properties properties = new Properties();
try {
Bundle bundle=Activator.getDefault().getBundle();
URL url = bundle.getEntry("plugin.properties");
InputStream stream;
stream = url.openStream();
properties.load(stream);
} catch (Exception e) {
e.printStackTrace();
}
And you have functioning "properties".
Explanations:
Doing (1) you will reach all that functionality:
public class Activator implements BundleActivator {
private static Bundle bundle;
public static Bundle getBundle() {
return myBundle;
}
public void start(BundleContext context) throws Exception {
bundle = context.getBundle();
}
}
It is present already in the pre-parent class Plugin. And you simply can't put it into Activator, because getBundle() is final in Plugin.
Notice Activator.getDefault() in (2). Without it bundle is unreachable, it is not static. And if you simply create a new instance of activator, bundle of it will be null.
There is one more way to take a bundle:
Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID);
Only check that Activator.PLUGIN_ID is set to the correct string - as is in the ID field of the Overview page of the plugin. BTW, you should check this Activator.PLUGIN_ID after every change of the plugin ID anyway.

Why am I having trouble accessing a .properties file in a standalone instance of tomcat but not in an eclipse-embedded instance?

I wrote a simple Hello World Servlet in Eclipse containing the following in the doGet method of my HelloWorldServlet.java
PrintWriter writer = response.getWriter();
String hello = PropertyLoader.bundle.getProperty("hello");
writer.append(hello);
writer.flush();
PropertyLoader is a simple class in the same package as the Servlet that does the following:
public class PropertyLoader {
public static final Properties bundle = new Properties();
static {
InputStream stream = null;
URL url = PropertyLoader.class.getResource("/helloSettings.properties");
stream = new FileInputStream(url.getFile());
bundle.load(stream);
}
}//End of class
I placed a file called helloSettings.properties in /WebContent/WEB-IND/classes that contains the following single line of content:
hello=Hello Settings World
When I add Tomcat 6.0 to my project and run it in eclipse it successfully prints
"Hello Settings World" to the web browser.
However when I export the project as a war file and manually place it in
.../Tomcat 6.0/webapps I then get "null" as my result.
Is it a problem with the classpath/classloader configuration? permissions? any of the other configuration files? I know for a fact that the helloSettings.properties file is in the WEB-INF/classes folder.
Any help?
Well, after much browsing I found what seems a "normal" why to do what I'm trying to do:
Instead of...(how I was doing it)
public class PropertyLoader {
public static final Properties bundle = new Properties();
static {
InputStream stream = null;
URL url = PropertyLoader.class.getResource("/helloSettings.properties");
stream = new FileInputStream(url.getFile());
bundle.load(stream);
}
}//End of class
THE FIX
public class PropertyLoader {
public static final Properties bundle = new Properties();
static {
InputStream stream = null;
stream = SBOConstants.class.getResourceAsStream("/sbonline.properties");
bundle.load(stream);
}
}//End of class
I'm modifiying someone else's code so I'm not sure why they did it the other way in the first place... but I guess url.getFile() was my problem and I don't know why.