Skip to content
Open
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
15 changes: 14 additions & 1 deletion platform/masterfs/apichanges.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,25 @@
under the License.

-->
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../../nbbuild/javadoctools/apichanges.dtd">
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.1//EN" "../../nbbuild/javadoctools/apichanges-1.1.dtd">
<apichanges>
<apidefs>
<apidef name="masterfs">MasterFileSystem API</apidef>
</apidefs>
<changes>
<change id="disable.hard.locks">
<api name="masterfs"/>
<summary>Adding ability to disable hard-locks as files</summary>
<version major="2" minor="91"/>
<date day="26" month="10" year="2026"/>
<author login="jlahoda"/>
<compatibility binary="compatible" source="compatible" deletion="no"/>
<description>
Adding an ability to disable hard-locks using files on the file system.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add a link to the "a name" reference to https://github.com/apache/netbeans/pull/6639/changes#r4344525386 - it would be something like:

<a href="@TOP@/architecture-summary.html#disable-.lck-files">

See <a href="@TOP@/architecture-summary.html#lookup-disable.LCK.files">the API usecase.</a>
</description>
<class package="org.netbeans.modules.masterfs.fileobjects" name="HardLockRegistry"/>
</change>
<change id="image.for.files">
<api name="masterfs"/>
<summary>Split image handling into UI module</summary>
Expand Down
7 changes: 7 additions & 0 deletions platform/masterfs/arch.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ cases. FileObjects erlier returned from <code>URLMapper</code> or
<code>CvsFileSystem</code> will be now provided by <code>MasterFileSystem</code>.
There will be guaranteed that there won't be more instances of FileObject
addressing one <code>java.ioFile</code>.
<p><api name="disable.LCK.files" group="lookup" type="export" category="devel">
To disable hard locks as on-disk files, remove class <code>org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileBaseHardLockRegistry</code>
from the global <code>Lookup</code>, e.g. by creating file:
<code>META-INF/services/org.netbeans.modules.masterfs.filebasedfs.fileobjects.HardLockRegistry</code> with this content:
<code>#-org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileBaseHardLockRegistry</code>.
This is an API under development and may be replaced or changed in the future.
</api></p>
</answer>


Expand Down
2 changes: 1 addition & 1 deletion platform/masterfs/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ test.config.stableBTD.excludes=\
**/SlowRefreshAndPriorityIOTest.class,\
**/SlowRefreshSuspendableTest.class,\
**/StatFilesTest.class
spec.version.base=2.90.0
spec.version.base=2.91.0

