Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 148 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,157 @@
Example Script Plugin for Fess [![Build Status](https://travis-ci.org/codelibs/fess-script-example.svg?branch=master)](https://travis-ci.org/codelibs/fess-script-example)
==========================
# Fess Script Example Plugin

[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.codelibs.fess/fess-script-example/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.codelibs.fess/fess-script-example)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

A simple example plugin demonstrating how to create custom script engines for [Fess](https://fess.codelibs.org/), the open-source enterprise search server.

## Overview

This is a example plugin for Fess Script.
This plugin provides a minimal implementation of a custom script engine for Fess. The `ExampleEngine` serves as a template and starting point for developers who want to create their own script engines with custom template processing logic.

### Key Features

## Download
- **Simple Implementation**: Demonstrates the basic structure of a Fess script engine
- **Template Pass-through**: Returns template strings unchanged (useful for testing and learning)
- **Full Integration**: Properly integrated with Fess's dependency injection container
- **Comprehensive Tests**: Includes extensive test cases covering edge cases and various scenarios

See [Maven Repository](https://repo1.maven.org/maven2/org/codelibs/fess/fess-script-example/).
## Architecture

The plugin extends Fess's `AbstractScriptEngine` class and implements:

- **Template Evaluation**: Process template strings with parameter maps
- **Engine Identification**: Provides a unique name ("example") for the script engine
- **DI Integration**: Configured via LastaDi container for seamless Fess integration

## Installation

See [Plugin](https://fess.codelibs.org/13.12/admin/plugin-guide.html) of Administration guide.
### Prerequisites

- Fess 15.0.0 or later
- Java 21 or later

### Download

You can download the plugin JAR from [Maven Central](https://repo1.maven.org/maven2/org/codelibs/fess/fess-script-example/).

### Plugin Installation

1. Download the latest `fess-script-example-{version}.jar` from the releases
2. Copy the JAR file to your Fess plugin directory (`$FESS_HOME/app/WEB-INF/plugin/`)
3. Restart Fess server
4. The "example" script engine will be available for use

For detailed installation instructions, see the [Fess Plugin Guide](https://fess.codelibs.org/15.0/admin/plugin-guide.html).

## Usage

Once installed, you can use the "example" script engine in your Fess configuration:

```xml
<component name="exampleScriptEngine" class="org.codelibs.fess.script.example.ExampleEngine"/>
```

The engine will process templates by returning them unchanged, making it useful for:
- Testing script engine integration
- Learning how to implement custom script engines
- As a starting point for more complex implementations

## Development

### Building from Source

```bash
git clone https://github.com/codelibs/fess-script-example.git
cd fess-script-example
mvn clean package
```

### Running Tests

```bash
mvn test
```

The test suite includes 19 comprehensive test cases covering:
- Basic functionality
- Edge cases (null/empty inputs)
- Various data types and special characters
- Multi-line templates and large content
- Instance independence and data integrity

### Code Quality

```bash
# Format code
mvn formatter:format

# Check license headers
mvn license:check

# Generate Javadoc
mvn javadoc:javadoc
```

## Project Structure

```
src/
├── main/java/
│ └── org/codelibs/fess/script/example/
│ └── ExampleEngine.java # Main script engine implementation
├── main/resources/
│ └── fess_se++.xml # DI container configuration
└── test/java/
└── org/codelibs/fess/script/example/
└── ExampleEngineTest.java # Comprehensive test suite
```

## Creating Your Own Script Engine

This project serves as a template for creating custom script engines. To create your own:

1. Fork this repository
2. Rename the `ExampleEngine` class
3. Implement your custom template processing logic in the `evaluate` method
4. Update the `getName` method to return your engine's unique identifier
5. Update the DI configuration in `fess_se++.xml`
6. Add appropriate tests

## Dependencies

- **Fess Framework**: Core search server functionality (provided scope)
- **LastaFlute**: Web application framework with DI container
- **DBFlute**: Database access framework
- **OpenSearch**: Search engine integration (provided scope)
- **UTFlute**: Testing framework for LastaFlute applications

## Contributing

We welcome contributions! Please feel free to submit issues, feature requests, or pull requests.

### Development Guidelines

- Follow the existing code style and formatting
- Add comprehensive tests for new functionality
- Update documentation as needed
- Ensure all tests pass before submitting PRs

## License

This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.

## Support

- **Documentation**: [Fess Official Documentation](https://fess.codelibs.org/)
- **Issues**: [GitHub Issues](https://github.com/codelibs/fess-script-example/issues)
- **Community**: [Fess Community Forum](https://discuss.codelibs.org/)

## Related Projects

- [Fess](https://github.com/codelibs/fess) - The main Fess search server
- [Fess Plugins](https://github.com/codelibs?q=fess-) - Other Fess plugins

---

Developed by [CodeLibs Project](https://github.com/codelibs)
24 changes: 24 additions & 0 deletions src/main/java/org/codelibs/fess/script/example/ExampleEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,37 @@

import org.codelibs.fess.script.AbstractScriptEngine;

/**
* Example script engine implementation that demonstrates how to create custom script engines for Fess.
* This implementation simply returns the template string unchanged without any processing.
*/
public class ExampleEngine extends AbstractScriptEngine {

/**
* Creates a new instance of ExampleEngine.
*/
public ExampleEngine() {
super();
}

/**
Comment on lines +28 to +35
Copy link

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The explicit constructor with super() call is redundant since Java provides a default no-argument constructor that calls super() automatically. Consider removing this constructor unless it serves a specific documentation purpose.

Suggested change
/**
* Creates a new instance of ExampleEngine.
*/
public ExampleEngine() {
super();
}
/**
// Removed redundant constructor and its associated comment.
/**

Copilot uses AI. Check for mistakes.
* Evaluates the given template with the provided parameter map.
* In this example implementation, the template is returned unchanged without any processing.
*
* @param template the template string to evaluate
* @param paramMap the parameter map containing variables for template evaluation
* @return the template string unchanged
*/
@Override
public Object evaluate(final String template, final Map<String, Object> paramMap) {
return template;
}

/**
* Returns the name of this script engine.
*
* @return the name "example" that identifies this script engine
*/
@Override
protected String getName() {
return "example";
Expand Down
124 changes: 124 additions & 0 deletions src/test/java/org/codelibs/fess/script/example/ExampleEngineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,128 @@ public void test_evaluate() {
public void test_getName() {
assertEquals("example", exampleEngine.getName());
}

// Comprehensive test cases for evaluating various input scenarios

public void test_evaluate_withNullTemplate() {
final Map<String, Object> params = new HashMap<>();
assertNull(exampleEngine.evaluate(null, params));
}

public void test_evaluate_withEmptyTemplate() {
final Map<String, Object> params = new HashMap<>();
assertEquals("", exampleEngine.evaluate("", params));
}

public void test_evaluate_withNullParameterMap() {
assertEquals("test", exampleEngine.evaluate("test", null));
}

public void test_evaluate_withEmptyParameterMap() {
final Map<String, Object> params = new HashMap<>();
assertEquals("test", exampleEngine.evaluate("test", params));
}

public void test_evaluate_withPopulatedParameterMap() {
final Map<String, Object> params = new HashMap<>();
params.put("key1", "value1");
params.put("key2", 123);
params.put("key3", true);
assertEquals("template content", exampleEngine.evaluate("template content", params));
}

public void test_evaluate_withSpecialCharacters() {
final Map<String, Object> params = new HashMap<>();
final String template = "Special chars: !@#$%^&*(){}[]|\\:;\"'<>,.?/~`";
assertEquals(template, exampleEngine.evaluate(template, params));
}

public void test_evaluate_withMultilineTemplate() {
final Map<String, Object> params = new HashMap<>();
final String template = "Line 1\nLine 2\nLine 3";
assertEquals(template, exampleEngine.evaluate(template, params));
}

public void test_evaluate_withJapaneseCharacters() {
final Map<String, Object> params = new HashMap<>();
final String template = "こんにちは世界";
assertEquals(template, exampleEngine.evaluate(template, params));
}

public void test_evaluate_withLongTemplate() {
final Map<String, Object> params = new HashMap<>();
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("This is a long template content. ");
}
final String template = sb.toString();
assertEquals(template, exampleEngine.evaluate(template, params));
}

public void test_evaluate_withComplexParameterMap() {
final Map<String, Object> params = new HashMap<>();
params.put("string", "value");
params.put("number", 42);
params.put("boolean", true);
params.put("null", null);
params.put("map", new HashMap<String, String>());
assertEquals("complex template", exampleEngine.evaluate("complex template", params));
}

public void test_getName_consistency() {
assertEquals("example", exampleEngine.getName());
assertEquals("example", exampleEngine.getName());
assertEquals("example", exampleEngine.getName());
}

public void test_getName_returnsConstant() {
final String name1 = exampleEngine.getName();
final String name2 = exampleEngine.getName();
assertSame(name1, name2);
}

public void test_constructor_initialization() {
final ExampleEngine engine = new ExampleEngine();
assertNotNull(engine);
assertEquals("example", engine.getName());
}

public void test_multipleInstances_independence() {
final ExampleEngine engine1 = new ExampleEngine();
final ExampleEngine engine2 = new ExampleEngine();

assertNotSame(engine1, engine2);
assertEquals(engine1.getName(), engine2.getName());

final Map<String, Object> params = new HashMap<>();
assertEquals("test", engine1.evaluate("test", params));
assertEquals("test", engine2.evaluate("test", params));
}

public void test_evaluate_preservesTemplateIntegrity() {
final Map<String, Object> params = new HashMap<>();
params.put("modify", "attempt");

final String originalTemplate = "original template";
final Object result = exampleEngine.evaluate(originalTemplate, params);

assertEquals(originalTemplate, result);
assertTrue(result instanceof String);
assertEquals(String.class, result.getClass());
}

public void test_evaluate_withWhitespaceOnly() {
final Map<String, Object> params = new HashMap<>();
assertEquals(" ", exampleEngine.evaluate(" ", params));
assertEquals(" ", exampleEngine.evaluate(" ", params));
assertEquals("\t", exampleEngine.evaluate("\t", params));
assertEquals("\n", exampleEngine.evaluate("\n", params));
}

public void test_evaluate_returnTypeConsistency() {
final Map<String, Object> params = new HashMap<>();
final Object result = exampleEngine.evaluate("test", params);
assertTrue(result instanceof String);
assertEquals("test", result);
}
}