RunWithSCM

37 views
Skip to first unread message

Gavin Mogan

unread,
May 31, 2019, 3:17:48 AM5/31/19
to Jenkins Developers
From a stapler point of view I have a few questions about RunWithSCM and other job interfaces.

1) Is there any reason its not exported? I'm assuming because the classes that actually implement it are exported?
2) Is there anything special about RunWithSCM and its kin that would help me find other useful implementations?

With graphql, when fetching data, as I understand it, you need to say how to handle subclasses.

query {
  allJobs {
    name
    _class
   ... on FreeStyleProject { // freestype stuff }
   ... on OtherClass { other class stuff }
  }
}

This means a client will have to know all the different types. Ideally I'd want to do it do it a bit more generically

query {
  allJobs {
    name
    _class
   ... on FreeStyleProject { // scm stuff (legacy) }
   ... on RunWithSCM { // modern scm stuff  }
  }
}

I know I can hard code a list of interfaces I want to support, but thats not really expandable.

Open to some suggestions, hopefully the scenario is explained properly.

Gavin

Gavin

unread,
May 31, 2019, 4:28:28 AM5/31/19
to jenkin...@googlegroups.com
Ah oops, should have realized runwithscm is run based. Assume everything I asked was run instead of project 

--
You received this message because you are subscribed to a topic in the Google Groups "Jenkins Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jenkinsci-dev/RklYADD2uKc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jenkinsci-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/a8d098b2-2d8c-4fca-9b4f-b5a62f68f962%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jesse Glick

unread,
May 31, 2019, 10:30:29 AM5/31/19
to Jenkins Dev
On Fri, May 31, 2019 at 3:17 AM 'Gavin Mogan' via Jenkins Developers
<jenkin...@googlegroups.com> wrote:
> From a stapler point of view I have a few questions about RunWithSCM and other job interfaces.
>
> 1) Is there any reason its not exported? I'm assuming because the classes that actually implement it are exported?

`@ExportedBean` you mean? Or `@Exported`? These annotations cannot be
used on interfaces or interface methods.

> 2) Is there anything special about RunWithSCM and its kin that would help me find other useful implementations?

Can you be more explicit?

> With graphql, when fetching data, as I understand it, you need to say how to handle subclasses.

Sounds like a question about GraphQL, or its Java bindings (that mixin
interfaces are not well supported), rather than about Jenkins per se.

> ... on FreeStyleProject { // scm stuff (legacy) }
> ... on RunWithSCM { // modern scm stuff }

In this case `AbstractBuild` is retrofitted to implement `RunWithSCM`
so the first clause would not be necessary.

Gavin Mogan

unread,
Jun 3, 2019, 6:25:08 PM6/3/19
to jenkin...@googlegroups.com
> `@ExportedBean` you mean? Or `@Exported`? These annotations cannot be used on interfaces or interface methods.

Well that explains that and certain other related questions

I'm now handling interfaces specifically in a different way (Making a fake graphql "type" that implements the interface as a fallback)

> Sounds like a question about GraphQL,

Its a bit of all 3. Stapler works somewhat in runtime, looking at the annotations of the actual class, graphql you need to define the schema before the query is executed. Ideally even before its sent. When its still being built. So i'm having to shift my designs a bit.

For the most part I have something basic working that gets data. Right now I'm using ExtensionList's TopLevelDescriptor to find all the job types, and then building a graphql schema from those. Its working beautifully, but some of the lower level objects I'm unsure how to handle.

For example. FreeStyleProject/AbstractProject has getAllActions() exported as Actions, which I've gotten working in graphql, but I lose the ability to have per instance exports.

Since action is not an extension point, they are not annotated with @Extension, so Is there a another way to get this? Even a slow way cause i'm just doing it on startup. I've been trying to google for various ways to get all the classes that implement an implementation but not really having much luck. As far as I can tell, stapler doesn't have a master list, it just has a cache it uses when you ask for the model - https://github.com/stapler/stapler/blob/master/core/src/main/java/org/kohsuke/stapler/export/ModelBuilder.java

Okay, since i started writing this, I found out about the reflections class/library.

so I think I can do as follows, which is probably a little expensive (And fairly slow, had to limit it to just hudson/jenkins package names), but is only done on startup:

```
            for (Package pkg :  Package.getPackages()) {
                if (pkg.getName().toLowerCase().contains("jenkins") || pkg.getName().toLowerCase().contains("hudson")) {
                    Reflections reflections = new Reflections(pkg.getName());
                    classQueue.addAll(reflections.getSubTypesOf(clazz));
                }
            }
```

Is there a better way of doing this? Can I find all classes that are @Exported?

Gavin





--
You received this message because you are subscribed to a topic in the Google Groups "Jenkins Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jenkinsci-dev/RklYADD2uKc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jenkinsci-de...@googlegroups.com.

Jesse Glick

unread,
Jun 3, 2019, 8:28:13 PM6/3/19
to Jenkins Dev
On Mon, Jun 3, 2019 at 6:25 PM 'Gavin Mogan' via Jenkins Developers
<jenkin...@googlegroups.com> wrote:
> For example. FreeStyleProject/AbstractProject has getAllActions() exported as Actions, which I've gotten working in graphql, but I lose the ability to have per instance exports.
>
> Since action is not an extension point, they are not annotated with @Extension, so Is there a another way to get this? Even a slow way cause i'm just doing it on startup. I've been trying to google for various ways to get all the classes that implement an implementation

Not sure I follow. You are asking if there is a way to get all
possible `Action` subtypes that might be returned from a
`getAllActions()` method? If so, it is not possible, at least not in
any supported way. The same would apply to pretty much any abstract
type in Jenkins—`Cause`, `CauseOfInterruption`, ad nauseam.

> Can I find all classes that are @Exported?

Without scanning all bytecode in the system? No.

Gavin Mogan

unread,
Jun 8, 2019, 2:08:13 AM6/8/19
to jenkin...@googlegroups.com
This has been a fun challenge. Learning more about jenkins internals and low level java.

I'm thinking i'll put in a hosting req soon, as it is working, but I still have a bunch of things I want to do for quality of life (like make it able to filter builds inside jobs).

If anyone wants to compile and run it, there's a working query in the readme file.

--

This is my solution so far for getting all classes, it does seem to work though a little redundant.


private static Set<Class<?>> _getAllClasses() {
        if (_getAllClassesCache != null) { return _getAllClassesCache; }
        _getAllClassesCache = new HashSet<>();

        List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
        classLoadersList.add(ClasspathHelper.contextClassLoader());
        classLoadersList.add(ClasspathHelper.staticClassLoader());
        if (Jenkins.getInstanceOrNull() != null) {
            classLoadersList.addAll(
                Jenkins.getInstanceOrNull().getPluginManager().getPlugins().stream()
                    .map(i -> i.classLoader).collect(Collectors.toList())
            );
        }

        try {
            final Field f = ClassLoader.class.getDeclaredField("classes");
            boolean oldAccessible = f.isAccessible();
            f.setAccessible(true);
            for (ClassLoader classLoader : classLoadersList) {
                Vector<Class> classes =  (Vector<Class>) f.get(classLoader);
                for (Class clazz : classes) {
                    if (clazz.getName().toLowerCase().contains("jenkins") || clazz.getName().toLowerCase().contains("hudson")) {
                        _getAllClassesCache.add(clazz);
                    }
                }
            }
            f.setAccessible(oldAccessible);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            LOGGER.info("Unable to use classloader, so falling back to reflections: " + e.getMessage());
        }

        Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(
                new SubTypesScanner(
                    false /* don't exclude Object.class */
                ),
                new ResourcesScanner()
            )
            .setUrls(
                ClasspathHelper.forClassLoader(
                    classLoadersList.toArray(
                        new ClassLoader[0]
                    )
                )
            )
            .filterInputsBy(
                new FilterBuilder()
                    .includePackage("com.cloudbees")
                    .includePackage("hudson.model")
                    .includePackage("hudson.plugins")
                    .includePackage("io.jenkins")
                    .includePackage("jenkins.plugins")
                    .includePackage("org.jenkins")
                    .includePackage("org.jenkinsci")
                    .includePackage("org.jenkinsci.plugins.workflow.job")
            )
        );

        _getAllClassesCache.addAll(reflections.getSubTypesOf(Object.class));
        LOGGER.info(
            _getAllClassesCache.stream().filter(i -> i.getName().contains("WorkflowRun")).collect(Collectors.toList()).toString()
        );
        LOGGER.info(
            _getAllClassesCache.stream().filter(i -> i.getName().contains("CauseAction")).collect(Collectors.toList()).toString()
        );
        return _getAllClassesCache;
    }

--
You received this message because you are subscribed to a topic in the Google Groups "Jenkins Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jenkinsci-dev/RklYADD2uKc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jenkinsci-de...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages