This is an automated email from the ASF dual-hosted git repository.

elecharny pushed a commit to branch 1.2.X
in repository https://gitbox.apache.org/repos/asf/mina-ftpserver.git


The following commit(s) were added to refs/heads/1.2.X by this push:
     new 4ff3bc9a o Applied Filip Cichy patch on in-memory filesystem with 
minor modifications (mainly a missing @param, static order and a few NL) o 
Bumped up dependencies and plugins o Added missing package-info
4ff3bc9a is described below

commit 4ff3bc9a7898f9a15dc13416d8defbd9ed97dcf6
Author: emmanuel lecharny <[email protected]>
AuthorDate: Wed Mar 4 05:52:15 2026 +0100

    o Applied Filip Cichy patch on in-memory filesystem with minor 
modifications (mainly a missing @param, static order and a few NL)
    o Bumped up dependencies and plugins
    o Added missing package-info
---
 core/pom.xml                                       |   4 +-
 .../inmemoryfs/InMemoryFileSystemFactory.java      |  60 +++
 .../inmemoryfs/impl/InMemoryFileSystemView.java    | 225 +++++++++
 .../inmemoryfs/impl/InMemoryFtpFile.java           | 534 +++++++++++++++++++++
 .../filesystem/inmemoryfs/impl/package-info.java   |  23 +
 .../filesystem/inmemoryfs/package-info.java        |  22 +
 .../inmemory/impl/InMemoryFileSystemViewTest.java  | 101 ++++
 .../inmemory/impl/InMemoryFtpFileTest.java         | 274 +++++++++++
 examples/ftpserver-osgi-ftplet-service/pom.xml     |   2 +-
 examples/ftpserver-osgi-spring-service/pom.xml     |   2 +-
 ftplet-api/pom.xml                                 |   2 +-
 pom.xml                                            |  15 +-
 12 files changed, 1252 insertions(+), 12 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 598e7f44..d89aa62b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -50,7 +50,7 @@
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
-        <version>5.1.4</version>
+        <version>${maven-bundle-plugin-version}</version>
         <extensions>true</extensions>
         <configuration>
           <instructions>
@@ -84,7 +84,7 @@
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>properties-maven-plugin</artifactId>
-        <version>1.2.1</version>
+        <version>1.3.0</version>
         <executions>
           <execution>
             <phase>generate-resources</phase>
diff --git 
a/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/InMemoryFileSystemFactory.java
 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/InMemoryFileSystemFactory.java
