1818import java .util .List ;
1919import java .util .concurrent .Executors ;
2020
21+ // Client code
2122public class ConFileManager extends DefaultConnection {
2223 private FileOutputStream tempFileOutputStream ;
2324
@@ -32,50 +33,55 @@ public boolean open() throws Exception {
3233
3334 channel .pipeline ().addLast (new ReplayingDecoder <Void >() {
3435 int state = 0 ;
35- // 0 = Opcode, 1 = FileDetails WantsContent, 2 = SaveFile Chunk, 3 = UploadFile Chunk
3636 File currentFile ;
3737
3838 @ Override
3939 protected void decode (ChannelHandlerContext ctx , ByteBuf in , List <Object > outList ) {
4040 try {
4141 if (state == 0 ) {
4242 byte op = in .readByte ();
43- if (op == 0 ) { // Get File Details
43+ if (op == 0 ) {
4444 String path = NettyUtils .readUTF (in );
4545 currentFile = path .isEmpty () ? GD .WORKING_DIR : new File (path );
46- Executors .newSingleThreadExecutor ().submit (() -> sendFileDetailsAndChildren (currentFile ));
46+ // FIX: Use shared executor instead of creating a new thread pool every time
47+ DefaultConnection .exec .submit (() -> sendFileDetailsAndChildren (currentFile ));
4748 if (!currentFile .isDirectory ()) {
4849 state = 1 ;
4950 }
5051 }
51- else if (op == 1 ) { // Create
52+ else if (op == 1 ) {
5253 String path = NettyUtils .readUTF (in );
5354 boolean isDir = in .readBoolean ();
54- Executors . newSingleThreadExecutor () .submit (() -> doCreate (path , isDir ));
55+ DefaultConnection . exec .submit (() -> doCreate (path , isDir ));
5556 }
56- else if (op == 2 ) { // Delete
57+ else if (op == 2 ) {
5758 int count = in .readInt ();
5859 String [] paths = new String [count ];
5960 for (int i = 0 ; i < count ; i ++) paths [i ] = NettyUtils .readUTF (in );
60- Executors . newSingleThreadExecutor () .submit (() -> doDelete (paths ));
61+ DefaultConnection . exec .submit (() -> doDelete (paths ));
6162 }
62- else if (op == 3 ) { // Rename
63+ else if (op == 3 ) {
6364 String path = NettyUtils .readUTF (in );
6465 String newName = NettyUtils .readUTF (in );
65- Executors . newSingleThreadExecutor () .submit (() -> doRename (path , newName ));
66+ DefaultConnection . exec .submit (() -> doRename (path , newName ));
6667 }
6768 else if (op == 4 ) { // Save File
6869 currentFile = new File (NettyUtils .readUTF (in ));
70+ if (currentFile .getParentFile () != null ) currentFile .getParentFile ().mkdirs ();
71+ if (!currentFile .exists ()) currentFile .createNewFile ();
6972 tempFileOutputStream = new FileOutputStream (currentFile );
73+ writeBool (true ); // FIX: Notify the server we are ready to receive the stream
7074 state = 2 ;
7175 }
7276 else if (op == 5 ) { // Receive Upload
7377 currentFile = new File (NettyUtils .readUTF (in ));
78+ if (currentFile .getParentFile () != null ) currentFile .getParentFile ().mkdirs ();
7479 if (!currentFile .exists ()) currentFile .createNewFile ();
7580 tempFileOutputStream = new FileOutputStream (currentFile );
81+ writeBool (true ); // FIX: Notify the server we are ready to receive the stream
7682 state = 3 ;
7783 }
78- else if (op == 6 ) { // Copy/Cut
84+ else if (op == 6 ) {
7985 int count = in .readInt ();
8086 boolean isCopy = in .readBoolean ();
8187 String targetDir = NettyUtils .readUTF (in );
@@ -85,20 +91,28 @@ else if (op == 6) { // Copy/Cut
8591 paths [i ] = NettyUtils .readUTF (in );
8692 isDirs [i ] = in .readBoolean ();
8793 }
88- Executors . newSingleThreadExecutor () .submit (() -> doCopyCut (paths , isDirs , isCopy , targetDir ));
94+ DefaultConnection . exec .submit (() -> doCopyCut (paths , isDirs , isCopy , targetDir ));
8995 }
90- else if (op == 7 ) { // Roots
91- Executors . newSingleThreadExecutor () .submit (ConFileManager .this ::sendRoots );
96+ else if (op == 7 ) {
97+ DefaultConnection . exec .submit (ConFileManager .this ::sendRoots );
9298 }
9399 checkpoint ();
94100 }
95101 else if (state == 1 ) { // Wait for Content Boolean
96102 boolean wantsContent = in .readBoolean ();
97103 if (wantsContent ) {
98- Executors . newSingleThreadExecutor () .submit (() -> {
104+ DefaultConnection . exec .submit (() -> {
99105 try {
100106 sendFileContent (currentFile );
101- } catch (IOException e ) { AL .warn (e ); }
107+ } catch (Exception e ) {
108+ AL .warn ("Failed to send file content:" , e );
109+ // FIX: Send EOF so the server doesn't wait indefinitely and timeout
110+ if (channel != null && channel .isActive ()) {
111+ ByteBuf buf = channel .alloc ().buffer ();
112+ NettyUtils .writeUTF (buf , NettyUtils .EOF );
113+ channel .writeAndFlush (buf );
114+ }
115+ }
102116 });
103117 }
104118 state = 0 ;
@@ -167,7 +181,7 @@ private void sendFileDetailsAndChildren(File file) {
167181 encodeDetails (buf , file );
168182 if (file .isDirectory ()) {
169183 File [] files = file .listFiles ();
170- if (files == null ) buf .writeInt (0 );
184+ if (files == null || files . length == 0 ) buf .writeInt (0 );
171185 else {
172186 buf .writeInt (files .length );
173187 for (File f : files ) if (f .isDirectory ()) encodeDetails (buf , f );
0 commit comments