Load a package and inside classes - class

I'm creating a package with some classes that I generated with wsimport on the fly and now I'm trying to load it to use, how can I do this? natively? or with some lib like byte-buddy, I tried the bellow code to load each class in a package:
File [] files = new File("<Path to package in filesystem with classes (*.class)>").listFiles();
List<URL> classUrls = new ArrayList<>();
for(File file : files) {
classUrls.add(new URL("file://" + file.getAbsolutePath()));
}
URL[] classz = new URL[classUrls.size()];
classz = classUrls.toArray(classz);
URLClassLoader child = new URLClassLoader(classz);
Class.forName("com.abc.external.resources.genwn239aqyhmfz.SomeClass", true, child);
But I still getting (Package: com.abc.external.resources.genwn239aqyhmfz.SomeClass)
java.lang.ClassNotFoundException: com.abc.external.resources.genwn239aqyhmfz.SomeClass

The rules for the class path are not different to the rules you have to obey when launching your application. The class path entries are not class files nor directories containing them, but the roots of your package structure.
So if the class you want to load is com.abc.external.resources.genwn239aqyhmfz.SomeClass, the class path entry has to be the directory containing the com directory, which contains the abc directory, and so on. If you know the expected full qualified name of one of the classes, it’s easy to find the right directory. Just traverse to the file hierarchy up as many times as the qualified name has package components. However, when you don’t know the name beforehand, finding it can be tricky. Here is a sketch:
// pass the expected name of one class contained in f or null if not known
static void loadClasses(File f, String predictedName)
throws IOException, ClassNotFoundException {
File[] classes = f.listFiles((d,n)->n.endsWith(".class"));
if(classes == null || classes.length == 0) {
System.err.println("no classes or not a directory");
return;
}
if(predictedName == null) predictedName = predictName(classes[0]);
for(int p = predictedName.indexOf('.'); p >= 0; p = predictedName.indexOf('.', p+1))
f = f.getParentFile();
URLClassLoader classLoader = new URLClassLoader(new URL[] { f.toURI().toURL() });
String packageName = predictedName.substring(0, predictedName.lastIndexOf('.')+1);
for(File cf: classes) {
String name = cf.getName();
name = name.substring(0, name.length()-6); // strip off ".class"
Class<?> cl = classLoader.loadClass(packageName+name);
// what do you wanna do with the classes?
System.out.println(cl);
}
}
private static String predictName(File classFile) throws IOException {
byte[] data = Files.readAllBytes(classFile.toPath());
return new ClassLoader() {
String getName() {
return defineClass(null, data, 0, data.length).getName();
}
}.getName();
}
The predictName implementation is a very simple one. If the class has dependencies to classes within the same file hierarchy which the JVM immediately tries to resolve, it will fail as we don’t have the necessary information yet. In that case, only a bytecode parsing library allowing to extract the name without loading the class would help. But that exceeds the scope of this question…

Related

RepositoryItem from List&Label .lst file

