I would to use Apache SSHD to create an SFTP server and use SftpFileSystemProvider to mount a remote directory.
I successfully create the virtual file system with SftpFileSystemProvider following the documentation https://github.com/apache/mina-sshd/blob/master/docs/sftp.md#using-sftpfilesystemprovider-to-create-an-sftpfilesystem.
However I'm stuck when mouting remote directory even with the documentation https://github.com/apache/mina-sshd/blob/master/docs/sftp.md#configuring-the-sftpfilesystemprovider. It keeps mouting the root directory instead of the target one.
I tried:
adding the target directory into the sftp uri (not working)
getting new filesystem from path (not working)
Here is a quick example.
object Main:
class Events extends SftpEventListener
class Auth extends PasswordAuthenticator {
override def authenticate(username: String, password: String, session: ServerSession): Boolean = {
true
}
}
class FilesSystem extends VirtualFileSystemFactory {
override def createFileSystem(session: SessionContext): FileSystem = {
val uri = new URI("sftp://xxx:yyy#host/plop")
// val uri = SftpFileSystemProvider.createFileSystemURI("host", 22, "xxx", "yyy")
val fs = Try(FileSystems.newFileSystem(uri, Collections.emptyMap[String, Object](), new SftpFileSystemProvider().getClass().getClassLoader())) match {
case Failure(exception) =>
println("Failed to mount bucket")
println(exception.getMessage)
throw exception
case Success(filesSystem) =>
println("Bucket mounted")
filesSystem
}
//fs.getPath("plop").getFileSystem
fs
}
}
private val fs = new FilesSystem()
private val sftpSubSystem = new SftpSubsystemFactory.Builder().build()
sftpSubSystem.addSftpEventListener(new Events())
private val sshd: SshServer = SshServer.setUpDefaultServer()
sshd.setPort(22)
sshd.setHost("0.0.0.0")
sshd.setSubsystemFactories(Collections.singletonList(sftpSubSystem))
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(Paths.get("hostkey.ser")))
sshd.setShellFactory(new InteractiveProcessShellFactory())
sshd.setCommandFactory(new ScpCommandFactory())
sshd.setFileSystemFactory(fs)
sshd.setPasswordAuthenticator(new Auth())
sshd.setSessionHeartbeat(HeartbeatType.IGNORE, Duration.ofSeconds(30L))
#main def m() = {
sshd.start()
while (sshd.isStarted) {
}
}
end Main
Am I missing something ?
SSHD version 2.8.0, SFTP protocol version 3, Scala3, Java11
I could be wrong, but, I think that these two ...
sshd.setShellFactory(new InteractiveProcessShellFactory())
sshd.setCommandFactory(new ScpCommandFactory())
sshd.setFileSystemFactory(fs)
... are redundant and this ...
private val sftpSubSystem = new SftpSubsystemFactory.Builder().build()
... needs to be made aware of the virtual file system.
I wrote a Spock test to learn how to use JGit. The general idea of the test follows these steps:
Create a "TestRepo" directory
Initialize a new Git repository there ("TestRepo/.git")
Create a new File in the parent directory (TestRepo) and set its text to something to take up space
Call "git status"
(debug) Groovy dump the returned Status object
Assert that the returned Status object has the file listed as untracked.
When I run the below test, it fails. Why?
state.dump() prints
Status#38989dff
diff=org.eclipse.jgit.lib.IndexDiff#72def3cd
clean=true
hasUncommittedChanges=false
Code below:
class GitActionsSpec extends Specification {
public static final ROOT_DIR_PATH = Paths.get(System.getProperty("user.home"), "TestRepo")
public static final ROOT_DIR_STRING = ROOT_DIR_PATH.toString()
public static final GIT_DIR_PATH = ROOT_DIR_PATH.resolve(".git")
#Shared
Git git
/**
* Creates a repository in rootDirPath
*/
def setupSpec() {
if (Files.exists(ROOT_DIR_PATH)) {
deleteDirectory(ROOT_DIR_PATH)
}
Files.createDirectory(ROOT_DIR_PATH)
/*
GitActions.createRepoIn(File parentDirectory) {
return Git.init().setDirectory(f).call()
}
*/
git = GitActions.createRepoIn(ROOT_DIR_PATH.toFile())
assert git.repository.getDirectory().exists()
}
// The actual test
def "A newly-created file should be listed as 'untracked'"() {
given: "A new file"
Path file = ROOT_DIR_PATH.relativize(ROOT_DIR_PATH.resolve("file.txt"))
file.text = "filler text"
assert Files.exists(file)
when: "user requests the status"
Status state = git.status().addPath(file.toString()).call()
then: "Git lists that file as untracked"
println state.dump()
!state.getUntracked().isEmpty()
}
def cleanupSpec() {
git.close()
deleteDirectory(ROOT_DIR_PATH)
}
def deleteDirectory(Path directory) {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
#Override
FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir)
return FileVisitResult.CONTINUE
}
#Override
FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file)
return FileVisitResult.CONTINUE
}
})
}
}
Turns out the issue lied in the code that sets up file.
File's toString() returns A, not B:
A: /home/user/Project/Module/file.txt
B: /home/user/TestRepo/file.txt
My Java migrations currently do not implement the MigrationChecksumProvider interface. I am wondering if they should.
I do not understand what role checksums play in Flyway. Would someone explain this to me, please? Moreover, if I implement MigrationChecksumProvider, how should a checksum be computed for a given migration?
Thank you.
Checksum are used for (accidental) change detection of migrations, which would invalidate the guarantee that the schema can be recreated exactly.
For Java migrations, the sky is the limit as to what you can do. It is therefore your call as to how you want to implement checksums to detect changes.
I think most of the time you want to calculate the checksum based upon the content of the class file in question. We are doing that like this....
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.CRC32;
import org.flywaydb.core.api.migration.jdbc.JdbcMigration;
import org.flywaydb.core.internal.util.FileCopyUtils;
import org.flywaydb.core.internal.util.logging.Log;
import org.flywaydb.core.internal.util.logging.LogFactory;
/**
* Utility class for checksum calculations.
*
*/
public final class Checksums {
private static final Log LOG = LogFactory.getLog(Checksums.class);
private Checksums() {
super(); // singleton
}
/**
* Calculates a checksum based on the given JdbcMigration. It builds the checksum from the byte content of the given
* migration class file using the same {#link CRC32} method as used by
* {#link org.flywaydb.core.internal.resolver.sql.SqlMigrationResolver}
*
* #param migration
* #return the checksum
*/
public static final Integer checksum(JdbcMigration migration) {
Integer checksum = null;
Class<?> migrationClass = migration.getClass();
String migrationClassFilePath = migrationClass.getName().replace(".", System.getProperty("file.separator")) + ".class";
InputStream inputStream = null;
try {
inputStream = ClassLoader.getSystemResourceAsStream(migrationClassFilePath);
byte[] classContent = FileCopyUtils.copyToByteArray(inputStream);
final CRC32 crc32 = new CRC32();
crc32.update(classContent);
checksum = (int) crc32.getValue();
} catch (IOException ioe) {
LOG.error("Problem calculating checksum for class " + migrationClass.getName(), ioe);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ioe) {
LOG.error("Problem closing input stream to " + migrationClassFilePath, ioe);
}
}
}
return checksum;
}
}
Then you can create a base class that looks something like
public abstract class JdbcMigrationWithChecksum implements JdbcMigration, MigrationChecksumProvider {
#Override
public Integer getChecksum() {
return Checksums.checksum(this);
}
}
I am trying to use XMLPullParser but I cannot find any useful tutorials. Based off of the instructions on http://xmlpull.org/ I need to download an implementation of XMLPullParser as a jar file and then add it to my class path. However I cannot find any link to any jar file that works. Does anyone know where I might be able to find a jar file I can download.
Thanks
Ok, here it is for you.
From the official doc :
XmlPull API Implementations:
XNI 2 XmlPull
XPP3/MXP1
KXML2
Here i use KXML2.
Steps :
Download KXML2 jar file from here.
Create a new java project
Create a new class
Right click the java project -> Properties -> Java Build path -> Libraries -> Add external jar's -> Add downloaded kxml2 jar file.
Java code
import java.io.IOException;
import java.io.StringReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
public class XmlPullparserBasic {
public static void main (String args[]) throws XmlPullParserException, IOException
{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
System.out.println("Start document");
} else if(eventType == XmlPullParser.START_TAG) {
System.out.println("Start tag "+xpp.getName());
} else if(eventType == XmlPullParser.END_TAG) {
System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
System.out.println("End document");
}
}
Output :
Hope it helps!
Say I have an enum
public enum E {A,B,C}
Is it possible to add another value, say D, by AspectJ?
After googling around, it seems that there used to be a way to hack the private static field $VALUES, then call the constructor(String, int) by reflection, but seems not working with 1.7 anymore.
Here are several links:
http://www.javaspecialists.eu/archive/Issue161.html (provided by #WimDeblauwe )
and this: http://www.jroller.com/VelkaVrana/entry/modify_enum_with_reflection
Actually, I recommend you to refactor the source code, maybe adding a collection of valid region IDs to each enumeration value. This should be straightforward enough for subsequent merging if you use Git and not some old-school SCM tool like SVN.
Maybe it would even make sense to use a dynamic data structure altogether instead of an enum if it is clear that in the future the list of commands is dynamic. But that should go into the upstream code base. I am sure the devs will accept a good patch or pull request if prepared cleanly.
Remember: Trying to avoid refactoring is usually a bad smell, a symptom of an illness, not a solution. I prefer solutions to symptomatic workarounds. Clean code rules and software craftsmanship attitude demand that.
Having said the above, now here is what you can do. It should work under JDK 7/8 and I found it on Jérôme Kehrli's blog (please be sure to add the bugfix mentioned in one of the comments below the article).
Enum extender utility:
package de.scrum_master.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
public class DynamicEnumExtender {
private static ReflectionFactory reflectionFactory =
ReflectionFactory.getReflectionFactory();
private static void setFailsafeFieldValue(Field field, Object target, Object value)
throws NoSuchFieldException, IllegalAccessException
{
// let's make the field accessible
field.setAccessible(true);
// next we change the modifier in the Field instance to
// not be final anymore, thus tricking reflection into
// letting us modify the static final field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(field);
// blank out the final bit in the modifiers int
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(field, modifiers);
FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
fa.set(target, value);
}
private static void blankField(Class<?> enumClass, String fieldName)
throws NoSuchFieldException, IllegalAccessException
{
for (Field field : Class.class.getDeclaredFields()) {
if (field.getName().contains(fieldName)) {
AccessibleObject.setAccessible(new Field[] { field }, true);
setFailsafeFieldValue(field, enumClass, null);
break;
}
}
}
private static void cleanEnumCache(Class<?> enumClass)
throws NoSuchFieldException, IllegalAccessException
{
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
blankField(enumClass, "enumConstants"); // IBM JDK
}
private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
throws NoSuchMethodException
{
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
parameterTypes[0] = String.class;
parameterTypes[1] = int.class;
System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
return reflectionFactory.newConstructorAccessor(enumClass .getDeclaredConstructor(parameterTypes));
}
private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues)
throws Exception
{
Object[] parms = new Object[additionalValues.length + 2];
parms[0] = value;
parms[1] = Integer.valueOf(ordinal);
System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
}
/**
* Add an enum instance to the enum class given as argument
*
* #param <T> the type of the enum (implicit)
* #param enumType the class of the enum to be modified
* #param enumName the name of the new enum instance to be added to the class
*/
#SuppressWarnings("unchecked")
public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName) {
// 0. Sanity checks
if (!Enum.class.isAssignableFrom(enumType))
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
// 1. Lookup "$VALUES" holder in enum class and get previous enum
// instances
Field valuesField = null;
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
if (field.getName().contains("$VALUES")) {
valuesField = field;
break;
}
}
AccessibleObject.setAccessible(new Field[] { valuesField }, true);
try {
// 2. Copy it
T[] previousValues = (T[]) valuesField.get(enumType);
List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
// 3. build new enum
T newValue = (T) makeEnum(
enumType, // The target enum class
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size(), new Class<?>[] {}, // could be used to pass values to the enum constuctor if needed
new Object[] {} // could be used to pass values to the enum constuctor if needed
);
// 4. add new value
values.add(newValue);
// 5. Set new values field
setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
// 6. Clean enum cache
cleanEnumCache(enumType);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
}
Sample application & enum:
package de.scrum_master.app;
/** In honour of "The Secret of Monkey Island"... ;-) */
public enum Command {
OPEN, CLOSE, PUSH, PULL, WALK_TO, PICK_UP, TALK_TO, GIVE, USE, LOOK_AT, TURN_ON, TURN_OFF
}
package de.scrum_master.app;
public class Server {
public void executeCommand(Command command) {
System.out.println("Executing command " + command);
}
}
package de.scrum_master.app;
public class Client {
private Server server;
public Client(Server server) {
this.server = server;
}
public void issueCommand(String command) {
server.executeCommand(
Command.valueOf(
command.toUpperCase().replace(' ', '_')
)
);
}
public static void main(String[] args) {
Client client = new Client(new Server());
client.issueCommand("use");
client.issueCommand("walk to");
client.issueCommand("undress");
client.issueCommand("sleep");
}
}
Console output with original enum:
Executing command USE
Executing command WALK_TO
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant de.scrum_master.app.Command.UNDRESS
at java.lang.Enum.valueOf(Enum.java:236)
at de.scrum_master.app.Command.valueOf(Command.java:1)
at de.scrum_master.app.Client.issueCommand(Client.java:12)
at de.scrum_master.app.Client.main(Client.java:22)
Now you can either add an aspect with an advice executed after the enum class was loaded or just call this manually in your application before extended enum values are to be used for the first time. Here I am showing how it can be done in an aspect.
Enum extender aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Command;
import de.scrum_master.util.DynamicEnumExtender;
public aspect CommandExtender {
after() : staticinitialization(Command) {
System.out.println(thisJoinPoint);
DynamicEnumExtender.addEnum(Command.class, "UNDRESS");
DynamicEnumExtender.addEnum(Command.class, "SLEEP");
DynamicEnumExtender.addEnum(Command.class, "WAKE_UP");
DynamicEnumExtender.addEnum(Command.class, "DRESS");
}
}
Console output with extended enum:
staticinitialization(de.scrum_master.app.Command.<clinit>)
Executing command USE
Executing command WALK_TO
Executing command UNDRESS
Executing command SLEEP
Et voilà! ;-)