wad-lab 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -41,47 +41,85 @@ const frontendOptions = [
41
41
  { name: 'vue-axios-interceptor (Vue 3 + Axios Interceptors + Toast Alerts)', value: 'vue-axios-interceptor' }
42
42
  ];
43
43
 
44
+ const featuredTemplates = [
45
+ {
46
+ key: '1',
47
+ value: 'fullstack-springboot-vue',
48
+ name: '[1] fullstack-springboot-vue (Spring Boot 2-Model API + Selectable Vue Frontend)',
49
+ desc: 'Complete stack: 2-model Spring Boot backend + matching Vue frontend (Router/Pinia/Props)'
50
+ },
51
+ {
52
+ key: '2',
53
+ value: 'springboot-2model-1controller-api',
54
+ name: '[2] springboot-2model-1controller-api (2 Models Dept & Emp, 2 Repos, 1 Service)',
55
+ desc: 'Extensible 2-model Spring Boot architecture (Department + Employee) with full CRUD & PATCH'
56
+ },
57
+ {
58
+ key: '3',
59
+ value: 'springboot-mongo-aggregation-join',
60
+ name: '[3] springboot-mongo-aggregation-join ($lookup JOIN, @Query, @Aggregation)',
61
+ desc: 'MongoDB $lookup multi-collection JOIN, pipeline stages ($match/$group), and @Query filters'
62
+ },
63
+ {
64
+ key: '4',
65
+ value: 'vue-axios-router-pinia-props',
66
+ name: '[4] vue-axios-router-pinia-props (Matching Frontend: Router + Pinia + Props/Emits)',
67
+ desc: 'Matching frontend for 2-model backend: Vue Router + Pinia centralized store + Props/Emits'
68
+ },
69
+ {
70
+ key: '5',
71
+ value: 'vue-axios-router-props',
72
+ name: '[5] vue-axios-router-props (Matching Frontend: Router Views + Props/Emits)',
73
+ desc: 'Matching frontend for 2-model backend: Vue Router pages + reusable components with Props/Emits'
74
+ },
75
+ {
76
+ key: '6',
77
+ value: 'servlet-jdbc',
78
+ name: '[6] servlet-jdbc (Tomcat 9 + MySQL Database Connection via JDBC)',
79
+ desc: 'Tomcat Servlet with MySQL Driver, JDBC connection, SQL statement execution, and HTML table'
80
+ },
81
+ {
82
+ key: '7',
83
+ value: 'servlet-flow',
84
+ name: '[7] servlet-flow (Tomcat 9 Execution Flow with GET/POST Form — No DB)',
85
+ desc: 'Pure servlet without DB: HTML form submission, doGet / doPost processing, and response render'
86
+ }
87
+ ];
88
+
44
89
  const categories = [
45
90
  {
46
91
  id: 'fullstack',
47
92
  title: 'Fullstack Web Applications',
48
- icon: '🌐',
49
93
  desc: 'Integrated Spring Boot + MongoDB + Vue 3 apps'
50
94
  },
51
95
  {
52
96
  id: 'springboot',
53
97
  title: 'Spring Boot Backend APIs',
54
- icon: '🍃',
55
98
  desc: 'REST APIs, MongoDB, Aggregations, Join & MockMvc'
56
99
  },
57
100
  {
58
101
  id: 'servlet',
59
102
  title: 'Java Servlets & Tomcat 9',
60
- icon: '☕',
61
103
  desc: 'Classic Tomcat Servlets, JDBC, Sessions & Web.xml'
62
104
  },
63
105
  {
64
106
  id: 'vue-axios',
65
107
  title: 'Vue 3 + Axios HTTP Variants',
66
- icon: '⚡',
67
108
  desc: 'All 10+ architectural combinations of Vue 3 with Axios'
68
109
  },
69
110
  {
70
111
  id: 'vue-core',
71
112
  title: 'Vue 3 Core Portals & State',
72
- icon: '🎨',
73
113
  desc: 'Router, Pinia, Native Fetch & Complete UI Portals'
74
114
  },
75
115
  {
76
116
  id: 'games',
77
117
  title: 'Interactive Games (JS & Vue)',
78
- icon: '🎮',
79
118
  desc: 'Logic, DOM & State Management Games'
80
119
  },
81
120
  {
82
121
  id: 'html-css',
83
122
  title: 'HTML5 & CSS3 Foundations',
84
- icon: '📄',
85
123
  desc: 'Form Validation, DOM Manipulation & 50 CSS Properties'
86
124
  }
87
125
  ];