Currently we are using a WPF application for creation/editing of List&Label Templates, but we are considering to move to the WebDesigner. Because we use project includes we need to use the repository mode.
I've been trying to import our existing templates, but I run into some issues regarding the RepositoryItemDescriptor. To create a RepositoryItem object you have to give a Descriptor in the constructor, but I cannot find any info regarding how you get it from the generated .lst file.
The data that we have at our disposal are:
TemplateType: List or Form
TemplateData: content of the .lst file (byte[])
IsMainTemplate: bool, is a "project include" or not
File name: name of the .lst file
The RepositoryItem constructor requires: string internalID, string descriptor, string type, DateTime lastModificationUTC.
What I have now is:
public class TemplateBaseModel : RepositoryItem
{
// Properties
// we have our own Ids and modification date, override RepositoryItem properties
public new InternalID => $"repository://{{{Id}}}";
public DateTime LastModificationUTC => ModifiedOn;
public TemplateBaseModel() : base($"repository://{{{Guid.NewGuid()}}}", /* ?? */, RepositoryItemType.ProjectList.Value, DateTime.Now) { }
public TemplateBaseModel(string internalID, string descriptor, string type, DateTime lastModificationUTC) : base(internalID, descriptor, type, lastModificationUTC) { }
}
In the documentation I can only find what it is (internal metadata that is serialized into a string, and can be edited with the class RepositoryItemDescriptor), but not how it's created or how you can get it, and if I try to debug the example I get (in the CreateOrUpdate() method)#2#PgUAAENoS19QYWNrZWQAeNqd1E1PE1EYxfHfmsTvMAyJEeLY8iKCtpChU5MmvAiOC2NcjDCYmqFtZkaEqF9dXThgsTVGt/fm+Z9zz3lyv3/r2HXlQiFwKVeqDI2NdIVWPdIWCuRGTo2dGRp5ryv0Suq5yKpNoUCllhk5kymMjeS6QtdyldCuHfcs6FgUiQQSqUQgEk3dJY70pF57oS8wURo7N1TIBd64Z0GgY1HfodRA6rXAqVIgdN+SK21tbZlnt4o9J41W2OjNo9Qy72Y421OcVGzvD6R9fQcNcdb7A4WhSm3FQ4GhWu7CimUrt6T5rJvJacruHcruHEosldo38PI3ykjmQi7Qk4ilYoElJ/qOvTJwoi+Z4s33daMeeGDJiyna8szs725+zf6vmz8Tf+71U5WJzGmT/5ncucxHhdoXE6VcJVe6lFsWCGdOQzsCb+ds8I3T6R2+2/qv/ZjNvit0IjcxVhmqjZWuDZpXhHfanE2rKzSQCO0o53Ceamn5rGdTrC3Ws6YtkuiJbYts2LJlXWRbbNWayIbEE7E9sZ4Na9Y91vdVR+vWx9+9pa5NmvwKhVaTzQe5U7WWQqX+R+q+TKV20PxI54ZyZ0I7LmXK5t17PkkcOnSkdKxtT6pwLNbVnava0brt6abP1txGfwD+q8AH, which doesn't help either.
Any idea how to properly create a RepositoryItem from a .lst file? or how to create/get the descriptor?
You should try and use the class RepositoryImportUtil from the combit.ListLabel23.Repository namespace. This helper class does all the hard work for you. Given an IRepositoryinterface and the lst file in place, the required code would be something like
IRepository listLabelRepository = <yourRepository>;
using (ListLabel LL = new ListLabel())
{
LL.FileRepository = listLabelRepository;
using (RepositoryImportUtil importUtil = new RepositoryImportUtil(listLabelRepository))
{
importUtil.ImportProjectFileWithDependencies(LL,
#"<PathToRootProject>");
}
}
If this method is not what your require, the helper class has a couple of other methods as well to help you importing existing projects.

Eclipse Project Explorer filter for XML files

I have many XML files in the workbench Project Explorer, each one an instance of one of ten different ecore models. For each ecore model I would like to contribute a commonFilter to the navigator's navigatorContent extension point to enable the user to show or hide the corresponding XML files. These are external tool files so there is not a way to identify the content merely by observing the file name or the xml extension, and renaming is not feasible. Using perhaps a class deriving from org.eclipse.jface.viewers.ViewerFilter, what is the best way to identify which of the ecore models the XML file contains? I presume there is a simple way to do this with EMF resources, or with EcoreUtil, or with adapters, but I haven't found a successful technique. Alternatively, a way to do this directly from the extension point's filterExpression or the viewer's viewerContentBinding would be fine. All of the genmodel-derived plugins are available for the various ecore models.
package com.my.navigator;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
public class MyViewerFilter extends ViewerFilter {
public MyViewerFilter() {
}
#Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if ( element instanceof IFile ) {
IFile file = (IFile)element;
// check whether file is one of our ecore models...
// ...
}
return true;
}
}
You could use the org.eclipse.core.contenttype.contentTypes to define new content types for your file.
Use the describer argument of the content type definition to specify a 'content describer' class which can check that the XML file meets your requirements. There is already an XMLContentDescriber class that you can use as a basis for the describer.
For example this is the content type definition for Ant build.xml files:
<extension
point="org.eclipse.core.contenttype.contentTypes">
<content-type
id="antBuildFile"
name="%antBuildFileContentType.name"
base-type="org.eclipse.core.runtime.xml"
file-names="build.xml"
file-extensions="macrodef,ent,xml,ant"
priority="normal">
<describer
class="org.eclipse.ant.internal.core.contentDescriber.AntBuildfileContentDescriber">
</describer>
</content-type>
</extension>
and this is the Ant content describer to give you a rough idea of what you can:
public final class AntBuildfileContentDescriber extends XMLContentDescriber implements IExecutableExtension {
private int checkCriteria(InputSource contents) throws IOException {
AntHandler antHandler = new AntHandler();
try {
if (!antHandler.parseContents(contents)) {
return INDETERMINATE;
}
}
catch (SAXException e) {
// we may be handed any kind of contents... it is normal we fail to parse
return INDETERMINATE;
}
catch (ParserConfigurationException e) {
// some bad thing happened - force this describer to be disabled
String message = "Internal Error: XML parser configuration error during content description for Ant buildfiles"; //$NON-NLS-1$
throw new RuntimeException(message);
}
// Check to see if we matched our criteria.
if (antHandler.hasRootProjectElement()) {
if (antHandler.hasProjectDefaultAttribute() || antHandler.hasTargetElement() || antHandler.hasAntElement()) {
// project and default attribute or project and target element(s)
// or project and top level ant element(s) (classpath, import, macrodef, path, property, taskdef, typedef)
return VALID;
}
// only a top level project element...maybe an Ant buildfile
return INDETERMINATE;
}
return INDETERMINATE;
}
#Override
public int describe(InputStream contents, IContentDescription description) throws IOException {
// call the basic XML describer to do basic recognition
if (super.describe(contents, description) == INVALID) {
return INVALID;
}
// super.describe will have consumed some chars, need to rewind
contents.reset();
// Check to see if we matched our criteria.
return checkCriteria(new InputSource(contents));
}
#Override
public int describe(Reader contents, IContentDescription description) throws IOException {
// call the basic XML describer to do basic recognition
if (super.describe(contents, description) == INVALID) {
return INVALID;
}
// super.describe will have consumed some chars, need to rewind
contents.reset();
// Check to see if we matched our criteria.
return checkCriteria(new InputSource(contents));
}
#Override
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
// do nothing
}
}

Quartz.Net looking in multiple directories

I'm using Quartz.Net 2.X and from what I understand it is scanning the executing directory to look for instances of IJob. Is there a way to define other directories (multiple directories ideally) where to look for "IJobs" ?
There are a few ways, one of them is to write a custom ITypeLoadHelper. In my setup I used the AppDomain.AssemblyResolve event to actually have any dependencies load from my custom folder:
public class CustomFolderTypeLoadHelper : SimpleTypeLoadHelper
{
private Dictionary<string, string> dllPaths;
public override void Initialize()
{
string path = #"C:\"; // quick&dirty way
dllPaths = new DirectoryInfo(path)
.GetFiles("*.dll")
.ToDictionary(fi => Path.GetFileNameWithoutExtension(fi.Name), fi => fi.FullName);
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
dllPaths.ContainsKey(args.Name) ? Assembly.LoadFrom(dllPaths[args.Name]) : null;
base.Initialize();
}
}
Then add this line to quartz.config:
quartz.scheduler.typeLoadHelper.type = MyNamespace.CustomFolderTypeLoadHelper, MyAssembly
Or you could use the method LoadType() on that interface to do it the Quartz way.
In either case, put the assembly with this bootstrapper class in Quartz bin folder.

Xtext: JvmModelInferrer initialize field

