{"record":{"id":"b423eaae666639b8","repo":"languagetool-org/languagetool","slug":"constructor-of-filter-class-classname-must-ha-b423ea","errorCode":null,"errorMessage":"Constructor of filter class '${className}' must have exactly one constructor, but it has ${constructors.length}","messagePattern":"Constructor of filter class '(.+?)' must have exactly one constructor, but it has (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilterCreator.java","lineNumber":49,"sourceCode":" */\npublic class RuleFilterCreator {\n  private static final RuleFilterCreator INSTANCE = new RuleFilterCreator();\n\n  private final Map<Class<?>, RuleFilter> myFilterCache = new ConcurrentHashMap<>();\n\n  private RuleFilterCreator() {\n  }\n\n  /**\n   * @param className fully qualified class Name of a class implementing {@link RuleFilter}\n   */\n  public RuleFilter getFilter(String className) {\n    try {\n      Class<?> aClass = JLanguageTool.getClassBroker().forName(className);\n      return myFilterCache.computeIfAbsent(aClass, clazz -> {\n        Constructor<?>[] constructors = clazz.getConstructors();\n        if (constructors.length != 1) {\n          throw new RuntimeException(\"Constructor of filter class '\"\n            + className + \"' must have exactly one constructor, but it has \" + constructors.length);\n        }\n        Constructor<?> constructor = constructors[0];\n        try {\n          if (constructor.getParameterTypes().length != 0) {\n            throw new RuntimeException(\"Constructor of filter class '\" + className + \"' must not have arguments: \" + constructor);\n          }\n          Object filter = constructor.newInstance();\n          if (filter instanceof RuleFilter) {\n            return (RuleFilter) filter;\n          } else {\n            throw new RuntimeException(\"Filter class '\" + className + \"' must implement interface \" + RuleFilter.class.getSimpleName());\n          }\n        } catch (Exception e) {\n          throw new RuntimeException(\"Could not create filter class using constructor \" + constructor, e);\n        }\n      });\n    } catch (ClassNotFoundException e) {","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilterCreator.java#L31-L67","documentation":"RuleFilterCreator.getFilter() loads a RuleFilter class by name and instantiates it reflectively, caching per class. It requires the class to expose exactly one public constructor; if getConstructors() returns more or fewer than one, it throws RuntimeException naming the class and the number of constructors found.","triggerScenarios":"Referencing (via <filter class='...'/>) a filter class that declares multiple public constructors, or zero public constructors (e.g. only package-private ones, or an implicit default blocked by a private/protected-only ctor).","commonSituations":"Adding a second convenience constructor to a filter class while an old no-arg public one still exists; making the constructor package-private during refactoring so getConstructors() returns 0; third-party filter classes with overloaded constructors.","solutions":["Keep exactly one public no-argument constructor in the filter class and remove or make non-public any other constructors","Check the class named in the <filter class='...'> attribute — make sure it is your filter, not a wrapper with extra constructors","If overloads are needed, keep one public no-arg constructor and delegate the overloads through private factory methods"],"exampleFix":"// before\npublic MyFilter() {}\npublic MyFilter(String config) { ... }\n// after\npublic MyFilter() {}\nprivate static MyFilter create(String config) { ... }","handlingStrategy":"validation","validationCode":"Class<?> c = Class.forName(className);\nint n = c.getConstructors().length;\nif (n != 1) throw new IllegalStateException(className + \" must have exactly one public constructor, found \" + n);","typeGuard":"boolean isUsableFilterClass(Class<?> c) { return RuleFilter.class.isAssignableFrom(c) && c.getConstructors().length == 1 && c.getConstructors()[0].getParameterCount() == 0; }","tryCatchPattern":"try {\n  RuleFilter f = new RuleFilterCreator().getFilter(className);\n} catch (RuntimeException e) {\n  if (e.getMessage().contains(\"must have exactly one constructor\")) {\n    throw new ConfigurationException(\"Bad filter class \" + className + \": \" + e.getMessage(), e);\n  }\n  throw e;\n}","preventionTips":["Give every RuleFilter exactly one public no-argument constructor","Keep other constructors package-private or use static factories","Add a unit test that loads all filter classes referenced by your rule XMLs","Check <filter class='...'> names against the classpath package to avoid loading the wrong class"],"tags":["java","languagetool","reflection","constructor"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}