Class Java

All Implemented Interfaces:
ContextProvider, PropertyChangeSource, Rebindable
Direct Known Subclasses:
DesktopJava

public class Java extends AnahataToolkit
A powerful toolkit for compiling and executing Java code dynamically within the application's JVM. It provides a "hot-reload" capability by using a child-first classloader and supports context-aware execution through the OnTheFlyAgiTool base class.
Author:
anahata
  • Field Details

    • parentFirstClassess

      protected final Set<String> parentFirstClassess
      A set of infrastructure classes that MUST always be loaded by the parent classloader (the ASI engine) to preserve static state and ThreadLocal context. This prevents "Identity Crisis" issues where a child-loaded script cannot access the engine's context.
    • defaultCompilerClasspath

      public String defaultCompilerClasspath
      The base compiler and classloader classpath. Extra entries can be provided at execution time. This serves as the foundation for both dynamic compilation and the child-first classloader logic.
    • classpathPrinter

      protected transient VeryPrettyClassPathPrinter classpathPrinter
      The transient printer used to generate token-optimized classpath manifests. It is recreated lazily to ensure it is always available after deserialization.
    • agiCompiledClasses

      protected Map<String, AgiCompiledClass> agiCompiledClasses
      In-memory compiled classes registered across turns for this AGI session.
    • agiClassLoader

      protected transient AgiClassLoader agiClassLoader
      Session-scoped classloader holding in-memory compiled classes and library URLs.
  • Constructor Details

    • Java

      public Java()
      Default constructor. Initializes the default classpath from the system's "java.class.path" property.
  • Method Details

    • initialize

      public void initialize()
      Performs initial setup for a newly created toolkit instance.

      This method is called exactly once by the ToolManager after the toolkit has been instantiated and registered. It provides a clean hook for toolkits to perform one-time initialization logic that requires the tool environment to be fully configured, such as scanning for project instructions or registering default resources.

      Empty implementation as per architectural refinement. Initialization logic is moved to postActivate() and setDefaultClasspath(String).

      Overrides:
      initialize in class AnahataToolkit
    • rebind

      public void rebind()
      Description copied from class: AnahataToolkit
      Rebinds the toolkit after deserialization.

      This method is called by the framework during session activation to restore transient state, reconnect listeners, or refresh cached project instances. It ensures the propertyChangeSupport is re-initialized.

      Specified by:
      rebind in interface Rebindable
      Overrides:
      rebind in class AnahataToolkit
    • postActivate

      public void postActivate()
      Callback method triggered by the Asi Container once the Agi session has been fully deserialized and associated to the ASI container.

      Synchronizes the classpath printer with the current default classpath after the toolkit has been activated or deserialized.

      Overrides:
      postActivate in class AnahataToolkit
    • getChildrenProviders

      public List<ContextProvider> getChildrenProviders()
      Gets the list of immediate child context providers.

      Dynamically includes all active in-memory AgiCompiledClass instances as child context providers.

      Specified by:
      getChildrenProviders in interface ContextProvider
      Overrides:
      getChildrenProviders in class AnahataToolkit
      Returns:
      The list of children, or an empty list if none.
    • getOrCreateAgiClassLoader

      public AgiClassLoader getOrCreateAgiClassLoader()
      Returns the active session-scoped AgiClassLoader, lazily instantiating it from all accumulated AgiCompiledClass library URLs if not yet created.
      Returns:
      the active AgiClassLoader.
    • resetAgiClassLoader

      public void resetAgiClassLoader()
      Closes and resets the active AgiClassLoader. Required when re-compiling or removing classes to allow the JVM to reload redefined classes on the next execution.
    • getSystemInstructions

      public List<String> getSystemInstructions() throws Exception
      Gets a list of system instruction strings provided by this context provider. These are typically prepended to the conversation as high-level guidance.

      Provides system instructions for runtime Java code compilation, detailing available methods, multi-threading patterns, memory safety, and JVM properties.

      Returns:
      the list of system instruction blocks.
      Throws:
      Exception - if an error occurs while assembling instructions.
    • registerParentFirstClass

      public final void registerParentFirstClass(Class<?> c)
      Registers a class and all its superclasses and interfaces to be loaded by the parent classloader.
      Parameters:
      c - The class to register.
    • getConcreteClassModelShouldExtend

      protected Class<? extends ToolContext> getConcreteClassModelShouldExtend()
      The class the model should extend when generating Agi tools.

      In this base implementation, it returns OnTheFlyAgiTool, which provides the necessary context anchors (log, error, etc.) for the script.

      Returns:
      the base AgiTool class.
    • getDefaultClasspath

      public String getDefaultClasspath()
      Gets the current default classpath used for compilation and class loading.
      Returns:
      The full default classpath string.
    • setDefaultClasspath

      public void setDefaultClasspath(String defaultCompilerClasspath)
      Sets the default classpath for the compiler and classloader.
      Parameters:
      defaultCompilerClasspath - The new default classpath string.
    • getPrettyPrintedDefaultClasspath

      public String getPrettyPrintedDefaultClasspath()
      Returns a token-efficient, pretty-printed version of the default classpath.

      Implementation note: This leverages lexical grouping and version promotion to keep the classpath manifest small.

      Returns:
      The pretty-printed classpath string.
    • getClasspathPrinter

      protected final VeryPrettyClassPathPrinter getClasspathPrinter()
      Gets the lazily-initialized classpath printer.
      Returns:
      The VeryPrettyClassPathPrinter instance.
    • createClassPathPrinter

      protected VeryPrettyClassPathPrinter createClassPathPrinter()
      Factory method to create the specialized classpath printer.
      Returns:
      A new VeryPrettyClassPathPrinter instance.
    • populateMessage

      public void populateMessage(RagMessage ragMessage) throws Exception
      Populates the given RagMessage with dynamic, just-in-time context parts. These parts are appended to the end of the user's prompt (RAG).

      Adds session/container map keys, the abbreviated classpath manifest, in-memory compiled classes held by AgiClassLoader, and available Java compilers and JDKs to the RAG message.

      Parameters:
      ragMessage - the incoming RAG message to populate.
      Throws:
      Exception - if an error occurs during message population.
    • addParentFirstClasses

      public void addParentFirstClasses(List<String> fqns) throws ClassNotFoundException
      Adds a list of class FQNs to the Parent-First ClassLoader guard set, resolving each class on the host classloader and recursively registering its superclasses and interfaces.
      Parameters:
      fqns - List of fully qualified class names (FQNs) to resolve and add to parent-first classes.
      Throws:
      ClassNotFoundException - If any requested class cannot be loaded by the host ClassLoader.
    • removeParentFirstClasses

      public void removeParentFirstClasses(List<String> fqns)
      Removes a list of class FQNs from the Parent-First guard set to allow Child-First hot reloading.
      Parameters:
      fqns - List of fully qualified class names (FQNs) to remove from parent-first classes.
    • getAllAgiCompiledBytecodes

      public Map<String,byte[]> getAllAgiCompiledBytecodes()
      Collects all compiled bytecode maps from active AgiCompiledClass instances.
      Returns:
      map of binary class name to compiled bytecode byte array.
    • getInternalMethods

      public String getInternalMethods(String classFqn) throws Exception
      Inspects the internal helper and context methods of a target class or hierarchy, describing what each method does and whether it requires a captured ToolContext when called from subthreads.
      Parameters:
      classFqn - The fully qualified name of the class to inspect.
      Returns:
      Formatted Markdown summary of the class hierarchy and its annotated internal methods.
      Throws:
      Exception - If the class cannot be found.
    • formatInternalMethods

      public static String formatInternalMethods(@NonNull @NonNull Class<?> clazz)
      Traverses the inheritance hierarchy of a class and formats all declared methods annotated with Internal.
      Parameters:
      clazz - The class to inspect.
      Returns:
      Formatted Markdown report.
    • logClassloading

      public static void logClassloading(String message)
      Emits a high-salience classloading lifecycle log message.

      If called within an active tool execution thread, it logs directly to the active JavaMethodToolResponse so the AI model and developer see classloader events live in the tool response. If called outside a tool execution thread, it logs to SLF4J at INFO level.

      Parameters:
      message - the classloading log message.
    • createReloadingClassLoader

      protected Java.AnahataClassLoader createReloadingClassLoader(List<URL> extraUrls, Map<String,byte[]> compiledClasses)
      Factory method to create an instance of Java.AnahataClassLoader with AgiClassLoader as parent.
      Parameters:
      extraUrls - extra URLs to search child-first.
      compiledClasses - in-memory compiled bytecode map.
      Returns:
      a new Java.AnahataClassLoader.
    • getKnownJdks

      public List<KnownJdk> getKnownJdks()
      Discovers all known JDK installations on the host environment.

      Scans: 1. The currently running JVM (via System.getProperty("java.home")). 2. The JAVA_HOME environment variable. 3. Standard platform JDK directories (/usr/lib/jvm, /Library/Java/JavaVirtualMachines, C:\Program Files\Java, etc.). 4. The javac executable available on the system PATH.

      Subclasses (such as NbJava and IntellijJava) override this method to add IDE-registered platforms and project SDKs.

      Returns:
      a list of discovered KnownJdk instances.
    • findJavacInJdkHome

      public static Path findJavacInJdkHome(Path home)
      Helper to find a javac executable within a candidate JDK home directory.
      Parameters:
      home - candidate JDK home path.
      Returns:
      path to javac binary if found and executable, or null.
    • resolveJavacPath

      public Path resolveJavacPath(String jdkNameOrPath) throws AgiToolException
      Resolves an explicit JDK identifier, name, or path to a javac executable path.
      Parameters:
      jdkNameOrPath - optional name, ID, directory, or direct javac executable path.
      Returns:
      the resolved Path to javac, or null if null/empty string provided.
      Throws:
      AgiToolException - if an explicit identifier or path was specified but could not be found.
    • compile

      public Class<?> compile(String sourceCode, String className, String extraClassPath, String[] compilerOptions, Path javacPath) throws Exception
      Compiles Java source code into a Class object.

      Resolution order: 1. If an explicit javacPath is provided, compiles externally using that binary. 2. If javacPath is null and in-memory JavaCompiler is available, compiles in memory. 3. If javacPath is null and in-memory compiler is NOT available (JRE/JBR), automatically falls back to the first available JDK javac from getKnownJdks().

      Parameters:
      sourceCode - the Java source code to compile.
      className - the simple or fully qualified name of the class.
      extraClassPath - additional classpath entries to include.
      compilerOptions - additional options for the compiler.
      javacPath - optional explicit path to a javac executable.
      Returns:
      the compiled Class object.
      Throws:
      Exception - if compilation or classloading fails.
    • compileInMemory

      public Class<?> compileInMemory(String sourceCode, String className, String extraClassPath, String[] compilerOptions, JavaCompiler compiler) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
      Compiles Java source code in memory using JavaCompiler.
      Parameters:
      sourceCode - the Java source code to compile.
      className - the fully qualified name of the class.
      extraClassPath - additional classpath entries to include.
      compilerOptions - additional options for the Java compiler.
      compiler - the compiler instance.
      Returns:
      the compiled Class object.
      Throws:
      ClassNotFoundException - if class not found.
      NoSuchMethodException - if method not found.
      IllegalAccessException - if access denied.
      InvocationTargetException - if invocation fails.
    • compileWithExternalJavac

      protected Class<?> compileWithExternalJavac(String sourceCode, String className, String extraClassPath, String[] compilerOptions, Path javacPath) throws Exception
      Compiles Java source code using an external javac process and loads the resulting class.

      Robust implementation: 1. Writes all compiler options to an @argfile to completely bypass OS/Windows command-line length limits. 2. Enforces matching --release bytecode compatibility to avoid UnsupportedClassVersionError. 3. Performs atomic cleanup of the scratch directory in a finally block (zero disk leaks).

      Parameters:
      sourceCode - the Java source code.
      className - the simple class name.
      extraClassPath - optional additional classpath entries.
      compilerOptions - optional compiler options.
      javacPath - the absolute path to the javac executable.
      Returns:
      the loaded Class.
      Throws:
      Exception - on compilation or classloading failure.
    • findClassFallbackBytes

      protected byte[] findClassFallbackBytes(String name)
      A hook for subclasses to provide class bytes if the standard loading flow fails.

      This is used by the NetBeans implementation (NbJava) to bridge Multi-Release JAR classes into the memory-based loader.

      Parameters:
      name - The FQN of the class.
      Returns:
      The class bytes, or null if not found.
    • getExtraClassLoaders

      protected List<ClassLoader> getExtraClassLoaders()
      Hook for subclasses to provide extra/sibling classloaders to search if both the child classpath and the parent classloader fail to find a class.
      Returns:
      A list of additional ClassLoaders to query.
    • compileAndExecute

      public Object compileAndExecute(String sourceCode, String extraClassPath, String[] compilerOptions, String jdk) throws Exception
      Compiles and executes a Java class named 'Anahata' on the application's JVM. The class must extend OnTheFlyAgiTool and implement Callable.
      Parameters:
      sourceCode - The Java source code to compile and execute.
      extraClassPath - Additional classpath entries.
      compilerOptions - Additional compiler options.
      jdk - Optional JDK name, ID, or path to a javac executable.
      Returns:
      The result of the execution.
      Throws:
      Exception - if compilation or execution fails.
    • compileAndExecute

      public Object compileAndExecute(String sourceCode, String extraClassPath, String[] compilerOptions) throws Exception
      Convenience overload for compileAndExecute(String, String, String[], String) using default compiler.
      Parameters:
      sourceCode - the source code.
      extraClassPath - additional classpath.
      compilerOptions - compiler options.
      Returns:
      the execution result.
      Throws:
      Exception - on error.
    • getDefaultJavaCompiler

      protected JavaCompiler getDefaultJavaCompiler()
      Overridable method for implementations to decide what compiler to use by default.
      Returns:
      ToolProvider.getSystemJavaCompiler();
    • compileInMemoryBytecodes

      public Map<String,byte[]> compileInMemoryBytecodes(String sourceCode, String classFqn, String extraClassPath, String[] compilerOptions, JavaCompiler compiler) throws Exception
      Compiles source code in memory and extracts all generated class bytecode byte arrays.
      Parameters:
      sourceCode - the Java source code.
      classFqn - the class fully qualified name.
      extraClassPath - optional extra classpath.
      compilerOptions - optional compiler options.
      compiler - the JavaCompiler instance.
      Returns:
      map of class binary names to compiled byte arrays.
      Throws:
      Exception - on compilation error.
    • compileInMemoryBytecodes

      public Map<String,byte[]> compileInMemoryBytecodes(List<AgiClassSource> sources, String extraClassPath, String[] compilerOptions, JavaCompiler compiler) throws Exception
      Compiles multiple source files in memory simultaneously and extracts all generated class bytecode byte arrays.
      Parameters:
      sources - the list of source descriptors.
      extraClassPath - optional extra classpath.
      compilerOptions - optional compiler options.
      compiler - the JavaCompiler instance.
      Returns:
      map of class binary names to compiled byte arrays.
      Throws:
      Exception - on compilation error.
    • compileBytecodes

      public Map<String,byte[]> compileBytecodes(String sourceCode, String classFqn, String extraClassPath, String[] compilerOptions, Path javacPath) throws Exception
      Compiles a modular Java class and returns all resulting bytecode byte arrays.
      Parameters:
      sourceCode - the Java source code.
      classFqn - the class fully qualified name.
      extraClassPath - optional extra classpath.
      compilerOptions - optional compiler options.
      javacPath - optional path to javac executable.
      Returns:
      map of class binary names to compiled byte arrays.
      Throws:
      Exception - on error.
    • compileBytecodes

      public Map<String,byte[]> compileBytecodes(List<AgiClassSource> sources, String extraClassPath, String[] compilerOptions, Path javacPath) throws Exception
      Compiles multiple modular Java classes simultaneously and returns all resulting bytecode byte arrays.
      Parameters:
      sources - the list of source descriptors.
      extraClassPath - optional extra classpath.
      compilerOptions - optional compiler options.
      javacPath - optional path to javac executable.
      Returns:
      map of class binary names to compiled byte arrays.
      Throws:
      Exception - on error.
    • compile

      public String compile(List<AgiClassSource> sources, String extraClassPath, String[] compilerOptions, String jdk) throws Exception
      Compiles one or more modular Java classes into the in-memory classpath of this AGI session without executing them.

      The compiled classes are registered in the session's RAM and automatically included in the RAG message as context providers. Passing multiple sources compiles them simultaneously, allowing mutual or circular dependencies (e.g. Class A references Class B and Class B references Class A).

      Parameters:
      sources - List of Java class source descriptors (each containing fqn and sourceCode).
      extraClassPath - Optional extra classpath entries separated by File.pathSeparator.
      compilerOptions - Optional compiler options.
      jdk - Optional JDK name (from Available JDKs) or explicit path to javac binary.
      Returns:
      Confirmation message with compilation summary.
      Throws:
      Exception - on compilation error.
    • setProviding

      public String setProviding(List<String> fqns, boolean providing)
      Sets whether one or more in-memory compiled classes should have their full source code included in the RAG message.
      Parameters:
      fqns - List of class fully qualified names to update providing status for.
      providing - Whether to include the source code in the RAG message.
      Returns:
      Confirmation message.
    • getAgiClassSources

      public Map<String,String> getAgiClassSources(List<String> classFqns)
      Retrieves the stored Java source code for one or more previously compiled AGI classes. If a requested class is already providing its source code in the RAG message, an error is logged and an informative notice is returned instead to avoid prompt token duplication.
      Parameters:
      classFqns - List of class fully qualified names to retrieve source code for.
      Returns:
      Map of class FQN to its Java source code.
    • removeAgiClasses

      public String removeAgiClasses(List<String> classFqns)
      Removes one or more compiled classes from the AGI in-memory registry.
      Parameters:
      classFqns - List of class fully qualified names to remove.
      Returns:
      Confirmation message with list of removed classes.
    • clearAllAgiClasses

      public String clearAllAgiClasses()
      Clears all compiled classes and sources from the AGI in-memory registry.
      Returns:
      Confirmation message.