@@ -385,7 +423,7 @@ const templates = [
385
423
  const PAGE_SIZE = 12;
386
424
  const templatesRoot = path.join(__dirname, '..', 'templates');
387
425
 
388
- let currentView = 'MAIN'; // 'MAIN' | 'CATEGORY' | 'SEARCH' | 'ALL'
426
+ let currentView = 'MAIN'; // 'MAIN' | 'CATEGORY_LIST' | 'CATEGORY' | 'SEARCH' | 'ALL'
389
427
  let activeCategory = null;
390
428
  let searchQuery = '';
391
429
  let selectedIndex = 0;
@@ -394,19 +432,29 @@ let isPrompting = false;
394
432
 
395
433
  function getActiveItemList() {
396
434
  if (currentView === 'MAIN') {
397
- const list = categories.map((cat, idx) => {
435
+ const list = featuredTemplates.map(f => ({
436
+ type: 'featured',
437
+ id: f.value,
438
+ name: f.name,
439
+ desc: f.desc
440
+ }));
441
+ list.push({ type: 'action', id: 'categories', name: '[C] Browse by Category (7 Groups, 56 Templates)', desc: 'Explore all 56 templates categorized into 7 groups' });
442
+ list.push({ type: 'action', id: 'search', name: '[S] Search by keyword across all 56 templates', desc: 'Type any keyword to find templates instantly' });
443
+ list.push({ type: 'action', id: 'all_view', name: '[L] View all 56 templates (grouped catalog)', desc: 'Browse the entire catalog with section dividers' });
444
+ list.push({ type: 'action', id: 'unpack_all', name: '[A] Unpack all 56 templates in 1-click', desc: 'Copy every single template into target folder' });
445
+ return list;
446
+ }
447
+
448
+ if (currentView === 'CATEGORY_LIST') {
449
+ return categories.map((cat, idx) => {
398
450
  const count = templates.filter(t => t.category === cat.id).length;
399
451
  return {
400
- type: 'category',
452
+ type: 'cat_entry',
401
453
  id: cat.id,
402
- name: `[${idx + 1}] ${cat.icon} ${cat.title} (${count} templates)`,
454
+ name: `[${idx + 1}] ${cat.title} (${count} templates)`,
403
455
  desc: cat.desc
404
456
  };
405
457
  });
406
- list.push({ type: 'action', id: 'search', name: '[S] 🔍 Search templates by keyword...', desc: 'Quickly find templates by typing name or topic' });
407
- list.push({ type: 'action', id: 'all_view', name: '[L] 📋 View all 56 templates (grouped)', desc: 'Browse the full catalog with section headers' });
408
- list.push({ type: 'action', id: 'unpack_all', name: '[A] 📦 Unpack all 56 templates in 1-click', desc: 'Copy all 56 templates into target folder' });
409
- return list;
410
458
  }
411
459
 
412
460
  if (currentView === 'CATEGORY') {
@@ -435,33 +483,59 @@ function renderScreen() {
435
483
 
436
484
  if (currentView === 'MAIN') {
437
485
  console.log('========================================================');
438
- console.log(' 🚀 WAD LAB STARTER TEMPLATES ');
439
- console.log(' 56 Exam-Ready & Lab-Tested Templates ');
486
+ console.log(' WAD LAB STARTER TEMPLATES ');
487
+ console.log(' Exam Quick-Start & 56 Ready Templates ');
440
488
  console.log('========================================================\n');
441
- console.log('Navigate with [UP / DOWN] arrow keys');
442
- console.log('Press [ENTER] or press [1-7] to enter a category');
443
- console.log('Press [S] to Search, [A] to Unpack All, [Ctrl+C] to Exit\n');
489
+ console.log('CORE EXAM STARTERS:');
490
+ console.log(' Press [1-7] for instant pick, or navigate with [UP / DOWN]');
491
+ console.log(' [SPACE] Select | [ENTER] Confirm | [C] Categories | [S] Search | [A] All\n');
444
492
 
445
493
  const items = getActiveItemList();
446
494
  items.forEach((item, idx) => {
447
- if (item.id === 'search') {
448
- console.log(' ----------------------------------------------------');
495
+ if (item.id === 'categories') {
496
+ console.log('\nFULL CATALOG & EXPLORATION:');
449
497
  }
450
498
  const isCursor = idx === selectedIndex;
451
- const prefix = isCursor ? ' 👉 ' : ' ';
499
+ const isChecked = selectedValues.has(item.id);
500
+ const box = item.type === 'featured' ? (isChecked ? '[x] ' : '[ ] ') : '';
501
+ const prefix = isCursor ? ' > ' : ' ';
502
+ console.log(prefix + box + item.name);
503
+ });
504
+
505
+ const activeItem = items[selectedIndex];
506
+ if (activeItem && activeItem.desc) {
507
+ console.log('\n Info: ' + activeItem.desc);
508
+ }
509
+ if (selectedValues.size > 0) {
510
+ console.log(`\n Selected: ${selectedValues.size} template(s) [Press ENTER to proceed]`);
511
+ }
512
+ return;
513
+ }
514
+
515
+ if (currentView === 'CATEGORY_LIST') {
516
+ console.log('========================================================');
517
+ console.log(' Browse Templates by Category (56 Total)');
518
+ console.log('========================================================\n');
519
+ console.log(' Press [1-7] or [ENTER] to open category');
520
+ console.log(' [B / ESC] Back to Main Menu\n');
521
+
522
+ const items = getActiveItemList();
523
+ items.forEach((item, idx) => {
524
+ const isCursor = idx === selectedIndex;
525
+ const prefix = isCursor ? ' > ' : ' ';
452
526
  console.log(prefix + item.name);
453
527
  });
454
528
 
455
529
  const activeItem = items[selectedIndex];
456
530
  if (activeItem && activeItem.desc) {
457
- console.log('\n ℹ️ ' + activeItem.desc);
531
+ console.log('\n Info: ' + activeItem.desc);
458
532
  }
459
533
  return;
460
534
  }
461
535
 
462
536
  if (currentView === 'CATEGORY') {
463
537
  console.log('========================================================');
464
- console.log(` ${activeCategory.icon} ${activeCategory.title}`);
538
+ console.log(` ${activeCategory.title}`);
465
539
  console.log(` ${activeCategory.desc}`);
466
540
  console.log('========================================================\n');
467
541
  console.log(' [SPACE] Select / Deselect (multi-select supported)');
@@ -479,20 +553,20 @@ function renderScreen() {
479
553
  const end = Math.min(start + PAGE_SIZE, total);
480
554
 
481
555
  if (start > 0) {
482
- console.log(` ▲ (${start} more above)`);
556
+ console.log(` ^ (${start} more above)`);
483
557
  }
484
558
 
485
559
  for (let i = start; i < end; i++) {
486
560
  const t = items[i];
487
561
  const isCursor = i === selectedIndex;
488
562
  const isChecked = selectedValues.has(t.value);
489
- const box = isChecked ? '[✓]' : '[ ]';
490
- const prefix = isCursor ? ' 👉 ' : ' ';
563
+ const box = isChecked ? '[x]' : '[ ]';
564
+ const prefix = isCursor ? ' > ' : ' ';
491
565
  console.log(prefix + box + ' ' + t.name);
492
566
  }
493
567
 
494
568
  if (end < total) {
495
- console.log(` ▼ (${total - end} more below)`);
569
+ console.log(` v (${total - end} more below)`);
496
570
  }
497
571
 
498
572
  const selCount = selectedValues.size;
@@ -502,7 +576,7 @@ function renderScreen() {
502
576
 
503
577
  if (currentView === 'SEARCH') {
504
578
  console.log('========================================================');
505
- console.log(' 🔍 Instant Template Search');
579
+ console.log(' Instant Template Search');
506
580
  console.log('========================================================');
507
581
  console.log(` Query: "${searchQuery}" (type letters/numbers, Backspace to delete)`);
508
582
  console.log(' [SPACE] Select | [ENTER] Confirm | [ESC] Back\n');
@@ -510,7 +584,7 @@ function renderScreen() {
510
584
  const items = getActiveItemList();
511
585
  if (!searchQuery.trim()) {
512
586
  console.log(' Start typing to search across all 56 templates...');
513
- console.log(' Try keywords: "mongo", "join", "props", "servlet", "pinia", "game"\n');
587
+ console.log(' Try keywords: "join", "2model", "props", "servlet", "pinia", "mongo"\n');
514
588
  return;
515
589
  }
516
590
 
@@ -528,20 +602,20 @@ function renderScreen() {
528
602
  const end = Math.min(start + PAGE_SIZE, total);
529
603
 
530
604
  if (start > 0) {
531
- console.log(` ▲ (${start} more above)`);
605
+ console.log(` ^ (${start} more above)`);
532
606
  }
533
607
 
534
608
  for (let i = start; i < end; i++) {
535
609
  const t = items[i];
536
610
  const isCursor = i === selectedIndex;
537
611
  const isChecked = selectedValues.has(t.value);
538
- const box = isChecked ? '[✓]' : '[ ]';
539
- const prefix = isCursor ? ' 👉 ' : ' ';
612
+ const box = isChecked ? '[x]' : '[ ]';
613
+ const prefix = isCursor ? ' > ' : ' ';
540
614
  console.log(prefix + box + ' ' + t.name);
541
615
  }
542
616
 
543
617
  if (end < total) {
544
- console.log(` ▼ (${total - end} more below)`);
618
+ console.log(` v (${total - end} more below)`);
545
619
  }
546
620
 
547
621
  console.log(`\n Matches: ${total} | Selected: ${selectedValues.size}`);
@@ -550,7 +624,7 @@ function renderScreen() {
550
624
 
551
625
  if (currentView === 'ALL') {
552
626
  console.log('========================================================');
553
- console.log(' 📋 Full Catalog: All 56 Templates');
627
+ console.log(' Full Catalog: All 56 Templates');
554
628
  console.log('========================================================\n');
555
629
  console.log(' [SPACE] Select / Deselect | [ENTER] Confirm | [ESC] Back\n');
556
630
 
@@ -565,7 +639,7 @@ function renderScreen() {
565
639
  const end = Math.min(start + PAGE_SIZE, total);
566
640
 
567
641
  if (start > 0) {
568
- console.log(` ▲ (${start} more above)`);
642
+ console.log(` ^ (${start} more above)`);
569
643
  }
570
644
 
571
645
  let lastCat = null;
@@ -574,19 +648,19 @@ function renderScreen() {
574
648
  if (t.category !== lastCat) {
575
649
  const catObj = categories.find(c => c.id === t.category);
576
650
  if (catObj) {
577
- console.log(` --- ${catObj.icon} ${catObj.title} ---`);
651
+ console.log(` --- ${catObj.title} ---`);
578
652
  }
579
653
  lastCat = t.category;
580
654
  }
581
655
  const isCursor = i === selectedIndex;
582
656
  const isChecked = selectedValues.has(t.value);
583
- const box = isChecked ? '[✓]' : '[ ]';
584
- const prefix = isCursor ? ' 👉 ' : ' ';
657
+ const box = isChecked ? '[x]' : '[ ]';
658
+ const prefix = isCursor ? ' > ' : ' ';
585
659
  console.log(prefix + box + ' ' + t.name);
586
660
  }
587
661
 
588
662
  if (end < total) {
589
- console.log(` ▼ (${total - end} more below)`);
663
+ console.log(` v (${total - end} more below)`);
590
664
  }
591
665
 
592
666
  console.log(`\n [${selectedIndex + 1}/${total}] Selected: ${selectedValues.size}`);
@@ -650,7 +724,7 @@ function promptFolderName(choices) {
650
724
 
651
725
  if (isAll) {
652
726
  copyDirRecursive(templatesRoot, destDir);
653
- console.log('\n✨ Successfully unpacked ALL 56 templates into: ' + destDir);
727
+ console.log('\nSuccessfully unpacked ALL 56 templates into: ' + destDir);
654
728
  } else {
655
729
  choices.forEach(c => {
656
730
  if (c === 'fullstack-springboot-vue') {
@@ -680,7 +754,7 @@ function promptFolderName(choices) {
680
754
  console.log('Unpacked [' + c + '] into: ' + dest);
681
755
  }
682
756
  });
683
- console.log('\n✨ Successfully unpacked all chosen templates into: ' + destDir);
757
+ console.log('\nSuccessfully unpacked all chosen templates into: ' + destDir);
684
758
  }
685
759
  process.exit(0);
686
760
  });
@@ -748,6 +822,10 @@ function startInteractive() {
748
822
  } else if (key.name === 'down') {
749
823
  selectedIndex = (selectedIndex + 1) % items.length;
750
824
  renderScreen();
825
+ } else if (str === 'c' || str === 'C') {
826
+ currentView = 'CATEGORY_LIST';
827
+ selectedIndex = 0;
828
+ renderScreen();
751
829
  } else if (str === 's' || str === 'S') {
752
830
  currentView = 'SEARCH';
753
831
  searchQuery = '';
@@ -761,15 +839,30 @@ function startInteractive() {
761
839
  renderScreen();
762
840
  } else if (str >= '1' && str <= '7') {
763
841
  const num = parseInt(str, 10);
764
- activeCategory = categories[num - 1];
765
- currentView = 'CATEGORY';
766
- selectedIndex = 0;
767
- renderScreen();
842
+ const target = featuredTemplates[num - 1];
843
+ if (target) {
844
+ promptFolderName([target.value]);
845
+ }
846
+ } else if (key.name === 'space') {
847
+ const item = items[selectedIndex];
848
+ if (item.type === 'featured') {
849
+ if (selectedValues.has(item.id)) {
850
+ selectedValues.delete(item.id);
851
+ } else {
852
+ selectedValues.add(item.id);
853
+ }
854
+ renderScreen();
855
+ }
768
856
  } else if (key.name === 'return' || key.name === 'enter') {
769
857
  const item = items[selectedIndex];
770
- if (item.type === 'category') {
771
- activeCategory = categories.find(c => c.id === item.id);
772
- currentView = 'CATEGORY';
858
+ if (selectedValues.size > 0) {
859
+ promptFolderName(Array.from(selectedValues));
860
+ return;
861
+ }
862
+ if (item.type === 'featured') {
863
+ promptFolderName([item.id]);
864
+ } else if (item.id === 'categories') {
865
+ currentView = 'CATEGORY_LIST';
773
866
  selectedIndex = 0;
774
867
  renderScreen();
775
868
  } else if (item.id === 'search') {
@@ -788,6 +881,36 @@ function startInteractive() {
788
881
  return;
789
882
  }
790
883
 
884
+ // CATEGORY_LIST VIEW INPUT
885
+ if (currentView === 'CATEGORY_LIST') {
886
+ if (key.name === 'escape' || key.name === 'left' || str === 'b' || str === 'B') {
887
+ currentView = 'MAIN';
888
+ selectedIndex = 0;
889
+ renderScreen();
890
+ return;
891
+ }
892
+ if (key.name === 'up') {
893
+ selectedIndex = (selectedIndex - 1 + items.length) % items.length;
894
+ renderScreen();
895
+ } else if (key.name === 'down') {
896
+ selectedIndex = (selectedIndex + 1) % items.length;
897
+ renderScreen();
898
+ } else if (str >= '1' && str <= '7') {
899
+ const num = parseInt(str, 10);
900
+ activeCategory = categories[num - 1];
901
+ currentView = 'CATEGORY';
902
+ selectedIndex = 0;
903
+ renderScreen();
904
+ } else if (key.name === 'return' || key.name === 'enter') {
905
+ const item = items[selectedIndex];
906
+ activeCategory = categories.find(c => c.id === item.id);
907
+ currentView = 'CATEGORY';
908
+ selectedIndex = 0;
909
+ renderScreen();
910
+ }
911
+ return;
912
+ }
913
+
791
914
  // SEARCH VIEW INPUT
792
915
  if (currentView === 'SEARCH') {
793
916
  if (key.name === 'escape') {
@@ -863,7 +986,7 @@ function startInteractive() {
863
986
  // CATEGORY / ALL VIEW INPUT
864
987
  if (currentView === 'CATEGORY' || currentView === 'ALL') {
865
988
  if (key.name === 'escape' || key.name === 'left' || str === 'b' || str === 'B') {
866
- currentView = 'MAIN';
989
+ currentView = currentView === 'CATEGORY' ? 'CATEGORY_LIST' : 'MAIN';
867
990
  selectedIndex = 0;
868
991
  renderScreen();
869
992
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wad-lab",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Interactive CLI for WAD Lab starter templates compatible with Node 12+",
5
5
  "bin": {
6
6
  "wad-lab": "bin/index.js"
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
3
+ # Usage:
4
+ # ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
5
+ # ./script.sh -d (runs in background daemon mode)
6
+ # ./script.sh [app-dir] [-d]
7
+ set -euo pipefail
8
+
9
+ DAEMON_MODE=false
10
+ APP_ARG=""
11
+ for arg in "$@"; do
12
+ if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
13
+ DAEMON_MODE=true
14
+ elif [ -z "$APP_ARG" ]; then
15
+ APP_ARG="$arg"
16
+ fi
17
+ done
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
21
+ APP_NAME="$(basename "$APP_DIR")"
22
+
23
+ # ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
24
+ TOMCAT_HOME="${CATALINA_HOME:-}"
25
+ if [ -z "$TOMCAT_HOME" ]; then
26
+ for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
27
+ if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
28
+ TOMCAT_HOME="$d"
29
+ break
30
+ fi
31
+ done
32
+ fi
33
+
34
+ [ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
35
+ export CATALINA_HOME="$TOMCAT_HOME"
36
+ chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
37
+ echo "Tomcat : $TOMCAT_HOME"
38
+ echo "App : $APP_NAME ($APP_DIR)"
39
+
40
+ # ---- stop Tomcat if it's already running (ignore errors)
41
+ "$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
42
+ sleep 2
43
+
44
+ # ---- copy the app into webapps (fresh copy, remove old exploded copy)
45
+ DEST="$TOMCAT_HOME/webapps/$APP_NAME"
46
+ rm -rf "$DEST"
47
+ mkdir -p "$DEST"
48
+ cp -r "$APP_DIR"/. "$DEST"/
49
+
50
+ # ---- compile servlets (if any .java files exist)
51
+ SRC_DIR="$DEST/WEB-INF/src"
52
+ [ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
53
+ mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
54
+ if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
55
+ mkdir -p "$DEST/WEB-INF/classes"
56
+ if command -v cygpath >/dev/null 2>&1; then
57
+ CP_PARTS=()
58
+ for j in "$TOMCAT_HOME"/lib/*.jar; do
59
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
60
+ done
61
+ if [ -d "$DEST/WEB-INF/lib" ]; then
62
+ for j in "$DEST"/WEB-INF/lib/*.jar; do
63
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
64
+ done
65
+ fi
66
+ CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
67
+ else
68
+ CP="$TOMCAT_HOME/lib/*"
69
+ [ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
70
+ fi
71
+ echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
72
+ javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
73
+ else
74
+ echo "No .java files found, skipping compile."
75
+ fi
76
+
77
+ # ---- start Tomcat
78
+ if [ "$DAEMON_MODE" = true ]; then
79
+ "$TOMCAT_HOME/bin/startup.sh"
80
+ echo
81
+ echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
82
+ echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
83
+ else
84
+ echo
85
+ echo "Started. Open: http://localhost:8080/$APP_NAME/"
86
+ echo "Press Ctrl+C to stop."
87
+ echo
88
+ exec "$TOMCAT_HOME/bin/catalina.sh" run
89
+ fi
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
3
+ # Usage:
4
+ # ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
5
+ # ./script.sh -d (runs in background daemon mode)
6
+ # ./script.sh [app-dir] [-d]
7
+ set -euo pipefail
8
+
9
+ DAEMON_MODE=false
10
+ APP_ARG=""
11
+ for arg in "$@"; do
12
+ if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
13
+ DAEMON_MODE=true
14
+ elif [ -z "$APP_ARG" ]; then
15
+ APP_ARG="$arg"
16
+ fi
17
+ done
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
21
+ APP_NAME="$(basename "$APP_DIR")"
22
+
23
+ # ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
24
+ TOMCAT_HOME="${CATALINA_HOME:-}"
25
+ if [ -z "$TOMCAT_HOME" ]; then
26
+ for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
27
+ if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
28
+ TOMCAT_HOME="$d"
29
+ break
30
+ fi
31
+ done
32
+ fi
33
+
34
+ [ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
35
+ export CATALINA_HOME="$TOMCAT_HOME"
36
+ chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
37
+ echo "Tomcat : $TOMCAT_HOME"
38
+ echo "App : $APP_NAME ($APP_DIR)"
39
+
40
+ # ---- stop Tomcat if it's already running (ignore errors)
41
+ "$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
42
+ sleep 2
43
+
44
+ # ---- copy the app into webapps (fresh copy, remove old exploded copy)
45
+ DEST="$TOMCAT_HOME/webapps/$APP_NAME"
46
+ rm -rf "$DEST"
47
+ mkdir -p "$DEST"
48
+ cp -r "$APP_DIR"/. "$DEST"/
49
+
50
+ # ---- compile servlets (if any .java files exist)
51
+ SRC_DIR="$DEST/WEB-INF/src"
52
+ [ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
53
+ mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
54
+ if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
55
+ mkdir -p "$DEST/WEB-INF/classes"
56
+ if command -v cygpath >/dev/null 2>&1; then
57
+ CP_PARTS=()
58
+ for j in "$TOMCAT_HOME"/lib/*.jar; do
59
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
60
+ done
61
+ if [ -d "$DEST/WEB-INF/lib" ]; then
62
+ for j in "$DEST"/WEB-INF/lib/*.jar; do
63
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
64
+ done
65
+ fi
66
+ CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
67
+ else
68
+ CP="$TOMCAT_HOME/lib/*"
69
+ [ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
70
+ fi
71
+ echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
72
+ javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
73
+ else
74
+ echo "No .java files found, skipping compile."
75
+ fi
76
+
77
+ # ---- start Tomcat
78
+ if [ "$DAEMON_MODE" = true ]; then
79
+ "$TOMCAT_HOME/bin/startup.sh"
80
+ echo
81
+ echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
82
+ echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
83
+ else
84
+ echo
85
+ echo "Started. Open: http://localhost:8080/$APP_NAME/"
86
+ echo "Press Ctrl+C to stop."
87
+ echo
88
+ exec "$TOMCAT_HOME/bin/catalina.sh" run
89
+ fi
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
3
+ # Usage:
4
+ # ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
5
+ # ./script.sh -d (runs in background daemon mode)
6
+ # ./script.sh [app-dir] [-d]
7
+ set -euo pipefail
8
+
9
+ DAEMON_MODE=false
10
+ APP_ARG=""
11
+ for arg in "$@"; do
12
+ if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
13
+ DAEMON_MODE=true
14
+ elif [ -z "$APP_ARG" ]; then
15
+ APP_ARG="$arg"
16
+ fi
17
+ done
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
21
+ APP_NAME="$(basename "$APP_DIR")"
22
+
23
+ # ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
24
+ TOMCAT_HOME="${CATALINA_HOME:-}"
25
+ if [ -z "$TOMCAT_HOME" ]; then
26
+ for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
27
+ if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
28
+ TOMCAT_HOME="$d"
29
+ break
30
+ fi
31
+ done
32
+ fi
33
+
34
+ [ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
35
+ export CATALINA_HOME="$TOMCAT_HOME"
36
+ chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
37
+ echo "Tomcat : $TOMCAT_HOME"
38
+ echo "App : $APP_NAME ($APP_DIR)"
39
+
40
+ # ---- stop Tomcat if it's already running (ignore errors)
41
+ "$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
42
+ sleep 2
43
+
44
+ # ---- copy the app into webapps (fresh copy, remove old exploded copy)
45
+ DEST="$TOMCAT_HOME/webapps/$APP_NAME"
46
+ rm -rf "$DEST"
47
+ mkdir -p "$DEST"
48
+ cp -r "$APP_DIR"/. "$DEST"/
49
+
50
+ # ---- compile servlets (if any .java files exist)
51
+ SRC_DIR="$DEST/WEB-INF/src"
52
+ [ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
53
+ mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
54
+ if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
55
+ mkdir -p "$DEST/WEB-INF/classes"
56
+ if command -v cygpath >/dev/null 2>&1; then
57
+ CP_PARTS=()
58
+ for j in "$TOMCAT_HOME"/lib/*.jar; do
59
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
60
+ done
61
+ if [ -d "$DEST/WEB-INF/lib" ]; then
62
+ for j in "$DEST"/WEB-INF/lib/*.jar; do
63
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
64
+ done
65
+ fi
66
+ CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
67
+ else
68
+ CP="$TOMCAT_HOME/lib/*"
69
+ [ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
70
+ fi
71
+ echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
72
+ javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
73
+ else
74
+ echo "No .java files found, skipping compile."
75
+ fi
76
+
77
+ # ---- start Tomcat
78
+ if [ "$DAEMON_MODE" = true ]; then
79
+ "$TOMCAT_HOME/bin/startup.sh"
80
+ echo
81
+ echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
82
+ echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
83
+ else
84
+ echo
85
+ echo "Started. Open: http://localhost:8080/$APP_NAME/"
86
+ echo "Press Ctrl+C to stop."
87
+ echo
88
+ exec "$TOMCAT_HOME/bin/catalina.sh" run
89
+ fi
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
3
+ # Usage:
4
+ # ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
5
+ # ./script.sh -d (runs in background daemon mode)
6
+ # ./script.sh [app-dir] [-d]
7
+ set -euo pipefail
8
+
9
+ DAEMON_MODE=false
10
+ APP_ARG=""
11
+ for arg in "$@"; do
12
+ if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
13
+ DAEMON_MODE=true
14
+ elif [ -z "$APP_ARG" ]; then
15
+ APP_ARG="$arg"
16
+ fi
17
+ done
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
21
+ APP_NAME="$(basename "$APP_DIR")"
22
+
23
+ # ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
24
+ TOMCAT_HOME="${CATALINA_HOME:-}"
25
+ if [ -z "$TOMCAT_HOME" ]; then
26
+ for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
27
+ if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
28
+ TOMCAT_HOME="$d"
29
+ break
30
+ fi
31
+ done
32
+ fi
33
+
34
+ [ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
35
+ export CATALINA_HOME="$TOMCAT_HOME"
36
+ chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
37
+ echo "Tomcat : $TOMCAT_HOME"
38
+ echo "App : $APP_NAME ($APP_DIR)"
39
+
40
+ # ---- stop Tomcat if it's already running (ignore errors)
41
+ "$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
42
+ sleep 2
43
+
44
+ # ---- copy the app into webapps (fresh copy, remove old exploded copy)
45
+ DEST="$TOMCAT_HOME/webapps/$APP_NAME"
46
+ rm -rf "$DEST"
47
+ mkdir -p "$DEST"
48
+ cp -r "$APP_DIR"/. "$DEST"/
49
+
50
+ # ---- compile servlets (if any .java files exist)
51
+ SRC_DIR="$DEST/WEB-INF/src"
52
+ [ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
53
+ mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
54
+ if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
55
+ mkdir -p "$DEST/WEB-INF/classes"
56
+ if command -v cygpath >/dev/null 2>&1; then
57
+ CP_PARTS=()
58
+ for j in "$TOMCAT_HOME"/lib/*.jar; do
59
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
60
+ done
61
+ if [ -d "$DEST/WEB-INF/lib" ]; then
62
+ for j in "$DEST"/WEB-INF/lib/*.jar; do
63
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
64
+ done
65
+ fi
66
+ CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
67
+ else
68
+ CP="$TOMCAT_HOME/lib/*"
69
+ [ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
70
+ fi
71
+ echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
72
+ javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
73
+ else
74
+ echo "No .java files found, skipping compile."
75
+ fi
76
+
77
+ # ---- start Tomcat
78
+ if [ "$DAEMON_MODE" = true ]; then
79
+ "$TOMCAT_HOME/bin/startup.sh"
80
+ echo
81
+ echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
82
+ echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
83
+ else
84
+ echo
85
+ echo "Started. Open: http://localhost:8080/$APP_NAME/"
86
+ echo "Press Ctrl+C to stop."
87
+ echo
88
+ exec "$TOMCAT_HOME/bin/catalina.sh" run
89
+ fi
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
3
+ # Usage:
4
+ # ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
5
+ # ./script.sh -d (runs in background daemon mode)
6
+ # ./script.sh [app-dir] [-d]
7
+ set -euo pipefail
8
+
9
+ DAEMON_MODE=false
10
+ APP_ARG=""
11
+ for arg in "$@"; do
12
+ if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
13
+ DAEMON_MODE=true
14
+ elif [ -z "$APP_ARG" ]; then
15
+ APP_ARG="$arg"
16
+ fi
17
+ done
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
21
+ APP_NAME="$(basename "$APP_DIR")"
22
+
23
+ # ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
24
+ TOMCAT_HOME="${CATALINA_HOME:-}"
25
+ if [ -z "$TOMCAT_HOME" ]; then
26
+ for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
27
+ if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
28
+ TOMCAT_HOME="$d"
29
+ break
30
+ fi
31
+ done
32
+ fi
33
+
34
+ [ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
35
+ export CATALINA_HOME="$TOMCAT_HOME"
36
+ chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
37
+ echo "Tomcat : $TOMCAT_HOME"
38
+ echo "App : $APP_NAME ($APP_DIR)"
39
+
40
+ # ---- stop Tomcat if it's already running (ignore errors)
41
+ "$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
42
+ sleep 2
43
+
44
+ # ---- copy the app into webapps (fresh copy, remove old exploded copy)
45
+ DEST="$TOMCAT_HOME/webapps/$APP_NAME"
46
+ rm -rf "$DEST"
47
+ mkdir -p "$DEST"
48
+ cp -r "$APP_DIR"/. "$DEST"/
49
+
50
+ # ---- compile servlets (if any .java files exist)
51
+ SRC_DIR="$DEST/WEB-INF/src"
52
+ [ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
53
+ mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
54
+ if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
55
+ mkdir -p "$DEST/WEB-INF/classes"
56
+ if command -v cygpath >/dev/null 2>&1; then
57
+ CP_PARTS=()
58
+ for j in "$TOMCAT_HOME"/lib/*.jar; do
59
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
60
+ done
61
+ if [ -d "$DEST/WEB-INF/lib" ]; then
62
+ for j in "$DEST"/WEB-INF/lib/*.jar; do
63
+ [ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
64
+ done
65
+ fi
66
+ CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
67
+ else
68
+ CP="$TOMCAT_HOME/lib/*"
69
+ [ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
70
+ fi
71
+ echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
72
+ javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
73
+ else
74
+ echo "No .java files found, skipping compile."
75
+ fi
76
+
77
+ # ---- start Tomcat
78
+ if [ "$DAEMON_MODE" = true ]; then
79
+ "$TOMCAT_HOME/bin/startup.sh"
80
+ echo
81
+ echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
82
+ echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
83
+ else
84
+ echo
85
+ echo "Started. Open: http://localhost:8080/$APP_NAME/"
86
+ echo "Press Ctrl+C to stop."
87
+ echo
88
+ exec "$TOMCAT_HOME/bin/catalina.sh" run
89
+ fi