new file mode 100644
index 00000000..fd1280b6
--- /dev/null
+++ 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/InMemoryFileSystemFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ftpserver.filesystem.inmemoryfs;
+
+import org.apache.ftpserver.filesystem.inmemoryfs.impl.InMemoryFileSystemView;
+import org.apache.ftpserver.ftplet.FileSystemFactory;
+import org.apache.ftpserver.ftplet.FileSystemView;
+import org.apache.ftpserver.ftplet.FtpException;
+import org.apache.ftpserver.ftplet.User;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Factory class for creating instances of an in-memory file system.
+ * <p>
+ *  * This implementation provides a lightweight and non-persistent file system
+ *  * stored entirely in memory, suitable for testing or temporary data 
storage.
+ * </p>
+ */
+public class InMemoryFileSystemFactory implements FileSystemFactory {
+
+    private static final Map<String, InMemoryFileSystemView> filesystemMap = 
new ConcurrentHashMap<>();
+
+    /**
+     * Create the appropriate user file system view.
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized FileSystemView createFileSystemView(User user) throws 
FtpException {
+        Objects.requireNonNull(user);
+
+        if (filesystemMap.containsKey(user.getName())) {
+            return filesystemMap.get(user.getName());
+        }
+
+        InMemoryFileSystemView view = new InMemoryFileSystemView(user);
+        filesystemMap.put(user.getName(), view);
+
+        return view;
+    }
+}
diff --git 
a/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/InMemoryFileSystemView.java
 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/InMemoryFileSystemView.java
new file mode 100644
index 00000000..f585c96e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/InMemoryFileSystemView.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ftpserver.filesystem.inmemoryfs.impl;
+
+import org.apache.ftpserver.ftplet.FileSystemView;
+import org.apache.ftpserver.ftplet.FtpFile;
+import org.apache.ftpserver.ftplet.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * View representation of the in-memory file system.
+ * <p>
+ * Provides mechanisms to navigate and manage the structure of the in-memory 
file system.
+ * </p>
+ *
+ * <p>
+ * This class is designed to support operations such as listing files, 
managing directories,
+ * and accessing file properties within a non-persistent in-memory environment.
+ * </p>
+ */
+public class InMemoryFileSystemView implements FileSystemView {
+
+    public static final String PARENT_DIRECTORY = "..";
+    public static final String CURRENT_DIRECTORY = ".";
+    public static final String CURRENT = "./";
+    private static final Logger LOG = 
LoggerFactory.getLogger(InMemoryFileSystemView.class);
+    private final InMemoryFtpFile homeDirectory;
+    private InMemoryFtpFile workingDirectory;
+
+    /**
+     * Default constructor for the factory.
+     * @param user The acting User
+     */
+    public InMemoryFileSystemView(User user) {
+        validateUser(user);
+        this.homeDirectory = 
InMemoryFtpFile.createRoot(user.getHomeDirectory());
+        this.workingDirectory = homeDirectory;
+    }
+
+    /**
+     * validates whether the user is eligible to create an instance
+     */
+    private void validateUser(User user) {
+        if (user == null) {
+            throw new IllegalArgumentException("user cannot be null");
+        }
+
+        if (user.getHomeDirectory() == null) {
+            throw new IllegalArgumentException("User home directory cannot be 
null");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FtpFile getHomeDirectory() {
+        return homeDirectory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FtpFile getWorkingDirectory() {
+        return workingDirectory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean changeWorkingDirectory(String path) {
+        LOG.debug("changing working directory from {} to path {}", 
workingDirectory.getAbsolutePath(), path);
+
+        if (isPathNotMatchingCurrentWorkingDirPath(path)) {
+            InMemoryFtpFile mayBeWorkingDirectory;
+
+            if (isAbsolute(path)) {
+                mayBeWorkingDirectory = homeDirectory.find(path);
+            } else {
+                mayBeWorkingDirectory = workingDirectory.find(path);
+            }
+
+            if (mayBeWorkingDirectory.doesExist() && 
mayBeWorkingDirectory.isDirectory()) {
+                workingDirectory = mayBeWorkingDirectory;
+                LOG.debug("changed working directory to {}", 
workingDirectory.getAbsolutePath());
+                return true;
+            }
+
+            LOG.info("changing working directory failed - cannot find file by 
path {}", path);
+
+            return false;
+        }
+
+        //path is same as current working directory
+        return true;
+    }
+
+    /**
+     *
+     * @return true if path is absolute, otherwise false
+     */
+    private boolean isPathNotMatchingCurrentWorkingDirPath(String dir) {
+        return !workingDirectory.getAbsolutePath().equals(dir);
+    }
+
+    /**
+     * Retrieves a file or directory within the in-memory file system based on 
the given path.
+     * <p>
+     * The method resolves paths using the following rules:
+     * <ul>
+     *   <li>If the path starts with "../", it navigates to the parent 
directory.</li>
+     *   <li>If the path starts with "./", it remains in the current 
directory.</li>
+     *   <li>Otherwise, it searches for the file or directory in the current 
directory.</li>
+     * </ul>
+     * </p>
+     *
+     * @param path the path to the file or directory
+     * @param file the current file or directory context
+     * @return the resolved file or directory, or {@code null} if not found
+     */
+    private FtpFile getFile(String path, InMemoryFtpFile file) {
+        path = removeSlash(path);
+
+        if (path.startsWith(PARENT_DIRECTORY)) {
+            if (path.equals(PARENT_DIRECTORY)) {
+                return file.getParent();
+            } else {
+                if (file.getParent() == null) {
+                    return null;
+                }
+
+                return getFile(path.substring(2), (InMemoryFtpFile) 
file.getParent());
+            }
+        } else if (path.startsWith(CURRENT_DIRECTORY)) {
+            if (path.equals(CURRENT_DIRECTORY)) {
+                return file;
+            } else if (path.startsWith(CURRENT)) {
+                if (path.equals(CURRENT)) {
+                    return file;
+                } else {
+                    return getFile(path.substring(2), file);
+                }
+            }
+        }
+
+        return file.find(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FtpFile getFile(String path) {
+        if (isAbsolute(path)) {
+            return getFile(path, homeDirectory);
+        } else {
+            return getFile(path, workingDirectory);
+        }
+    }
+
+    /**
+     * check if path is absolute
+     * @return true if it is, otherwise false
+     */
+    private boolean isAbsolute(String path) {
+        return path.startsWith("/");
+    }
+
+    /**
+     * Removes the leading slash ('/') from the given path, if present.
+     * <p>
+     * For example:
+     * <ul>
+     *   <li>If the input path is "/dir1/dir2", the returned path will be 
"dir1/dir2".</li>
+     *   <li>If the input path is "./dir1/dir2", the returned path remains 
"./dir1/dir2".</li>
+     * </ul>
+     * </p>
+     *
+     * @param path the input path from which to remove the leading slash
+     * @return the path without the leading slash if it starts with '/', 
otherwise the original path
+     */
+    private String removeSlash(String path) {
+        if (path.startsWith("/")) {
+            return path.substring(1);
+        }
+
+        return path;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRandomAccessible() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispose() {
+    }
+}
+
diff --git 
a/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/InMemoryFtpFile.java
 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/InMemoryFtpFile.java
new file mode 100644
index 00000000..7bbf4eae
--- /dev/null
+++ 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/InMemoryFtpFile.java
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ftpserver.filesystem.inmemoryfs.impl;
+
+import org.apache.ftpserver.ftplet.FtpFile;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * This class wraps in-memory file object.
+ */
+public class InMemoryFtpFile implements FtpFile {
+
+    public static final String SLASH = "/";
+    private static final Logger LOG = 
LoggerFactory.getLogger(InMemoryFtpFile.class);
+    private boolean isExist = false;
+    private boolean isFolder = true;
+    private String name;
+    private Map<String, InMemoryFtpFile> children;
+    private InMemoryFtpFile parent;
+    private long lastModify = System.currentTimeMillis();
+
+    private ByteArrayOutputStream data = new ByteArrayOutputStream();
+
+    /**
+     * default constructor
+     * @param name filename
+     * @param parent file, null if file is root
+     * @throws IllegalArgumentException 'name' is empty or null
+     */
+    private InMemoryFtpFile(String name, InMemoryFtpFile parent) {
+        if (name == null || name.isEmpty()) {
+            throw new IllegalArgumentException("null or empty file name");
+        }
+
+        this.name = name;
+        this.children = new ConcurrentHashMap<>();
+        this.parent = parent;
+        LOG.debug("created file in path {}", this.getAbsolutePath());
+    }
+
+    /**
+     * Creates a root file object for the in-memory file system.
+     *
+     * @param name The name of the root directory.
+     * @return The created root file object.
+     */
+    static InMemoryFtpFile createRoot(String name) {
+        InMemoryFtpFile file = new InMemoryFtpFile(name, null);
+        file.isExist = true;
+        file.isFolder = true;
+
+        return file;
+    }
+
+    /**
+     * Creates a leaf file object within the given parent directory.
+     *
+     * @param name The name of the leaf file.
+     * @param parent The parent directory for the leaf file.
+     * @return The created leaf file object.
+     * @throws NullPointerException If the `parent` parameter is null.
+     * @throws IllegalArgumentException  If the `name` parameter is null or 
empty.
+     */
+    private static InMemoryFtpFile createLeaf(String name, InMemoryFtpFile 
parent) {
+        Objects.requireNonNull(parent);
+
+        return new InMemoryFtpFile(name, parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getAbsolutePath() {
+        if (isRoot()) {
+            return name;
+        }
+
+        return parent.getAbsolutePath(name);
+    }
+
+    /**
+     * Returns the absolute path of the given child path within this directory.
+     * This method is used internally to construct the absolute path for child 
files or directories.
+     *
+     * @param childPath The relative path of the child.
+     * @return The absolute path of the child.
+     */
+    private String getAbsolutePath(String childPath) {
+        if (isRoot()) {
+            return getName() + childPath;
+        }
+
+        return parent.getAbsolutePath(name + SLASH + childPath);
+    }
+
+    /**
+     * Check is file root file.
+     * @return <code>true</code> if file is root
+     */
+    private boolean isRoot() {
+        return parent == null;
+    }
+
+    /**
+     * This method returns a list of all child files and directories that 
currently exist within this directory.
+     *
+     * @return A list of child files and directories.
+     */
+    protected List<InMemoryFtpFile> getChildren() {
+        return 
children.values().stream().filter(InMemoryFtpFile::doesExist).collect(Collectors.toList());
+    }
+
+    /**
+     * This method searches for the specified file or directory within the 
current directory and its subdirectories.
+     * It supports relative paths, including ".." for navigating to the parent 
directory.
+     *
+     * @param path The relative path of the file or directory to find.
+     * @return The found file object, or null if the file or directory does 
not exist.
+     */
+    public InMemoryFtpFile find(String path) {
+        if (path.startsWith(SLASH)) {
+            path = path.substring(1);
+        }
+
+        if (path.isEmpty()) {
+            return this;
+        }
+
+        if (isGoUpPath(path)) {
+            if (isRoot()) {
+                return this;
+            }
+
+            return parent.find(path.substring(2));
+        }
+
+        String[] splitPath = path.split(SLASH, 2);
+
+        if (isMultiFolderPath(splitPath)) {
+            InMemoryFtpFile file = this.find(splitPath[0]);
+
+            return file.find(splitPath[1]);
+        } else {
+            InMemoryFtpFile leaf = InMemoryFtpFile.createLeaf(path, this);
+            InMemoryFtpFile existingFile = 
children.putIfAbsent(leaf.getName(), leaf);
+
+            return existingFile == null ? leaf : existingFile;
+        }
+    }
+
+    /**
+     * Checks if the given path represents a multi-level directory path.
+     *
+     * A multi-level directory path contains more than one directory level
+     * (e.g., "dir1/dir2/file.txt").
+     *
+     * @param splitPath An array of path components obtained by splitting the 
original path.
+     * @return `true` if the path contains more than one directory level, 
`false` otherwise.
+     */
+    private boolean isMultiFolderPath(String[] splitPath) {
+        return splitPath.length > 1;
+    }
+
+    /**
+     * Checks if the given path represents a request to navigate to the parent 
directory.
+     *
+     * @param path The path to be checked.
+     * @return `true` if the path starts with ".." (indicating a parent 
directory request), `false` otherwise.
+     */
+    private boolean isGoUpPath(String path) {
+        return path.startsWith("..");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isHidden() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isDirectory() {
+        return isRoot() || isFolder;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isFile() {
+        return !isRoot() && !isFolder;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean doesExist() {
+        return isExist;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isReadable() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isWritable() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRemovable() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getOwnerName() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getGroupName() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getLinkCount() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long getLastModified() {
+        return lastModify;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean setLastModified(long time) {
+        lastModify = time;
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long getSize() {
+        return data.size();
+    }
+
+    /**
+     * There is no physicl file in in-memory filesystem.
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public Object getPhysicalFile() {
+        throw new UnsupportedOperationException("physical file not available 
in memory file system");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean mkdir() {
+        LOG.debug("creating folder at path {}", getAbsolutePath());
+
+        if (isExist && isFolder) {
+            return false;
+        }
+
+        if (!isRoot()) {
+            isExist = true;
+            isFolder = true;
+            parent.mkdir();
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean delete() {
+        LOG.debug("deleting file at path {}", getAbsolutePath());
+        isExist = false;
+        this.children = null;
+        removeFromParent();
+        this.parent = null;
+
+        return true;
+    }
+
+    /**
+     * This method removes the entry for this file or directory from the 
`children` map of its parent.
+     */
+    private void removeFromParent() {
+        parent.children.remove(getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean move(FtpFile destination) {
+        LOG.debug("moving file from {} to {}", getAbsolutePath(), 
destination.getAbsolutePath());
+        removeFromParent();
+        InMemoryFtpFile inMemoryDestination = (InMemoryFtpFile) destination;
+        clone(inMemoryDestination);
+        inMemoryDestination.parent.getChildren().add(inMemoryDestination);
+
+        return true;
+    }
+
+    /**
+     * Creates a shallow copy of this InMemoryFtpFile object into the provided 
target object.
+     *
+     * This method copies the data references, flags, and parent reference 
from the current object to the target object.
+     * It does not create deep copies of child objects or data.
+     *
+     * @param target The target InMemoryFtpFile object to copy the data into.
+     */
+    private void clone(InMemoryFtpFile target) {
+        target.data = this.data;
+        target.isFolder = this.isFolder;
+        target.children = this.children;
+        target.isExist = this.isExist;
+        target.parent = this.parent;
+        target.name = this.name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<? extends FtpFile> listFiles() {
+        if (isDirectory()) {
+            return 
children.values().stream().filter(InMemoryFtpFile::doesExist).collect(Collectors.toList());
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public OutputStream createOutputStream(long offset) {
+        isFolder = false;
+        isExist = true;
+        if (offset != 0) {
+            throw new IllegalArgumentException("offset must be equal 0");
+        }
+
+        return new ByteArrayOutputStreamWrapper(data);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public InputStream createInputStream(long offset) throws IOException {
+        validateInputStream(offset);
+
+        return new ByteArrayInputStream(data.toByteArray());
+    }
+
+   /**
+     * This method checks if the current object is a directory and if the 
specified offset is valid.
+     *
+     * @param offset The desired offset for the input stream.
+     * @throws IOException If the current object is a directory.
+     * @throws IllegalArgumentException If the offset is not equal to 0.
+     */
+    private void validateInputStream(long offset) throws IOException {
+        if (isFolder) {
+            throw new IOException("cannot read bytes from folder");
+        }
+
+        if (offset != 0) {
+            throw new IllegalArgumentException("offset must be equal 0");
+        }
+    }
+
+    /**
+     * Returns the parent directory of this file or directory.
+     *
+     * @return The parent directory object, or null if this is the root 
directory.
+     */
+    public FtpFile getParent() {
+        return parent;
+    }
+
+    /**
+     * Two InMemoryFtpFile objects are considered equal if they have the same 
absolute path.
+     *
+     * @param o The object to compare with.
+     * @return `true` if the objects are equal, `false` otherwise.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        InMemoryFtpFile file = (InMemoryFtpFile) o;
+
+        return Objects.equals(getAbsolutePath(), file.getAbsolutePath());
+    }
+
+    /**
+     * Returns the hash code of this InMemoryFtpFile object.
+     *
+     * The hash code is calculated based on the absolute path of the file or 
directory.
+     *
+     * @return The hash code of this object.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(getAbsolutePath());
+    }
+
+    /**
+     * This class allows the ByteArrayOutputStream to be used as an 
OutputStream
+     * by delegating all write operations to the underlying 
ByteArrayOutputStream.
+     */
+    private static class ByteArrayOutputStreamWrapper extends OutputStream {
+
+        private final ByteArrayOutputStream byteArrayOutputStream;
+
+        /**
+         * Default Constructor.
+         */
+        ByteArrayOutputStreamWrapper(ByteArrayOutputStream 
byteArrayOutputStream) {
+            this.byteArrayOutputStream = byteArrayOutputStream;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void write(int b) {
+            byteArrayOutputStream.write(b);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void write(byte[] b) throws IOException {
+            byteArrayOutputStream.write(b);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void write(byte[] b, int off, int len) {
+            byteArrayOutputStream.write(b, off, len);
+        }
+    }
+}
+
diff --git 
a/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/package-info.java
 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/package-info.java
new file mode 100644
index 00000000..c813b130
--- /dev/null
+++ 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/impl/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
+ */
+package org.apache.ftpserver.filesystem.inmemoryfs.impl;
+
diff --git 
a/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/package-info.java
 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/package-info.java
new file mode 100644
index 00000000..06e15fb9
--- /dev/null
+++ 
b/core/src/main/java/org/apache/ftpserver/filesystem/inmemoryfs/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
+ */
+package org.apache.ftpserver.filesystem.inmemoryfs;
diff --git 
a/core/src/test/java/org/apache/ftpserver/filesystem/inmemory/impl/InMemoryFileSystemViewTest.java
 
b/core/src/test/java/org/apache/ftpserver/filesystem/inmemory/impl/InMemoryFileSystemViewTest.java
new file mode 100644
index 00000000..250cefdc
--- /dev/null
+++ 
b/core/src/test/java/org/apache/ftpserver/filesystem/inmemory/impl/InMemoryFileSystemViewTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ftpserver.filesystem.inmemory.impl;
+
+import junit.framework.TestCase;
+import org.apache.ftpserver.filesystem.inmemoryfs.impl.InMemoryFileSystemView;
+import org.apache.ftpserver.usermanager.impl.BaseUser;
+
+public class InMemoryFileSystemViewTest extends TestCase {
+    private final String HOME_DIR = "/";
+    protected BaseUser user = new BaseUser();
+
+    public void testShouldNotCreateViewIfUserIsNull() {
+        try {
+            new InMemoryFileSystemView(null);
+            fail("user should not be null");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof IllegalArgumentException);
+        }
+    }
+
+    public void testShouldNotCreateViewIfUserIsHasNoHomeDir() {
+        try {
+            user.setHomeDirectory(null);
+            new InMemoryFileSystemView(user);
+            fail("user home directory should not be ull");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof IllegalArgumentException);
+        }
+    }
+
+    public void testHomeDir() throws Exception {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        assertEquals(HOME_DIR, view.getHomeDirectory().getAbsolutePath());
+    }
+
+    public void testWorkingDir() throws Exception {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        assertEquals(HOME_DIR, view.getWorkingDirectory().getAbsolutePath());
+    }
+
+    public void testChangeWorkingDir() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile("1").mkdir();
+
+        view.changeWorkingDirectory("1");
+
+        assertEquals("1", view.getWorkingDirectory().getName());
+    }
+
+    public void testChangeWorkingDirToGoUp() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile("1").mkdir();
+
+        view.changeWorkingDirectory("1");
+
+        assertEquals("1", view.getWorkingDirectory().getName());
+
+        view.changeWorkingDirectory("..");
+        assertEquals("/", view.getWorkingDirectory().getAbsolutePath());
+    }
+
+    public void testChangeWorkingDirToGoUp2Times() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile("1/2").mkdir();
+
+        view.changeWorkingDirectory("1/2");
+
+        assertEquals("2", view.getWorkingDirectory().getName());
+
+        assertEquals("/", view.getFile("../..").getAbsolutePath());
+    }
+
+    public void testGetCurrentDir() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        assertEquals("/", view.getFile(".").getAbsolutePath());
+    }
+
+    private InMemoryFileSystemView getInMemoryFileSystemView() {
+        user.setHomeDirectory(HOME_DIR);
+        InMemoryFileSystemView view = new InMemoryFileSystemView(user);
+        return view;
+    }
+}
+
diff --git 
a/core/src/test/java/org/apache/ftpserver/filesystem/inmemory/impl/InMemoryFtpFileTest.java
 
b/core/src/test/java/org/apache/ftpserver/filesystem/inmemory/impl/InMemoryFtpFileTest.java
new file mode 100644
index 00000000..4c217be8
--- /dev/null
+++ 
b/core/src/test/java/org/apache/ftpserver/filesystem/inmemory/impl/InMemoryFtpFileTest.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ftpserver.filesystem.inmemory.impl;
+
+import junit.framework.TestCase;
+import org.apache.ftpserver.filesystem.inmemoryfs.impl.InMemoryFileSystemView;
+import org.apache.ftpserver.filesystem.inmemoryfs.impl.InMemoryFtpFile;
+import org.apache.ftpserver.ftplet.FtpFile;
+import org.apache.ftpserver.usermanager.impl.BaseUser;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+public class InMemoryFtpFileTest extends TestCase {
+
+    public static final String FILE1_NAME = "1";
+    public static final String FILE2_NAME = "2";
+    public static final String FILE3_NAME = "3";
+    protected BaseUser user = new BaseUser();
+    private final static String HOME_DIR = "/";
+
+    public void testGetAbsolutePath() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        FtpFile file2 = view.getFile(FILE1_NAME + "/" + FILE2_NAME);
+        assertEquals(HOME_DIR + FILE1_NAME, file1.getAbsolutePath());
+        assertEquals(HOME_DIR + FILE1_NAME + "/" + FILE2_NAME, 
file2.getAbsolutePath());
+    }
+
+    public void testFindFile() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+
+        InMemoryFtpFile file = (InMemoryFtpFile) view.getFile(FILE1_NAME);
+        file.mkdir();
+
+        InMemoryFtpFile subFile1 = (InMemoryFtpFile) 
view.getFile(file.getAbsolutePath() + "/" + FILE2_NAME);
+        subFile1.mkdir();
+
+        assertEquals(file, file.find(""));
+        assertEquals(subFile1, file.find(subFile1.getName()));
+    }
+
+    public void testGetName() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file = view.getFile(FILE1_NAME);
+
+        assertEquals(FILE1_NAME, file.getName());
+    }
+
+    public void testIsDirectory() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file = view.getFile(FILE1_NAME);
+
+        assertTrue(file.isDirectory());
+        assertFalse(file.isFile());
+    }
+
+    public void testIsFile() throws Exception{
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file = view.getFile(FILE1_NAME);
+        file.createOutputStream(0).close(); //setting that file is file, not 
directory
+        assertFalse(file.isDirectory());
+        assertTrue(file.isFile());
+    }
+    public void testModificationTime() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+
+        long time = System.currentTimeMillis();
+
+        file1.setLastModified(time);
+
+        assertEquals(time, file1.getLastModified());
+    }
+
+    public void testGetSize() throws Exception {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file = view.getFile(FILE1_NAME);
+
+        OutputStream outputStream = file.createOutputStream(0);
+        String data = "Test";
+        outputStream.write(data.getBytes());
+        outputStream.close();
+
+        assertEquals(data.getBytes().length, file.getSize());
+    }
+
+    public void testDeleteLeaf() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        view.changeWorkingDirectory(FILE1_NAME);
+        FtpFile file2 = view.getFile(FILE1_NAME + "/" + FILE2_NAME);
+        file2.mkdir();
+
+        assertTrue(file2.delete());
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+        assertEquals(0, file1.listFiles().size());
+    }
+
+    public void testDeleteRoot() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        file1.mkdir();
+        view.changeWorkingDirectory(FILE1_NAME);
+
+        assertTrue(file1.delete());
+
+        assertEquals(0, view.getHomeDirectory().listFiles().size());
+    }
+
+    public void testWriteAndRead() throws Exception {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file = view.getFile(FILE1_NAME);
+
+        OutputStream outputStream = file.createOutputStream(0);
+        String input_data = "Hello, World!";
+        outputStream.write(input_data.getBytes());
+        outputStream.close();
+
+        InputStream inputStream = file.createInputStream(0);
+        StringBuilder output_data = new StringBuilder();
+        int data;
+        while ((data = inputStream.read()) != -1) {
+            output_data.append((char) data);
+        }
+        inputStream.close();
+
+        assertEquals(input_data, output_data.toString());
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+    }
+
+    public void testMove() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        FtpFile file2 = view.getFile(FILE2_NAME);
+
+        file1.move(file2);
+
+        assertEquals(file1.getAbsolutePath(), file2.getAbsolutePath());
+        assertEquals(file1.getName(), file2.getName());
+        assertEquals(file1.getSize(), file2.getSize());
+    }
+
+    public void testListFiles() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        file1.mkdir();
+        FtpFile file2 = view.getFile(FILE1_NAME + "/" + FILE2_NAME);
+        file2.mkdir();
+
+        List<FtpFile> result = (List<FtpFile>) file1.listFiles();
+        assertEquals(1, result.size());
+        assertEquals(file2, result.get(0));
+    }
+
+    public void testDeleteWithFilesInside() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        file1.mkdir();
+
+        FtpFile file2 = view.getFile(FILE1_NAME + "/" + FILE2_NAME);
+        file2.mkdir();
+
+        file1.delete();
+        assertEquals(0, view.getHomeDirectory().listFiles().size());
+    }
+
+    public void testMultipleWorkingDirectoryChange() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        FtpFile file1 = view.getFile(FILE1_NAME);
+        file1.mkdir();
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+        FtpFile file2 = view.getFile(FILE1_NAME + "/" + FILE2_NAME);
+        file2.mkdir();
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+        view.changeWorkingDirectory(FILE1_NAME);
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+        view.changeWorkingDirectory(FILE2_NAME);
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+        view.changeWorkingDirectory("..");
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+        view.changeWorkingDirectory("..");
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size());
+        assertEquals(1, file1.listFiles().size());
+        assertEquals(0, file2.listFiles().size());
+    }
+
+    public void testCreateDirectoryByMultiDirectoryPath() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile(FILE1_NAME + "/" + FILE2_NAME).mkdir();
+
+        assertEquals(1, view.getWorkingDirectory().listFiles().size());
+        assertTrue(view.getFile(FILE1_NAME).doesExist() && 
view.getFile(FILE1_NAME).isDirectory());
+
+        view.changeWorkingDirectory(FILE1_NAME);
+
+        assertEquals(1, view.getWorkingDirectory().listFiles().size());
+        assertTrue(view.getFile(FILE2_NAME).doesExist() && 
view.getFile(FILE2_NAME).isDirectory());
+
+        view.changeWorkingDirectory(FILE2_NAME);
+
+        assertEquals(0, view.getWorkingDirectory().listFiles().size());
+    }
+
+    public void testCWDToNotExisingDirectoryNotChangingIt() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile(FILE1_NAME + "/" + FILE2_NAME + "/" + FILE3_NAME).mkdir();
+        view.changeWorkingDirectory(FILE1_NAME + "/" + FILE2_NAME + "/" + 
FILE3_NAME);
+
+        assertEquals(FILE3_NAME, view.getWorkingDirectory().getName());
+
+        view.changeWorkingDirectory("not_exist");
+
+        assertEquals(FILE3_NAME, view.getWorkingDirectory().getName());
+    }
+
+    public void testCreateDirectoryByMultiDirectoryAbsolutePath() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile(HOME_DIR + "/" + FILE1_NAME + "/" + FILE2_NAME + "/" + 
FILE3_NAME).mkdir();
+
+        assertEquals(1, view.getHomeDirectory().listFiles().size()); // home
+        assertEquals(1, 
view.getHomeDirectory().listFiles().get(0).listFiles().size()); //1
+        assertEquals(1, 
view.getHomeDirectory().listFiles().get(0).listFiles().get(0).listFiles().size());
 //2
+        assertEquals(0, 
view.getHomeDirectory().listFiles().get(0).listFiles().get(0).listFiles().get(0).listFiles().size());
 //3
+    }
+
+    public void testGoUpDirectory() {
+        InMemoryFileSystemView view = getInMemoryFileSystemView();
+        view.getFile(HOME_DIR + "/" + FILE1_NAME + "/" + FILE2_NAME + "/" + 
FILE3_NAME).mkdir();
+
+        view.changeWorkingDirectory(HOME_DIR + "/" + FILE1_NAME + "/" + 
FILE2_NAME + "/" + FILE3_NAME);
+        view.changeWorkingDirectory("..");
+        assertEquals(FILE2_NAME, view.getWorkingDirectory().getName());
+    }
+
+    private InMemoryFileSystemView getInMemoryFileSystemView() {
+        user.setHomeDirectory(HOME_DIR);
+        return new InMemoryFileSystemView(user);
+    }
+}
+
diff --git a/examples/ftpserver-osgi-ftplet-service/pom.xml 
b/examples/ftpserver-osgi-ftplet-service/pom.xml
index 9ea08b1b..dd254342 100644
--- a/examples/ftpserver-osgi-ftplet-service/pom.xml
+++ b/examples/ftpserver-osgi-ftplet-service/pom.xml
@@ -51,7 +51,7 @@
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
-        <version>5.1.4</version>
+        <version>${maven-bundle-plugin-version}</version>
         <extensions>true</extensions>
         <configuration>
           <instructions>
diff --git a/examples/ftpserver-osgi-spring-service/pom.xml 
b/examples/ftpserver-osgi-spring-service/pom.xml
index f7d24f5f..93411ca9 100644
--- a/examples/ftpserver-osgi-spring-service/pom.xml
+++ b/examples/ftpserver-osgi-spring-service/pom.xml
@@ -45,7 +45,7 @@
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
-        <version>5.1.4</version>
+        <version>${maven-bundle-plugin-version}</version>
         <extensions>true</extensions>
         <configuration>
           <instructions>
diff --git a/ftplet-api/pom.xml b/ftplet-api/pom.xml
index ecd732a7..560ef840 100644
--- a/ftplet-api/pom.xml
+++ b/ftplet-api/pom.xml
@@ -36,7 +36,7 @@
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
-        <version>5.1.4</version>
+        <version>${maven-bundle-plugin-version}</version>
         <extensions>true</extensions>
         <configuration>
           <instructions>
diff --git a/pom.xml b/pom.xml
index 4324c6ff..acd743e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
   <parent>
     <artifactId>apache</artifactId>
     <groupId>org.apache</groupId>
-    <version>33</version>
+    <version>37</version>
   </parent>
   
   <modelVersion>4.0.0</modelVersion>
@@ -151,14 +151,14 @@
     <!-- doclint>none</doclint-->
 
     <!-- Set versions for depending jars -->
-    <commons.codec.version>1.17.2</commons.codec.version>
-    <commons.net.version>3.11.1</commons.net.version>
+    <commons.codec.version>1.21.0</commons.codec.version>
+    <commons.net.version>3.12.0</commons.net.version>
     <ftpserver.version>${project.version}</ftpserver.version>
     <hsqldb.version>1.8.0.10</hsqldb.version>
     <jcl.over.slf4j.version>1.7.36</jcl.over.slf4j.version>
     <junit.version>4.13.2</junit.version>
-    <log4j.version>2.24.3</log4j.version>
-    <mina.core.version>2.2.4</mina.core.version>
+    <log4j.version>2.25.3</log4j.version>
+    <mina.core.version>2.2.5</mina.core.version>
     <osgi.r4.version>1.0</osgi.r4.version>
     <slf4j.api.version>1.7.36</slf4j.api.version>
     <slf4j.log4j12.version>1.7.36</slf4j.log4j12.version>
@@ -175,7 +175,8 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 
     <!-- Maven plugin version -->
-    <maven-javadoc-plugin-version>3.11.2</maven-javadoc-plugin-version>
+    <maven-javadoc-plugin-version>3.12.0</maven-javadoc-plugin-version>
+    <maven-bundle-plugin-version>6.0.2</maven-bundle-plugin-version>
     <version.site.plugin>4.0.0-M16</version.site.plugin>
   </properties>
 
@@ -353,7 +354,7 @@
         <plugin>
           <groupId>com.github.siom79.japicmp</groupId>
           <artifactId>japicmp-maven-plugin</artifactId>
-          <version>0.23.1</version>
+          <version>0.25.4</version>
           <configuration>
             <oldVersion>
               <dependency>


Reply via email to