# TODO remove SM usage in tests
test.jms.flags=-Djava.security.manager=allow
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.netbeans.modules.masterfs.filebasedfs.fileobjects;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager;
import org.openide.util.Exceptions;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider(service = HardLockRegistry.class, position = 100)
public final class FileBaseHardLockRegistry implements HardLockRegistry {

public void doHardLock(File hardLock, String absolutePath) throws IOException {
hardLock.getParentFile().mkdirs();
hardLock.createNewFile();
try (OutputStream os = Files.newOutputStream(hardLock.toPath())) {
os.write(absolutePath.getBytes());
}
}

public boolean doHardUnlock(File hardLock) {
return hardLock.delete();
}

public boolean hardLockExists(File hardLock) throws IOException {
return FileChangedManager.getInstance().exists(hardLock);
}

public String getHardLockContent(File hardLock) {
if (FileChangedManager.getInstance().exists(hardLock)) {
try (InputStream is = new FileInputStream(hardLock)) {
byte[] path = new byte[is.available()];
if (path.length > 0 && is.read(path) == path.length) {
return new String(path);
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.netbeans.modules.masterfs.filebasedfs.fileobjects;

import java.io.File;
import java.io.IOException;
import org.openide.util.Lookup;

/** A pluggable hard lock handler.
* @see <a href="@TOP@/architecture-summary.html#lookup-disable.LCK.files">the API usecase.</a>.
*/
public interface HardLockRegistry {

public static HardLockRegistry getInstance() {
return HardLockRegistryInstance.INSTANCE;
}

public void doHardLock(File hardLock, String absolutePath) throws IOException;

public boolean doHardUnlock(File hardLock);

public boolean hardLockExists(File hardLock) throws IOException;

public String getHardLockContent(File hardLock);

}

//init the HardLockRegistry instance lazily:
final class HardLockRegistryInstance {
public static final HardLockRegistry INSTANCE = Lookup.getDefault().lookup(HardLockRegistry.class);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.netbeans.modules.masterfs.filebasedfs.fileobjects;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.util.Exceptions;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider(service = HardLockRegistry.class, position = 1000)
public final class InMemoryHardLockRegistry implements HardLockRegistry {

private static final Logger LOGGER = Logger.getLogger(InMemoryHardLockRegistry.class.getName());
private final ConcurrentHashMap<String, String> hardLocks2Content = new ConcurrentHashMap<>();

private static String getKey(File hardLock) throws IOException {
Path realParentPath = hardLock.toPath().getParent().toRealPath();
return realParentPath.resolve(hardLock.getName()).toString();
}

public void doHardLock(File hardLock, String absolutePath) throws IOException {
hardLocks2Content.put(getKey(hardLock), absolutePath);
}

public boolean doHardUnlock(File hardLock) {
try {
return hardLocks2Content.remove(getKey(hardLock)) != null;
} catch (IOException ex) {
LOGGER.log(Level.FINE, null, ex);
return false;
}
}

public boolean hardLockExists(File hardLock) throws IOException {
return hardLocks2Content.containsKey(getKey(hardLock));
}

public String getHardLockContent(File hardLock) {
try {
return hardLocks2Content.get(getKey(hardLock));
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,11 @@
package org.netbeans.modules.masterfs.filebasedfs.fileobjects;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager;
import org.netbeans.modules.masterfs.filebasedfs.utils.Utils;
import org.openide.filesystems.FileAlreadyLockedException;
import org.openide.filesystems.FileLock;
Expand All @@ -43,8 +38,8 @@
public class LockForFile extends FileLock {

private static final ConcurrentHashMap<String, Namesakes> name2Namesakes = new ConcurrentHashMap<>();
private static final String PREFIX = ".LCK";
private static final String SUFFIX = "~";
static final String PREFIX = ".LCK";
static final String SUFFIX = "~";
private File file;
private File lock;
private boolean valid = false;
Expand Down Expand Up @@ -79,7 +74,7 @@ private static LockForFile registerLock(LockForFile result) throws IOException,
File file = result.getFile();
Namesakes namesakes = new Namesakes();
Namesakes oldNamesakes = name2Namesakes.putIfAbsent(file.getName(), namesakes);
if (oldNamesakes != null) {
if (oldNamesakes != null) {
namesakes = oldNamesakes;
}
if (namesakes.putInstance(file, result) == null) {
Expand Down Expand Up @@ -152,18 +147,17 @@ boolean hardLock() throws IOException {
if (isHardLocked()) {
throw new FileAlreadyLockedException(file.getAbsolutePath());
}

File hardLock = getLock();
hardLock.getParentFile().mkdirs();
hardLock.createNewFile();
try (OutputStream os = Files.newOutputStream(hardLock.toPath())) {
os.write(getFile().getAbsolutePath().getBytes());
return true;
}

HardLockRegistry.getInstance().doHardLock(hardLock, getFile().getAbsolutePath());

return true;
}

/*not private for tests*/
boolean hardUnlock() {
return getLock().delete();
return HardLockRegistry.getInstance().doHardUnlock(getLock());
}

private static synchronized boolean hardUnlockAll() {
Expand Down Expand Up @@ -194,25 +188,9 @@ public File getFile() {
}

public File getHardLock() {
if (FileChangedManager.getInstance().exists(lock)) {
InputStream is = null;
try {
is = new FileInputStream(lock);
byte[] path = new byte[is.available()];
if (path.length > 0 && is.read(path) == path.length) {
return new File(new String(path));
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
String content = HardLockRegistry.getInstance().getHardLockContent(lock);
if (content != null) {
return new File(content);
}
return null;
}
Expand All @@ -238,7 +216,7 @@ public static File getLockFile(File file) {

final String lckName = sb.toString();
final File lck = new File(parentFile, lckName);
return lck;
return lck.getAbsoluteFile();
}

@Override
Expand Down Expand Up @@ -286,12 +264,17 @@ private void hardLock() throws IOException {
if (reference != null) {
LockForFile lockForFile = reference.get();
if (lockForFile != null) {
if (!FileChangedManager.getInstance().exists(lockForFile.getLock())) {
if (!HardLockRegistry.getInstance().hardLockExists(lockForFile.getLock())) {
lockForFile.hardLock();
}
}
}
}
}
}

public static boolean hasActiveLockFileSigns(final String filename) {
return filename.startsWith(PREFIX) && filename.endsWith(SUFFIX);
}

}
Loading
Loading