I'd like to generate a List field into my class generated from my DSL and initialize it like this:
private List<MyObject> myObjects= Lists.newArrayList();
The only way I know for this, is to append some text to the initializer:
members += appRule.toField("myObjects", appRule.newTypeRef(List, it.newTypeRef(MyObject))) [
initializer = [append('''Lists.newArrayList()''')]
]
However, using this approach the JvmModelInferrer won't import the Guava Strings library, thus will raise compilation issues. Is there any way to overcome this obstacle?
If I understand your issue (as you are referring to the Guava Strings library that is not used in the code :) ), your problem is, that the class reference Lists is not imported.
For such constructs, we have a helper method in EMF-IncQuery that serializes a type reference the same way parameters are serialized. This functionality relies on the injectable TypeReferenceSerializer class.
def referClass(ITreeAppendable appendable, EObject ctx, Class<?> clazz, JvmTypeReference... typeArgs) {
val ref = ctx.newTypeRef(clazz, typeArgs)
if (ref != null) {
appendable.serialize(ref, ctx)
} else {
//Class resolution error - error handling required here
//A fallback to writing out the fqn of the class
appendable.append(clazz.canonicalName)
}
}
def serialize(ITreeAppendable appendable, JvmTypeReference ref, EObject ctx) {
typeReferenceSerializer.serialize(ref, ctx, appendable)
}

How can I selectively apply a VSTemplate?

I am creating a custom VSTemplate for MVC 4 applications for my company that uses a wizard that is comparable to the wizard that appears when you create a new MVC4 application. I have one of two templates I would like to apply when the developer creates a new app of this type as shown here:
Both of those entries correspond to templates that are defined inside my VSIX project under a folder called ProjectTemplates:
My question is, how do I apply the correct template when the wizard runs? I know how to create a vstemplate with multiple projects (using the ProjectCollection node in the vstemplate), but that's not really what I want to do since they will never be deployed together. I see that I can add both vstemplates as Assets to my vsixmanifest file, but I'm not really sure how to apply just one template conditionally.
Thanks!
You'll need to include the files for your "optional" template(s) in sub directories of the the "root" template folder but EXCLUDE them from the TemplateContent Element of the "root" template.
Your IWizard implementation needs to keep a reference to the EnvDTE.DTE object (the first parameter of RunStarted) and use it in the ProjectFinishedGenerating to add the projects to the solution using the template(s) that match what the user selected.
public class SelectTemplatesWizard : IWizard
{
private EnvDTE.DTE _dte = null;
private string _solutionDir = null;
private string _templateDir = null;
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
// Store the reference to the environment for later use
_dte = automationObject as EnvDTE.DTE;
/*
The value of the item in the replacements dictionary for the key
$destinationdirectory$ is populated with the physical location of
the directory (named based on the user entered project name) created
sibling to the solution file.
The solution directory will be this directories parent
when the Type attribute of the VSTemplate element is ProjectGroup
*/
_solutionDir = System.IO.Path.GetDirectoryName(replacementsDictionary["$destinationdirectory$"]);
// customParams[0] is a default custom param that contains the physical location of the template that is currently being applied
_templateDir = System.IO.Path.GetDirectoryName(customParams[0] as string);
}
public void ProjectFinishedGenerating(Project project)
{
int userSelected = 1;
string name= null, projectPath= null, templatePath = null;
switch (userSelected)
{
case 0:
{
name = "Angular";
projectPath = System.IO.Path.Combine(_solutionDir, "Angular");
templatePath = System.IO.Path.Combine(_templateDir , "Angular\Angular.vstemplate");
}
break;
case 1:
{
name = "MVC4";
projectPath = System.IO.Path.Combine(_solutionDir, "MVC4");
templatePath = System.IO.Path.Combine(_templateDir , "MVC4\MVC4.vstemplate");
}
break;
}
_dte.Solution.AddFromTemplate(templatePath, projectPath, name);
}
/* Other IWizard methods excluded for brevity */
}