This is an automated email from the ASF dual-hosted git repository. robertlazarski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/axis-axis2-c-core.git
commit 44603e5164e13c4ea88803700b647e18873a4995 Author: Robert Lazarski <[email protected]> AuthorDate: Tue Jan 6 05:04:49 2026 -1000 Add Android support with static service registry and HTTP/2 This commit adds comprehensive Android support to Axis2/C, enabling deployment on Android devices with HTTP/2 and JSON-RPC capabilities. ## C Standard: -std=gnu99 (all builds) Changed all configure.ac files from -ansi to -std=gnu99. This is required because json-c headers use C99 inline keyword which is incompatible with strict ANSI mode. Android NDK headers also require GNU extensions. The following files were updated: - configure.ac (root) - util/configure.ac - axiom/configure.ac - neethi/configure.ac - guththila/configure.ac - samples/configure.ac - tools/md5/configure.ac - tools/tcpmon/configure.ac ## Static Service Registry for Android Android cannot use dlopen() with static linking, so services must be compiled into the binary. This is solved using weak symbol architecture: 1. Framework declares weak symbol stubs that return NULL 2. Applications provide strong symbol implementations 3. At link time, strong symbols override weak symbols 4. The android_static_service_lookup() function maps service names to function pointers Implementation in src/core/receivers/axis2_json_rpc_msg_recv.c: - Weak symbol: camera_control_service_invoke_json() returns NULL - Lookup function: android_static_service_lookup(service_name) - Applications link with --whole-archive to ensure symbol resolution Applications provide two files: - Adapter: bridges Axis2/C types to app implementation - Implementation: app-specific service logic ## mod_axis2 Android Adaptations - Skip shared memory allocation (SELinux restrictions) - Use pool-based allocator for Android - Single-process mode support (Apache -X flag) - Android logging via __android_log_print() at INFO level ## Debug Logging Cleanup - Changed AXIS2_ANDROID_LOG macro to use ANDROID_LOG_INFO level - Removed verbose DEBUG/TRACE logging from mod_axis2.c - Cleaned up excessive logging in apache2_worker.c - Removed fprintf(stderr) debug statements from deployment code ## Bug Fixes - thread_unix.c: Fixed type mismatch in axutil_threadattr_detach_get() call (passed attr instead of temp) - samples/build.sh: Added make clean to fix parallel build race condition - build_for_tests.sh: Reduced parallelism from -j 10 to -j 4 ## Documentation Added docs/HTTP2_ANDROID.md with: - Quick reference for Claude AI sessions - Build system configuration - Static service registry architecture - Linking instructions with --whole-archive - HTTP/2 configuration for single-process mode 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --- axiom/configure.ac | 16 +- build_for_tests.sh | 2 +- configure.ac | 24 +- docs/HTTP2_ANDROID.md | 371 +++++++++++++++++ guththila/configure.ac | 15 +- neethi/configure.ac | 16 +- samples/build.sh | 3 +- samples/configure.ac | 16 +- .../user_guide/camera-control-service/services.xml | 95 +++++ .../src/camera_control_service.c | 442 ++++++++++++++++++++- .../src/camera_control_service.h | 127 ++++++ src/core/deployment/conf_builder.c | 152 ++++--- src/core/deployment/conf_init.c | 7 +- src/core/deployment/dep_engine.c | 23 +- src/core/deployment/desc_builder.c | 31 ++ src/core/receivers/axis2_json_rpc_msg_recv.c | 195 +++++++++ .../http/receiver/stub_http_transport_receiver.c | 218 ++++++++++ .../http/sender/stub_http_transport_sender.c | 117 ++++++ .../transport/http/server/apache2/apache2_worker.c | 27 +- src/core/transport/http/server/apache2/mod_axis2.c | 325 +++++---------- tools/md5/configure.ac | 16 +- tools/tcpmon/configure.ac | 16 +- util/configure.ac | 34 +- .../platforms/unix/axutil_date_time_util_unix.h | 2 + util/include/platforms/unix/axutil_unix.h | 26 ++ util/src/Makefile.am | 2 +- util/src/platforms/unix/date_time_util_unix.c | 8 +- util/src/platforms/unix/thread_unix.c | 2 +- 28 files changed, 1981 insertions(+), 347 deletions(-) diff --git a/axiom/configure.ac b/axiom/configure.ac index a49e76f43..8a5cb423c 100644 --- a/axiom/configure.ac +++ b/axiom/configure.ac @@ -62,8 +62,22 @@ AC_CHECK_LIB(dl, dlopen) AC_CHECK_LIB(z, inflate) CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -Wall -Wno-implicit-function-declaration " + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration " fi AC_PATH_TOOL(PKGCONFIG, pkg-config) diff --git a/build_for_tests.sh b/build_for_tests.sh index 2aa618791..24bad99fb 100755 --- a/build_for_tests.sh +++ b/build_for_tests.sh @@ -37,7 +37,7 @@ sh configure --prefix=${AXIS2C_HOME} \ --enable-coverage \ --enable-asan \ --enable-json=yes -make -j 10 +make -j 4 make install cd samples diff --git a/configure.ac b/configure.ac index 5554653e2..6ef47f80d 100644 --- a/configure.ac +++ b/configure.ac @@ -75,12 +75,26 @@ fi CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE" CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + AC_DEFINE([AXIS2_ANDROID_BUILD], [1], [Building for Android]) + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) +AM_CONDITIONAL([BUILD_FOR_ANDROID], [test "x$android_build" = "xyes"]) + if test "$GCC" = "yes"; then - dnl Remove -ansi flag to support modern C99 features required by json-c library - dnl The -ansi flag enforces C89/C90 standard which conflicts with json-c headers - dnl that use inline functions and other C99 constructs. This change maintains - dnl backward compatibility while enabling JSON functionality. - CFLAGS="$CFLAGS -Wall -Wno-implicit-function-declaration -g -D_GNU_SOURCE" + dnl Use gnu99 mode for compatibility with json-c and other modern libraries + dnl The -ansi flag conflicts with json-c headers which use C99 inline + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration -g -D_GNU_SOURCE" + AC_MSG_NOTICE([Using -std=gnu99 for compatibility]) fi dnl Checks for header files. diff --git a/docs/HTTP2_ANDROID.md b/docs/HTTP2_ANDROID.md new file mode 100644 index 000000000..94de1a749 --- /dev/null +++ b/docs/HTTP2_ANDROID.md @@ -0,0 +1,371 @@ +# Axis2/C Android Support + +This document describes how Axis2/C supports Android deployment with HTTP/2 and JSON-RPC. + +## Quick Reference for Claude + +When working with Axis2/C Android support, remember: + +1. **All builds use `-std=gnu99`** - Required for json-c header compatibility +2. **Android uses static linking** - No dlopen(), services use weak symbol registry +3. **Service code lives in applications** - Axis2/C core only has weak symbol stubs +4. **`--whole-archive` is required** - For weak/strong symbol resolution during linking + +## Build System + +### C Standard: Always gnu99 + +All Axis2/C builds use `-std=gnu99` (GNU C99 mode). This is required because: + +1. **json-c headers** use C99 `inline` keyword +2. **Android NDK headers** require GNU extensions +3. **POSIX functions** are hidden by strict ANSI mode + +**Implementation in all configure.ac files:** + +```m4 +if test "$GCC" = "yes"; then + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration -D_GNU_SOURCE" +fi +``` + +The following configure.ac files all use this pattern: +- `configure.ac` (root) +- `util/configure.ac` +- `axiom/configure.ac` +- `neethi/configure.ac` +- `guththila/configure.ac` +- `samples/configure.ac` +- `tools/md5/configure.ac` +- `tools/tcpmon/configure.ac` + +### Android Detection + +Android builds are detected by the `--host` parameter: + +```m4 +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + AC_DEFINE([AXIS2_ANDROID_BUILD], [1], [Building for Android]) + ;; + *) + android_build=no + ;; +esac +``` + +When `android_build=yes`: +- `AXIS2_ANDROID_BUILD` is defined +- `__ANDROID__` is typically already defined by the NDK compiler +- Static linking is expected + +### Android NDK Cross-Compilation + +**Environment setup:** + +```bash +export ANDROID_NDK_HOME=$HOME/Android/Sdk/ndk/28.0.12916984 +export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64 +export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang +export AR=$TOOLCHAIN/bin/llvm-ar +export RANLIB=$TOOLCHAIN/bin/llvm-ranlib +``` + +**Configure command:** + +```bash +./configure \ + --host=aarch64-linux-android \ + --prefix=$DEPS \ + --enable-static \ + --disable-shared \ + --enable-http2 \ + --enable-json \ + --with-apr=$DEPS \ + --with-openssl=$DEPS +``` + +## Static Service Registry (Android) + +### The Problem + +Traditional Axis2/C loads services dynamically: + +```c +void *lib = dlopen("libmyservice.so", RTLD_NOW); +func = dlsym(lib, "service_invoke_json"); +``` + +Android with static linking has no `.so` files to load. + +### Solution: Weak Symbol Architecture + +Axis2/C uses weak symbols so applications can provide service implementations at link time. + +**In `src/core/receivers/axis2_json_rpc_msg_recv.c`:** + +```c +#ifdef __ANDROID__ +typedef json_object* (*json_service_invoke_func_t)( + const axutil_env_t *env, json_object *json_request); + +/* Weak symbol - returns NULL if no implementation linked */ +__attribute__((weak)) +json_object* camera_control_service_invoke_json( + const axutil_env_t *env, json_object *json_request) +{ + (void)env; + (void)json_request; + return NULL; +} + +/* Lookup function - returns function pointer for known services */ +json_service_invoke_func_t +android_static_service_lookup(const char *service_name) +{ + if (!service_name) return NULL; + + if (strcmp(service_name, "CameraControlService") == 0) { + return camera_control_service_invoke_json; + } + /* Add more services here */ + + return NULL; +} +#endif +``` + +### How It Works + +``` +[Axis2/C Framework] [Application] + | | + weak symbol: strong symbol: + camera_control_service_ camera_control_service_ + invoke_json() -> NULL invoke_json() -> response + | | + +----------- Link Time ---------+ + | + Strong symbol wins +``` + +1. **Framework compiles** with weak symbol stubs (return NULL) +2. **Application provides** strong symbol implementations +3. **At link time**, strong symbols override weak symbols +4. **Result**: Application's service code handles requests + +### Application Service Implementation + +Applications provide two files: + +**Adapter (bridges Axis2/C to app):** + +```c +// axis2_static_service_adapter.c +#include <axutil_env.h> +#include <json-c/json.h> + +extern int camera_control_service_impl(const char* req, char* resp, size_t size); + +/* Strong symbol overrides weak in Axis2/C */ +json_object* camera_control_service_invoke_json( + const axutil_env_t *env, json_object *json_request) +{ + const char *req_str = json_object_to_json_string(json_request); + char response[65536] = {0}; + + camera_control_service_impl(req_str, response, sizeof(response)); + + return json_tokener_parse(response); +} +``` + +**Implementation (app-specific logic):** + +```c +// camera_control_service.c +int camera_control_service_impl(const char* req, char* resp, size_t size) +{ + /* Parse request, handle action, build response */ + snprintf(resp, size, "{\"result\":\"ok\"}"); + return 0; +} +``` + +### Critical: Functions Must Be Non-Static + +Registry functions MUST NOT use `static`: + +```c +/* WRONG - may be optimized away */ +static json_service_invoke_func_t android_static_service_lookup(...) { } + +/* CORRECT - global symbol preserved */ +json_service_invoke_func_t android_static_service_lookup(...) { } +``` + +Static functions have internal linkage and may be excluded from static archives. + +## Linking for Android + +### Direct Linking Required + +Libtool strips `--whole-archive` flags, so direct linking is necessary: + +```bash +$CC -fPIC -o httpd modules.o buildmark.o \ + -Wl,--export-dynamic \ + -L$DEPS/lib \ + # Apache modules... \ + $DEPS/lib/libmod_axis2.a \ + -Wl,--whole-archive $DEPS/lib/libaxis2_engine.a -Wl,--no-whole-archive \ + -Wl,--whole-archive libapp_services.a -Wl,--no-whole-archive \ + # Other Axis2 libraries... \ + -ljson-c -lnghttp2 -laprutil-1 -lapr-1 \ + -lm -llog -pthread +``` + +**Key flags:** + +| Flag | Purpose | +|------|---------| +| `-Wl,--whole-archive` | Include ALL objects from archive | +| `-Wl,--no-whole-archive` | Resume normal archive linking | +| `-Wl,--export-dynamic` | Export all symbols | +| `-llog` | Android logging | + +### Verify Symbol Inclusion + +```bash +nm httpd | grep -E "android_static|camera_control" + +# Expected (T = global text): +# 00000000007b4998 T android_static_service_lookup +# 00000000007b3ae0 T camera_control_service_invoke_json +``` + +## services.xml Configuration + +### REST Dispatcher Parameters + +Use `RESTLocation` and `RESTMethod`, NOT `httpPath` and `httpMethod`: + +```xml +<service name="CameraControlService"> + <operation name="getStatus"> + <messageReceiver class="axis2_json_rpc_msg_recv"/> + <parameter name="RESTMethod">POST</parameter> + <parameter name="RESTLocation">/getStatus</parameter> + <parameter name="contentType">application/json</parameter> + </operation> +</service> +``` + +## Adding New Services + +### Step 1: Add Weak Symbol to Axis2/C + +In `src/core/receivers/axis2_json_rpc_msg_recv.c`: + +```c +#ifdef __ANDROID__ +__attribute__((weak)) +json_object* my_new_service_invoke_json( + const axutil_env_t *env, json_object *json_request) +{ + (void)env; (void)json_request; + return NULL; +} + +json_service_invoke_func_t android_static_service_lookup(const char *svc) +{ + /* Existing services... */ + if (strcmp(svc, "MyNewService") == 0) { + return my_new_service_invoke_json; + } + return NULL; +} +#endif +``` + +### Step 2: Implement in Application + +Create adapter and implementation files in your application, then link with `--whole-archive`. + +### Step 3: Create services.xml + +Deploy to `services/MyNewService/services.xml` in the Axis2 repository. + +## mod_axis2 Configuration + +### Android-Specific Initialization + +In `mod_axis2.c`, Android builds: +- Skip shared memory (SELinux restrictions) +- Use pool-based allocator +- Run in single-process mode (`-X` flag) + +```c +#ifdef __ANDROID__ + /* Skip shared memory, use pool allocation */ + allocator = apr_palloc(pconf, sizeof(axutil_allocator_t)); + goto engine_init; +#endif +``` + +### Debug Logging + +Android logging uses INFO level: + +```c +#ifdef __ANDROID__ +#include <android/log.h> +#define AXIS2_ANDROID_LOG(fmt, ...) \ + __android_log_print(ANDROID_LOG_INFO, "Axis2-mod", fmt, ##__VA_ARGS__) +#else +#define AXIS2_ANDROID_LOG(fmt, ...) ((void)0) +#endif +``` + +## HTTP/2 on Android + +### Single-Process Mode Considerations + +Apache runs with `-X` (single-process) on Android. For older devices, configure HTTP/2 conservatively: + +```apache +# In http2-performance.conf +H2MaxSessionStreams 1 # Single stream for stability +H2StreamTimeout 30 # Prevent hanging streams +H2WindowSize 65535 # Reduced memory +H2Push off # Not needed for JSON-RPC +H2MinWorkers 1 +H2MaxWorkers 2 +``` + +## Summary Table + +| Aspect | Desktop/Server | Android | +|--------|----------------|---------| +| C standard | `-std=gnu99` | `-std=gnu99` | +| Library type | Shared (.so) | Static (.a) | +| Service loading | dlopen() | Weak symbol registry | +| Service location | External .so | Application provides strong symbols | +| Linker | Libtool | Direct clang with `--whole-archive` | +| Apache mode | Multi-process | Single-process (`-X`) | + +## Files Reference + +Key files for Android support: + +- `configure.ac` - Android detection, C flags +- `src/core/receivers/axis2_json_rpc_msg_recv.c` - Static service registry +- `src/core/transport/http/server/apache2/mod_axis2.c` - Android init, logging +- `src/core/transport/http/server/apache2/apache2_worker.c` - Request processing + +--- + +*Last updated: 2026-01-06* diff --git a/guththila/configure.ac b/guththila/configure.ac index dfb054c2c..a276ffbc6 100644 --- a/guththila/configure.ac +++ b/guththila/configure.ac @@ -63,8 +63,21 @@ AC_CHECK_LIB(dl, dlopen) CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE -DAXIS2_GUTHTHILA_ENABLED" +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -ggdb3 -Wall -Wno-implicit-function-declaration " + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -ggdb3 -Wall -Wno-implicit-function-declaration " fi AC_MSG_CHECKING(whether to set -Werror) diff --git a/neethi/configure.ac b/neethi/configure.ac index 6de3f989e..4b297c25c 100644 --- a/neethi/configure.ac +++ b/neethi/configure.ac @@ -45,8 +45,22 @@ dnl Checks for libraries. AC_CHECK_LIB(dl, dlopen) CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -Wall -Wno-implicit-function-declaration " + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration " fi AC_PATH_TOOL(PKGCONFIG, pkg-config) diff --git a/samples/build.sh b/samples/build.sh index dd9263a7b..ecbd224e9 100755 --- a/samples/build.sh +++ b/samples/build.sh @@ -17,6 +17,7 @@ sh autogen.sh ./configure --prefix=${AXIS2C_HOME} -make -j 10 +make clean || true +make -j 4 make install diff --git a/samples/configure.ac b/samples/configure.ac index b64bb0550..8143e8e7e 100644 --- a/samples/configure.ac +++ b/samples/configure.ac @@ -61,8 +61,22 @@ dnl Checks for libraries. AC_CHECK_LIB(dl, dlopen) CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -Werror -Wno-implicit-function-declaration -g -D_GNU_SOURCE" + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Werror -Wno-implicit-function-declaration -g -D_GNU_SOURCE" fi AC_MSG_CHECKING(whether to set -Werror) diff --git a/samples/user_guide/camera-control-service/services.xml b/samples/user_guide/camera-control-service/services.xml index f01b39c4a..80a350db3 100644 --- a/samples/user_guide/camera-control-service/services.xml +++ b/samples/user_guide/camera-control-service/services.xml @@ -179,5 +179,100 @@ <parameter name="responseType">application/json</parameter> </operation> + <!-- HTTP/2 JSON Operation: listFiles --> + <operation name="listFiles"> + <description> + List video files on the camera device storage. + + STUB IMPLEMENTATION: Users must implement camera_device_list_files_impl() + function with storage-specific integration code. + + Returns a list of video files with metadata (name, size, modified timestamp). + Supports optional pattern filtering. + + Example JSON Request: + { + "action": "list_files" + } + + Or with pattern filter: + { + "action": "list_files", + "pattern": "*.mp4" + } + + Example JSON Response: + { + "success": true, + "file_count": 2, + "total_size": 293020170, + "directory": "/storage/emulated/0/DCIM/OpenCamera", + "files": [ + {"name": "VID_20260104_105049.mp4", "size": 178613590, "modified": "2026-01-04 10:51:36"}, + {"name": "VID_20260104_112809.mp4", "size": 114406580, "modified": "2026-01-04 11:28:55"} + ] + } + + Pattern support (reference: Kanaha implementation): + - Empty/NULL: List all video files + - "*.mp4": List files matching wildcard + - "today": List files from current date + - "YYYY-MM-DD": List files from specific date + + NOTE: Action name accepts both "list_files" (snake_case) and "listFiles" (camelCase) + </description> + <messageReceiver class="axis2_json_rpc_msg_recv"/> + + <!-- REST-style HTTP mapping --> + <parameter name="httpMethod">POST</parameter> + <parameter name="httpPath">/listFiles</parameter> + <parameter name="contentType">application/json</parameter> + <parameter name="responseType">application/json</parameter> + </operation> + + <!-- HTTP/2 JSON Operation: deleteFiles --> + <operation name="deleteFiles"> + <description> + Delete video files from the camera device storage. + + STUB IMPLEMENTATION: Users must implement camera_device_delete_files_impl() + function with storage-specific integration code. + + Deletes files matching the specified pattern. Pattern is required. + + Example JSON Request: + { + "action": "delete_files", + "pattern": "VID_20260104_105049.mp4" + } + + Pattern support (reference: Kanaha implementation): + - Specific file: "VID_20260104_105049.mp4" + - Wildcard: "*.mp4", "VID_2026*" + - Date: "2026-01-04" (all files from that date) + - Today: "today" (all files from current date) + - All: "*" (delete all video files) + + Example JSON Response: + { + "success": true, + "message": "Files deleted successfully" + } + + SECURITY NOTE: Implementation should validate patterns to prevent: + - Path traversal attacks (reject patterns containing "..") + - Deletion outside video directory + + NOTE: Action name accepts both "delete_files" (snake_case) and "deleteFiles" (camelCase) + </description> + <messageReceiver class="axis2_json_rpc_msg_recv"/> + + <!-- REST-style HTTP mapping --> + <parameter name="httpMethod">POST</parameter> + <parameter name="httpPath">/deleteFiles</parameter> + <parameter name="contentType">application/json</parameter> + <parameter name="responseType">application/json</parameter> + </operation> + </service> </serviceGroup> \ No newline at end of file diff --git a/samples/user_guide/camera-control-service/src/camera_control_service.c b/samples/user_guide/camera-control-service/src/camera_control_service.c index a14d2753b..e962463e1 100644 --- a/samples/user_guide/camera-control-service/src/camera_control_service.c +++ b/samples/user_guide/camera-control-service/src/camera_control_service.c @@ -418,6 +418,175 @@ sftp_transfer_params_free(sftp_transfer_params_t *params, AXIS2_FREE(env->allocator, params); } +/** + * Create File List Parameters from JSON string using pure json-c + */ +AXIS2_EXTERN file_list_params_t* AXIS2_CALL +file_list_params_create_from_json( + const axutil_env_t *env, + const axis2_char_t *json_string) +{ + file_list_params_t *params = NULL; + json_object *json_obj = NULL; + json_object *value_obj = NULL; + + if (!env || !json_string) + { + AXIS2_ERROR_SET(env->error, AXIS2_ERROR_INVALID_NULL_PARAM, AXIS2_FAILURE); + return NULL; + } + + json_obj = json_tokener_parse(json_string); + if (!json_obj) + { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to parse JSON file list request"); + return NULL; + } + + params = AXIS2_MALLOC(env->allocator, sizeof(file_list_params_t)); + if (!params) + { + json_object_put(json_obj); + return NULL; + } + memset(params, 0, sizeof(file_list_params_t)); + + /* Extract pattern (optional) */ + if (json_object_object_get_ex(json_obj, "pattern", &value_obj)) + { + const char *pattern = json_object_get_string(value_obj); + if (pattern) + { + params->pattern = camera_service_sanitize_input(env, pattern, + CAMERA_PATH_MAX_LENGTH); + } + } + + /* Extract directory (optional) */ + if (json_object_object_get_ex(json_obj, "directory", &value_obj)) + { + const char *directory = json_object_get_string(value_obj); + if (directory) + { + params->directory = camera_service_sanitize_input(env, directory, + CAMERA_PATH_MAX_LENGTH); + } + } + + json_object_put(json_obj); + return params; +} + +/** + * Free file list parameters + */ +AXIS2_EXTERN void AXIS2_CALL +file_list_params_free(file_list_params_t *params, + const axutil_env_t *env) +{ + if (!params || !env) + return; + + if (params->pattern) + AXIS2_FREE(env->allocator, params->pattern); + if (params->directory) + AXIS2_FREE(env->allocator, params->directory); + + AXIS2_FREE(env->allocator, params); +} + +/** + * Free file list result + */ +AXIS2_EXTERN void AXIS2_CALL +file_list_result_free(file_list_result_t *result, + const axutil_env_t *env) +{ + if (!result || !env) + return; + + if (result->files) + { + for (int i = 0; i < result->file_count; i++) + { + if (result->files[i].name) + AXIS2_FREE(env->allocator, result->files[i].name); + if (result->files[i].modified) + AXIS2_FREE(env->allocator, result->files[i].modified); + } + AXIS2_FREE(env->allocator, result->files); + } + + if (result->directory) + AXIS2_FREE(env->allocator, result->directory); + + AXIS2_FREE(env->allocator, result); +} + +/** + * Create File Delete Parameters from JSON string using pure json-c + */ +AXIS2_EXTERN file_delete_params_t* AXIS2_CALL +file_delete_params_create_from_json( + const axutil_env_t *env, + const axis2_char_t *json_string) +{ + file_delete_params_t *params = NULL; + json_object *json_obj = NULL; + json_object *value_obj = NULL; + + if (!env || !json_string) + { + AXIS2_ERROR_SET(env->error, AXIS2_ERROR_INVALID_NULL_PARAM, AXIS2_FAILURE); + return NULL; + } + + json_obj = json_tokener_parse(json_string); + if (!json_obj) + { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to parse JSON file delete request"); + return NULL; + } + + params = AXIS2_MALLOC(env->allocator, sizeof(file_delete_params_t)); + if (!params) + { + json_object_put(json_obj); + return NULL; + } + memset(params, 0, sizeof(file_delete_params_t)); + + /* Extract pattern (required) */ + if (json_object_object_get_ex(json_obj, "pattern", &value_obj)) + { + const char *pattern = json_object_get_string(value_obj); + if (pattern) + { + params->pattern = camera_service_sanitize_input(env, pattern, + CAMERA_PATH_MAX_LENGTH); + } + } + + json_object_put(json_obj); + return params; +} + +/** + * Free file delete parameters + */ +AXIS2_EXTERN void AXIS2_CALL +file_delete_params_free(file_delete_params_t *params, + const axutil_env_t *env) +{ + if (!params || !env) + return; + + if (params->pattern) + AXIS2_FREE(env->allocator, params->pattern); + + AXIS2_FREE(env->allocator, params); +} + /* ======================================================================== * STUB IMPLEMENTATIONS - USERS MUST REPLACE WITH CAMERA-SPECIFIC CODE * ======================================================================== @@ -750,6 +919,193 @@ camera_device_sftp_transfer_impl( return AXIS2_SUCCESS; /* Stub always succeeds for testing */ } +/** + * STUB: List video files on device implementation + * + * IMPLEMENTATION REQUIRED: Users must replace this stub with storage-specific code. + * + * Example implementations (reference: Kanaha camera app): + * + * 1. Android Integration via Intent IPC: + * ```c + * // Send broadcast to Java receiver + * char intent_command[1024]; + * snprintf(intent_command, sizeof(intent_command), + * "am broadcast --user 0 " + * "-n org.myapp/org.myapp.CameraReceiver " + * "-a org.myapp.CAMERA_CONTROL " + * "--es action list_files " + * "--es pattern '%s'", + * params->pattern ? params->pattern : ""); + * system(intent_command); + * // Read response from file written by Java receiver + * ``` + * + * 2. Linux filesystem integration: + * ```c + * DIR *dir = opendir("/var/camera/videos"); + * struct dirent *entry; + * struct stat st; + * while ((entry = readdir(dir)) != NULL) { + * if (fnmatch(params->pattern ? params->pattern : "*.mp4", + * entry->d_name, 0) == 0) { + * stat(entry->d_name, &st); + * // Add to result list + * } + * } + * closedir(dir); + * ``` + */ +AXIS2_EXTERN file_list_result_t* AXIS2_CALL +camera_device_list_files_impl( + const axutil_env_t *env, + const file_list_params_t *params) +{ + /* STUB IMPLEMENTATION - USER MUST REPLACE */ + + file_list_result_t *result = NULL; + + if (!env) + { + return NULL; + } + + result = AXIS2_MALLOC(env->allocator, sizeof(file_list_result_t)); + if (!result) + { + return NULL; + } + memset(result, 0, sizeof(file_list_result_t)); + + /* Log the stub operation */ + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "STUB: List files request - pattern: %s, directory: %s", + params && params->pattern ? params->pattern : "*", + params && params->directory ? params->directory : "default"); + + AXIS2_LOG_WARNING(env->log, AXIS2_LOG_SI, + "STUB: camera_device_list_files_impl() - Replace with storage-specific implementation!"); + + /* Create stub result - empty file list */ + result->file_count = 0; + result->total_size = 0; + result->files = NULL; + result->directory = axutil_strdup(env, "/storage/emulated/0/DCIM/OpenCamera"); + + /* + * USER IMPLEMENTATION GOES HERE + * Replace this entire function with storage-specific code to list video files + * + * Expected behavior: + * 1. Determine target directory (use params->directory or default video storage) + * 2. List files matching params->pattern (or all video files if NULL) + * 3. For each file, populate file_info_t with name, size, modified timestamp + * 4. Return file_list_result_t with file_count, total_size, and files array + */ + + return result; +} + +/** + * STUB: Delete video files from device implementation + * + * IMPLEMENTATION REQUIRED: Users must replace this stub with storage-specific code. + * + * Pattern support (reference: Kanaha implementation): + * - Specific file: "VID_20260104_105049.mp4" + * - Wildcard: "*.mp4", "VID_2026*" + * - Date: "2026-01-04" (all files from that date) + * - Today: "today" (all files from current date) + * - All: "*" (delete all video files) + * + * Example implementations: + * + * 1. Android Integration via Intent IPC: + * ```c + * char intent_command[1024]; + * snprintf(intent_command, sizeof(intent_command), + * "am broadcast --user 0 " + * "-n org.myapp/org.myapp.CameraReceiver " + * "-a org.myapp.CAMERA_CONTROL " + * "--es action delete_files " + * "--es pattern '%s'", + * params->pattern); + * system(intent_command); + * ``` + * + * 2. Linux filesystem integration: + * ```c + * // Security: Validate pattern doesn't contain ".." + * if (strstr(params->pattern, "..")) return AXIS2_FAILURE; + * + * glob_t globbuf; + * char path_pattern[512]; + * snprintf(path_pattern, sizeof(path_pattern), "/var/camera/videos/%s", + * params->pattern); + * if (glob(path_pattern, 0, NULL, &globbuf) == 0) { + * for (size_t i = 0; i < globbuf.gl_pathc; i++) { + * unlink(globbuf.gl_pathv[i]); + * } + * globfree(&globbuf); + * } + * ``` + */ +AXIS2_EXTERN axis2_status_t AXIS2_CALL +camera_device_delete_files_impl( + const axutil_env_t *env, + const file_delete_params_t *params) +{ + /* STUB IMPLEMENTATION - USER MUST REPLACE */ + + if (!env || !params) + { + return AXIS2_FAILURE; + } + + /* Security validation - reject path traversal attempts */ + if (params->pattern && strstr(params->pattern, "..")) + { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "STUB: Security violation - path traversal detected in pattern: %s", + params->pattern); + return AXIS2_FAILURE; + } + + /* Require a pattern for deletion */ + if (!params->pattern || strlen(params->pattern) == 0) + { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "STUB: Missing required 'pattern' parameter for file deletion"); + return AXIS2_FAILURE; + } + + /* Log the stub operation */ + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "STUB: Delete files request - pattern: %s", + params->pattern); + + AXIS2_LOG_WARNING(env->log, AXIS2_LOG_SI, + "STUB: camera_device_delete_files_impl() - Replace with storage-specific implementation!"); + + /* + * USER IMPLEMENTATION GOES HERE + * Replace this entire function with storage-specific code to delete video files + * + * Expected behavior: + * 1. Validate pattern for security (no path traversal) + * 2. Resolve pattern to list of files to delete: + * - Specific filename: delete that file + * - Wildcard (*.mp4, VID_2026*): delete matching files + * - "today": delete files with today's date in filename + * - Date (YYYY-MM-DD): delete files from that date + * - "*": delete all video files + * 3. Delete each matching file + * 4. Return AXIS2_SUCCESS if all deletions succeeded + */ + + return AXIS2_SUCCESS; /* Stub always succeeds for testing */ +} + /* ======================================================================== * END STUB IMPLEMENTATIONS * ======================================================================== */ @@ -793,8 +1149,11 @@ camera_control_service_invoke_json(const axutil_env_t *env, AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Processing camera action: %s", action); - /* Route to appropriate camera operation */ - if (strcmp(action, "start_recording") == 0) + /* Route to appropriate camera operation + * Accept both snake_case (Axis2/C style) and camelCase (user-friendly) action names + * Reference: Kanaha implementation accepts both for API compatibility + */ + if (strcmp(action, "start_recording") == 0 || strcmp(action, "startRecording") == 0) { /* Handle start recording */ const char *json_str = json_object_to_json_string(json_request); @@ -832,7 +1191,7 @@ camera_control_service_invoke_json(const axutil_env_t *env, camera_recording_params_free(params, env); } - else if (strcmp(action, "stop_recording") == 0) + else if (strcmp(action, "stop_recording") == 0 || strcmp(action, "stopRecording") == 0) { /* Handle stop recording */ axis2_status_t result = camera_device_stop_recording_impl(env); @@ -846,7 +1205,7 @@ camera_control_service_invoke_json(const axutil_env_t *env, response = create_error_response(env, "Failed to stop recording"); } } - else if (strcmp(action, "get_status") == 0) + else if (strcmp(action, "get_status") == 0 || strcmp(action, "getStatus") == 0) { /* Handle status request */ camera_status_t *status = camera_device_get_status_impl(env); @@ -871,7 +1230,7 @@ camera_control_service_invoke_json(const axutil_env_t *env, response = create_error_response(env, "Failed to get camera status"); } } - else if (strcmp(action, "configure_settings") == 0) + else if (strcmp(action, "configure_settings") == 0 || strcmp(action, "configureSettings") == 0 || strcmp(action, "configure") == 0) { /* Handle settings configuration */ const char *json_str = json_object_to_json_string(json_request); @@ -895,7 +1254,7 @@ camera_control_service_invoke_json(const axutil_env_t *env, camera_settings_free(settings, env); } - else if (strcmp(action, "sftp_transfer") == 0) + else if (strcmp(action, "sftp_transfer") == 0 || strcmp(action, "sftpTransfer") == 0) { /* Handle SFTP file transfer */ const char *json_str = json_object_to_json_string(json_request); @@ -928,6 +1287,77 @@ camera_control_service_invoke_json(const axutil_env_t *env, sftp_transfer_params_free(sftp_params, env); } + else if (strcmp(action, "list_files") == 0 || strcmp(action, "listFiles") == 0) + { + /* Handle list files request */ + const char *json_str = json_object_to_json_string(json_request); + file_list_params_t *params = file_list_params_create_from_json(env, json_str); + + /* Call user-implementable function */ + file_list_result_t *result = camera_device_list_files_impl(env, params); + + if (result) + { + /* Build JSON response with file list */ + response = json_object_new_object(); + json_object_object_add(response, "success", json_object_new_boolean(1)); + json_object_object_add(response, "file_count", json_object_new_int(result->file_count)); + json_object_object_add(response, "total_size", json_object_new_int64(result->total_size)); + json_object_object_add(response, "directory", + json_object_new_string(result->directory ? result->directory : "")); + + /* Add files array */ + json_object *files_array = json_object_new_array(); + for (int i = 0; i < result->file_count && result->files; i++) + { + json_object *file_obj = json_object_new_object(); + json_object_object_add(file_obj, "name", + json_object_new_string(result->files[i].name ? result->files[i].name : "")); + json_object_object_add(file_obj, "size", + json_object_new_int64(result->files[i].size)); + json_object_object_add(file_obj, "modified", + json_object_new_string(result->files[i].modified ? result->files[i].modified : "")); + json_object_array_add(files_array, file_obj); + } + json_object_object_add(response, "files", files_array); + + file_list_result_free(result, env); + } + else + { + response = create_error_response(env, "Failed to list files"); + } + + if (params) + file_list_params_free(params, env); + } + else if (strcmp(action, "delete_files") == 0 || strcmp(action, "deleteFiles") == 0) + { + /* Handle delete files request */ + const char *json_str = json_object_to_json_string(json_request); + file_delete_params_t *params = file_delete_params_create_from_json(env, json_str); + + if (!params || !params->pattern || strlen(params->pattern) == 0) + { + if (params) + file_delete_params_free(params, env); + return create_error_response(env, "Missing required 'pattern' parameter"); + } + + /* Call user-implementable function */ + axis2_status_t result = camera_device_delete_files_impl(env, params); + + if (result == AXIS2_SUCCESS) + { + response = create_success_response(env, "Files deleted successfully"); + } + else + { + response = create_error_response(env, "Failed to delete files"); + } + + file_delete_params_free(params, env); + } else { /* Unknown action */ diff --git a/samples/user_guide/camera-control-service/src/camera_control_service.h b/samples/user_guide/camera-control-service/src/camera_control_service.h index f02b19ef4..da358e790 100644 --- a/samples/user_guide/camera-control-service/src/camera_control_service.h +++ b/samples/user_guide/camera-control-service/src/camera_control_service.h @@ -322,6 +322,133 @@ AXIS2_EXTERN axis2_status_t AXIS2_CALL camera_device_configure_multicam_deploy_impl(const axutil_env_t *env, const multi_camera_deploy_params_t *params); +/** + * File listing parameters structure + */ +typedef struct file_list_params +{ + axis2_char_t *pattern; /**< Optional filter pattern (e.g., "*.mp4", "today") */ + axis2_char_t *directory; /**< Directory to list (defaults to video storage) */ +} file_list_params_t; + +/** + * File info structure for list results + */ +typedef struct file_info +{ + axis2_char_t *name; /**< Filename */ + long size; /**< File size in bytes */ + axis2_char_t *modified; /**< Last modified timestamp */ +} file_info_t; + +/** + * File list result structure + */ +typedef struct file_list_result +{ + int file_count; /**< Number of files found */ + long total_size; /**< Total size of all files */ + file_info_t *files; /**< Array of file info structures */ + axis2_char_t *directory; /**< Directory that was listed */ +} file_list_result_t; + +/** + * File deletion parameters structure + */ +typedef struct file_delete_params +{ + axis2_char_t *pattern; /**< Pattern for files to delete */ +} file_delete_params_t; + +/* File list/delete parameter creation and free functions */ +AXIS2_EXTERN file_list_params_t* AXIS2_CALL +file_list_params_create_from_json(const axutil_env_t *env, + const axis2_char_t *json_string); + +AXIS2_EXTERN void AXIS2_CALL +file_list_params_free(file_list_params_t *params, + const axutil_env_t *env); + +AXIS2_EXTERN void AXIS2_CALL +file_list_result_free(file_list_result_t *result, + const axutil_env_t *env); + +AXIS2_EXTERN file_delete_params_t* AXIS2_CALL +file_delete_params_create_from_json(const axutil_env_t *env, + const axis2_char_t *json_string); + +AXIS2_EXTERN void AXIS2_CALL +file_delete_params_free(file_delete_params_t *params, + const axutil_env_t *env); + +/** + * List video files on device - STUB IMPLEMENTATION + * + * IMPLEMENTATION NOTE: This is a STUB function. Users must replace this with + * camera/storage-specific implementation to list video files. + * + * Example integrations: + * + * For Android integration: + * - Query MediaStore for video files in DCIM/OpenCamera directory + * - Use ContentResolver to get file metadata (size, date, duration) + * - Return JSON array of file info objects + * + * For Linux/V4L2 integration: + * - Use opendir/readdir to list files in video storage directory + * - Use stat() to get file size and modification time + * - Filter by pattern (glob matching) + * + * Pattern support (reference: Kanaha implementation): + * - Empty/NULL: List all video files + * - "*.mp4": List files matching wildcard + * - "today": List files from current date + * - "YYYY-MM-DD": List files from specific date + * + * @param env Apache Axis2/C environment + * @param params File listing parameters (pattern, directory) + * @return File list result structure (caller must free), NULL on failure + */ +AXIS2_EXTERN file_list_result_t* AXIS2_CALL +camera_device_list_files_impl(const axutil_env_t *env, + const file_list_params_t *params); + +/** + * Delete video files from device - STUB IMPLEMENTATION + * + * IMPLEMENTATION NOTE: This is a STUB function. Users must replace this with + * camera/storage-specific implementation to delete video files. + * + * Example integrations: + * + * For Android integration: + * - Use MediaStore to delete files (requires WRITE_EXTERNAL_STORAGE permission) + * - Use ContentResolver.delete() for MediaStore entries + * - Also delete the actual file from filesystem + * + * For Linux integration: + * - Use unlink() to delete files matching pattern + * - Implement glob matching for wildcard patterns + * + * Pattern support (reference: Kanaha implementation): + * - Specific file: "VID_20260104_105049.mp4" + * - Wildcard: "*.mp4", "VID_2026*" + * - Date: "2026-01-04" (all files from that date) + * - Today: "today" (all files from current date) + * - All: "*" (delete all video files) + * + * SECURITY NOTE: Implementation should validate patterns to prevent: + * - Path traversal attacks (reject patterns containing "..") + * - Deletion outside video directory + * + * @param env Apache Axis2/C environment + * @param params File deletion parameters (pattern) + * @return AXIS2_SUCCESS if files deleted successfully, AXIS2_FAILURE otherwise + */ +AXIS2_EXTERN axis2_status_t AXIS2_CALL +camera_device_delete_files_impl(const axutil_env_t *env, + const file_delete_params_t *params); + /* Utility functions */ AXIS2_EXTERN axis2_char_t* AXIS2_CALL camera_service_sanitize_input(const axutil_env_t *env, diff --git a/src/core/deployment/conf_builder.c b/src/core/deployment/conf_builder.c index 15485b0c8..860443c69 100644 --- a/src/core/deployment/conf_builder.c +++ b/src/core/deployment/conf_builder.c @@ -155,16 +155,13 @@ axis2_conf_builder_populate_conf( axis2_status_t status = AXIS2_FAILURE; axutil_param_t *param = NULL; - AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_conf_builder_populate_conf"); conf_node = axis2_desc_builder_build_om(conf_builder->desc_builder, env); if(!conf_node) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Building om tree failed. Unable to continue"); return AXIS2_FAILURE; } conf_element = axiom_node_get_data_element(conf_node, env); /* processing Paramters */ - /* Processing service level paramters */ qparamst = axutil_qname_create(env, AXIS2_PARAMETERST, NULL, NULL); itr = axiom_element_get_children_with_qname(conf_element, env, qparamst, conf_node); axutil_qname_free(qparamst, env); @@ -190,8 +187,6 @@ axis2_conf_builder_populate_conf( msg_recv_element); if(!msg_recv) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Message receiver loading failed. Unable to continue"); return AXIS2_FAILURE; } @@ -210,22 +205,14 @@ axis2_conf_builder_populate_conf( if(disp_order_element) { axis2_conf_builder_process_disp_order(conf_builder, env, disp_order_node); - AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "Found the custom disptaching" - " order and continue with that order"); } else { status = axis2_conf_set_default_dispatchers(conf_builder->conf, env); if(!status) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Setting default dispatchers to configuration failed, " - "unable to continue."); return AXIS2_FAILURE; } - - AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "No custom dispatching order" - " found. Continue with the default dispatching order"); } /* Process Module refs */ @@ -235,11 +222,10 @@ axis2_conf_builder_populate_conf( status = axis2_conf_builder_process_module_refs(conf_builder, env, module_itr); if(!status) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Processing module ref's failed, unable to continue."); return AXIS2_FAILURE; } - /* Proccessing Transport Sennders */ + + /* Proccessing Transport Senders */ qtransportsender = axutil_qname_create(env, AXIS2_TRANSPORTSENDER, NULL, NULL); trs_senders = axiom_element_get_children_with_qname(conf_element, env, qtransportsender, conf_node); @@ -247,8 +233,6 @@ axis2_conf_builder_populate_conf( status = axis2_conf_builder_process_transport_senders(conf_builder, env, trs_senders); if(!status) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Processing transport senders failed, unable to continue"); return AXIS2_FAILURE; } @@ -260,8 +244,6 @@ axis2_conf_builder_populate_conf( if(!status) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Processing transport receivers failed, unable to continue"); return AXIS2_FAILURE; } @@ -273,8 +255,6 @@ axis2_conf_builder_populate_conf( if(!status) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Processing phase orders failed, unable to continue"); return AXIS2_FAILURE; } @@ -778,85 +758,91 @@ axis2_conf_builder_process_transport_senders( return AXIS2_FAILURE; } - /* transport impl class */ + /* transport impl class - OPTIONAL for statically linked transports */ qdllname = axutil_qname_create(env, AXIS2_CLASSNAME, NULL, NULL); trs_dll_att = axiom_element_get_attribute(transport_element, env, qdllname); axutil_qname_free(qdllname, env); if(!trs_dll_att) { - AXIS2_ERROR_SET(env->error, AXIS2_ERROR_TRANSPORT_SENDER_ERROR, AXIS2_FAILURE); - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Transport dll name attribute is not set in the " - "%s transport element node", name); - return AXIS2_FAILURE; - } - class_name = axiom_attribute_get_value(trs_dll_att, env); - impl_info_param = axutil_param_create(env, class_name, NULL); - if(!impl_info_param) - { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Creating module dll name parameter failed for %s. Unable " - "to continue", class_name); - axis2_transport_out_desc_free(transport_out, env); - return AXIS2_FAILURE; - } - dll_desc = axutil_dll_desc_create(env); - dll_name = axutil_dll_desc_create_platform_specific_dll_name(dll_desc, env, class_name); - if(!axis2_flag) - { - repos_name = axis2_dep_engine_get_repos_path(axis2_desc_builder_get_dep_engine( - conf_builder->desc_builder, env), env); - temp_path = axutil_stracat(env, repos_name, AXIS2_PATH_SEP_STR); - temp_path2 = axutil_stracat(env, temp_path, AXIS2_LIB_FOLDER); - temp_path3 = axutil_stracat(env, temp_path2, AXIS2_PATH_SEP_STR); - path_qualified_dll_name = axutil_stracat(env, temp_path3, dll_name); - AXIS2_FREE(env->allocator, temp_path); - AXIS2_FREE(env->allocator, temp_path2); - AXIS2_FREE(env->allocator, temp_path3); + /* No class attribute - transport is statically linked, skip DLL loading */ + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "Transport sender %s has no class attribute - assuming statically linked", name); + transport_sender = NULL; /* Will be set by static initialization */ } else { - libparam = axis2_conf_get_param(conf, env, AXIS2_LIB_DIR); - if(libparam) + class_name = axiom_attribute_get_value(trs_dll_att, env); + impl_info_param = axutil_param_create(env, class_name, NULL); + if(!impl_info_param) { - libdir = axutil_param_get_value(libparam, env); + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "Creating module dll name parameter failed for %s. Unable " + "to continue", class_name); + axis2_transport_out_desc_free(transport_out, env); + return AXIS2_FAILURE; } - - if(!libdir) + dll_desc = axutil_dll_desc_create(env); + dll_name = axutil_dll_desc_create_platform_specific_dll_name(dll_desc, env, class_name); + if(!axis2_flag) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Specifying" - "services and modules directories using axis2.xml but" - " path of the library directory is not present"); - return AXIS2_FAILURE; + repos_name = axis2_dep_engine_get_repos_path(axis2_desc_builder_get_dep_engine( + conf_builder->desc_builder, env), env); + temp_path = axutil_stracat(env, repos_name, AXIS2_PATH_SEP_STR); + temp_path2 = axutil_stracat(env, temp_path, AXIS2_LIB_FOLDER); + temp_path3 = axutil_stracat(env, temp_path2, AXIS2_PATH_SEP_STR); + path_qualified_dll_name = axutil_stracat(env, temp_path3, dll_name); + AXIS2_FREE(env->allocator, temp_path); + AXIS2_FREE(env->allocator, temp_path2); + AXIS2_FREE(env->allocator, temp_path3); } - path_qualified_dll_name = axutil_strcat(env, libdir, AXIS2_PATH_SEP_STR, dll_name, - NULL); - } + else + { + libparam = axis2_conf_get_param(conf, env, AXIS2_LIB_DIR); + if(libparam) + { + libdir = axutil_param_get_value(libparam, env); + } - axutil_dll_desc_set_name(dll_desc, env, path_qualified_dll_name); - AXIS2_FREE(env->allocator, path_qualified_dll_name); + if(!libdir) + { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Specifying" + "services and modules directories using axis2.xml but" + " path of the library directory is not present"); + return AXIS2_FAILURE; + } + path_qualified_dll_name = axutil_strcat(env, libdir, AXIS2_PATH_SEP_STR, dll_name, + NULL); + } - axutil_dll_desc_set_type(dll_desc, env, AXIS2_TRANSPORT_SENDER_DLL); - axutil_param_set_value(impl_info_param, env, dll_desc); - axutil_param_set_value_free(impl_info_param, env, axutil_dll_desc_free_void_arg); - axutil_class_loader_init(env); - transport_sender = axutil_class_loader_create_dll(env, impl_info_param); - axis2_transport_out_desc_add_param(transport_out, env, impl_info_param); + axutil_dll_desc_set_name(dll_desc, env, path_qualified_dll_name); + AXIS2_FREE(env->allocator, path_qualified_dll_name); - if(!transport_sender) - { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Transport sender is NULL for transport %s, unable to " - "continue", name); - axis2_transport_out_desc_free(transport_out, env); - return status; + axutil_dll_desc_set_type(dll_desc, env, AXIS2_TRANSPORT_SENDER_DLL); + axutil_param_set_value(impl_info_param, env, dll_desc); + axutil_param_set_value_free(impl_info_param, env, axutil_dll_desc_free_void_arg); + axutil_class_loader_init(env); + transport_sender = axutil_class_loader_create_dll(env, impl_info_param); + axis2_transport_out_desc_add_param(transport_out, env, impl_info_param); + + if(!transport_sender) + { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "Transport sender is NULL for transport %s, unable to " + "continue", name); + axis2_transport_out_desc_free(transport_out, env); + return status; + } } - status = axis2_transport_out_desc_set_sender(transport_out, env, transport_sender); - if(!status) + /* Only set sender if we have one - statically linked transports have NULL here */ + if(transport_sender) { - axis2_transport_out_desc_free(transport_out, env); - return status; + status = axis2_transport_out_desc_set_sender(transport_out, env, transport_sender); + if(!status) + { + axis2_transport_out_desc_free(transport_out, env); + return status; + } } /* Process Parameters */ diff --git a/src/core/deployment/conf_init.c b/src/core/deployment/conf_init.c index f9d895881..f3f15ebb6 100644 --- a/src/core/deployment/conf_init.c +++ b/src/core/deployment/conf_init.c @@ -56,7 +56,8 @@ axis2_build_conf_ctx_with_dep_engine( conf = axis2_dep_engine_load(dep_engine, env); if(!conf) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Loading deployment engine failed"); + int err_code = env->error ? env->error->error_number : -1; + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Loading deployment engine failed, error_code=%d", err_code); axis2_dep_engine_free(dep_engine, env); return NULL; } @@ -93,7 +94,6 @@ axis2_build_conf_ctx( axis2_conf_ctx_t *conf_ctx = NULL; axis2_dep_engine_t *dep_engine = NULL; - AXIS2_LOG_TRACE(env->log, AXIS2_LOG_SI, "Entry:axis2_build_conf_ctx"); dep_engine = axis2_dep_engine_create_with_repos_name(env, repo_name); if(!dep_engine) { @@ -105,8 +105,9 @@ axis2_build_conf_ctx( conf_ctx = axis2_build_conf_ctx_with_dep_engine(env, dep_engine, AXIS2_VALUE_TRUE); if(!conf_ctx) { + int err_code = env->error ? env->error->error_number : -1; AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Loading configuration context failed for repository %s.", repo_name); + "Loading configuration context failed for repository %s. error_code=%d", repo_name, err_code); return NULL; } diff --git a/src/core/deployment/dep_engine.c b/src/core/deployment/dep_engine.c index 89d27a58e..e650cc8bb 100644 --- a/src/core/deployment/dep_engine.c +++ b/src/core/deployment/dep_engine.c @@ -720,47 +720,46 @@ axis2_dep_engine_load( axis2_status_t status = AXIS2_FAILURE; axutil_array_list_t *out_fault_phases = NULL; + if(!dep_engine->conf_name) { AXIS2_ERROR_SET(env->error, AXIS2_PATH_TO_CONFIG_CAN_NOT_BE_NULL, AXIS2_FAILURE); - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, - "Path to axis2 configuration file is NULL. Unable to continue"); return NULL; } + dep_engine->conf = axis2_conf_create(env); if(!dep_engine->conf) { - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory. Allocation to configuration failed"); return NULL; } - /* Set a flag to mark that conf is created using axis2 xml. To find out that conf is build using - * axis2 xml , this flag can be used. - */ + axis2_conf_set_axis2_flag(dep_engine->conf, env, dep_engine->file_flag); axis2_conf_set_axis2_xml(dep_engine->conf, env, dep_engine->conf_name); + dep_engine->conf_builder = axis2_conf_builder_create_with_file_and_dep_engine_and_conf(env, dep_engine->conf_name, dep_engine, dep_engine->conf); if(!(dep_engine->conf_builder)) { axis2_conf_free(dep_engine->conf, env); - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Configuration builder creation failed"); dep_engine->conf = NULL; + return NULL; } - /* Populate the axis2 configuration from reading axis2.xml. - */ + status = axis2_conf_builder_populate_conf(dep_engine->conf_builder, env); if(AXIS2_SUCCESS != status) { + int err_code = env->error ? env->error->error_number : -1; axis2_conf_free(dep_engine->conf, env); dep_engine->conf = NULL; - AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Populating Axis2 Configuration failed"); return NULL; } + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, "[dep_engine] populate_conf succeeded, about to set paths"); + status = axis2_dep_engine_set_svc_and_module_dir_path(dep_engine, env); if(AXIS2_SUCCESS != status) { @@ -768,6 +767,8 @@ axis2_dep_engine_load( return NULL; } + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, "[dep_engine] set paths succeeded, about to set features"); + status = axis2_dep_engine_set_dep_features(dep_engine, env); if(AXIS2_SUCCESS != status) { @@ -775,6 +776,8 @@ axis2_dep_engine_load( return NULL; } + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, "[dep_engine] set features succeeded"); + if(dep_engine->repos_listener) { axis2_repos_listener_free(dep_engine->repos_listener, env); diff --git a/src/core/deployment/desc_builder.c b/src/core/deployment/desc_builder.c index 1483c4d86..62fcb25c7 100644 --- a/src/core/deployment/desc_builder.c +++ b/src/core/deployment/desc_builder.c @@ -960,6 +960,37 @@ axis2_desc_builder_load_msg_recv( axutil_qname_free(class_qname, env); class_name = axiom_attribute_get_value(recv_name, env); +#ifdef __ANDROID__ + /* + * Android Static Message Receiver Registry + * + * On Android with static linking, message receivers are compiled directly + * into the httpd binary. Instead of trying to dlopen non-existent .so files, + * we use direct create functions for known message receivers. + * + * Available receivers in HTTP/2 JSON mode: axis2_json_rpc_msg_recv, axis2_msg_recv + */ + if (class_name) + { + if (axutil_strcmp(class_name, "axis2_json_rpc_msg_recv") == 0) + { + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[desc_builder] Android static linking: creating axis2_json_rpc_msg_recv directly"); + return axis2_json_rpc_msg_recv_create(env); + } + else if (axutil_strcmp(class_name, "axis2_msg_recv") == 0) + { + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[desc_builder] Android static linking: creating axis2_msg_recv directly"); + return axis2_msg_recv_create(env); + } + /* Unknown class - will fall through to dlopen attempt which will fail with clear error */ + AXIS2_LOG_WARNING(env->log, AXIS2_LOG_SI, + "[desc_builder] Android: Unknown message receiver class '%s', attempting dlopen (may fail)", + class_name); + } +#endif + conf = axis2_dep_engine_get_axis_conf(desc_builder->engine, env); if(!conf) { diff --git a/src/core/receivers/axis2_json_rpc_msg_recv.c b/src/core/receivers/axis2_json_rpc_msg_recv.c index e14363677..dfff532aa 100644 --- a/src/core/receivers/axis2_json_rpc_msg_recv.c +++ b/src/core/receivers/axis2_json_rpc_msg_recv.c @@ -45,6 +45,173 @@ #include <dlfcn.h> /* For JSON-direct service loading */ /* Revolutionary: NO AXIOM includes - pure JSON processing only */ +#ifdef __ANDROID__ +/* + * Android Static Service Registry + * + * On Android with static linking, services are compiled directly into the + * httpd binary. Instead of trying to dlopen non-existent .so files, we use + * direct invoke functions for known services. + * + * Each service exports: <servicename>_invoke_json(env, json_request) + * + * WEAK SYMBOL ARCHITECTURE: + * Service functions are declared as weak symbols. This allows: + * 1. Core framework to compile without service implementations + * 2. Applications to provide strong symbol implementations at link time + * 3. Graceful handling when services are not linked (returns NULL) + * + * To add a new static service: + * 1. Add weak declaration here + * 2. Add entry in android_static_service_lookup() + * 3. Provide implementation in your application, compiled as a .o or .a file + * 4. Link the service archive during final httpd linking with --whole-archive + */ + +typedef json_object* (*json_service_invoke_func_t)(const axutil_env_t *env, json_object *json_request); + +/* + * Weak symbol declarations for statically linked services + * Applications provide strong implementations at link time + */ +__attribute__((weak)) +json_object* camera_control_service_invoke_json( + const axutil_env_t *env, + json_object *json_request) +{ + /* Weak default - returns NULL if no implementation linked */ + (void)env; + (void)json_request; + return NULL; +} + +/* Add more weak service declarations here: + * __attribute__((weak)) + * json_object* another_service_invoke_json(const axutil_env_t *env, json_object *json_request) + * { + * (void)env; (void)json_request; + * return NULL; + * } + */ + +/** + * Android Static Service Registry - looks up services by name + * + * Returns function pointer for known service names. If only weak symbol + * was linked (no strong implementation), calls will return NULL responses, + * which the caller handles appropriately. + * + * @param service_name The service name (e.g., "CameraControlService") + * @return Function pointer to the service's invoke_json function, or NULL + */ +/* Non-static to ensure linker includes this function when linking from archives */ +json_service_invoke_func_t +android_static_service_lookup(const char *service_name) +{ + if (!service_name) { + return NULL; + } + + /* Static service registry - returns function pointer for known services. + * If application linked a strong implementation, it will be used. + * If only weak stub exists, caller handles NULL response gracefully. */ + if (strcmp(service_name, "CameraControlService") == 0) { + return camera_control_service_invoke_json; + } + + /* Add more services as needed: + * if (strcmp(service_name, "AnotherService") == 0) { + * return another_service_invoke_json; + * } + */ + + return NULL; /* Service not found in static registry */ +} + +/** + * Try static service invocation for Android + * + * @param env Environment + * @param service_name Service name + * @param json_request_str JSON request string + * @param json_response_out Output parameter for JSON response + * @return AXIS2_TRUE if successful, AXIS2_FALSE otherwise + */ +/* Non-static to ensure linker includes this function when linking from archives */ +axis2_bool_t +try_android_static_service(const axutil_env_t *env, + const char *service_name, + const char *json_request_str, + axis2_char_t **json_response_out) +{ + json_service_invoke_func_t service_invoke = NULL; + json_object *json_request = NULL; + json_object *json_response_obj = NULL; + const char *json_response_str = NULL; + + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Looking up service: %s", service_name); + + service_invoke = android_static_service_lookup(service_name); + if (!service_invoke) { + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Service '%s' not in static registry", service_name); + return AXIS2_FALSE; + } + + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Found service '%s' in static registry", service_name); + + /* Parse JSON request */ + json_request = json_tokener_parse(json_request_str); + if (!json_request) { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Failed to parse JSON request"); + return AXIS2_FALSE; + } + + /* Invoke service */ + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Invoking %s", service_name); + + json_response_obj = service_invoke(env, json_request); + + json_object_put(json_request); /* Free request */ + + if (!json_response_obj) { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Service returned NULL response"); + return AXIS2_FALSE; + } + + /* Convert response to string */ + json_response_str = json_object_to_json_string(json_response_obj); + if (!json_response_str) { + json_object_put(json_response_obj); + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Failed to serialize response"); + return AXIS2_FALSE; + } + + /* Copy response for caller */ + *json_response_out = axutil_strdup(env, json_response_str); + + json_object_put(json_response_obj); /* Free response */ + + if (!*json_response_out) { + AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Failed to duplicate response string"); + return AXIS2_FALSE; + } + + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[ANDROID_STATIC] Service '%s' invoked successfully, response length: %d", + service_name, (int)strlen(*json_response_out)); + + return AXIS2_TRUE; +} +#endif /* __ANDROID__ */ + /** * Try JSON-direct service loading for HTTP/2 JSON services * @@ -546,6 +713,31 @@ axis2_json_rpc_msg_recv_invoke_business_logic_sync( "[JSON RPC MSG RECV] DEBUG: Loading service implementation: %s", service_name ? service_name : "unknown"); +#ifdef __ANDROID__ + /* + * Android Static Service Loading + * + * On Android with static linking, try the static service registry first. + * This avoids dlopen() which won't work with statically linked services. + */ + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[JSON RPC MSG RECV] Android: Trying static service registry for '%s'", + service_name ? service_name : "unknown"); + + if (service_name && json_request && + try_android_static_service(env, service_name, json_request, &json_response)) { + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[JSON RPC MSG RECV] Android: Static service '%s' invoked successfully", + service_name); + /* json_response is already set, skip to response handling */ + goto response_ready; + } + + AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, + "[JSON RPC MSG RECV] Android: Service '%s' not in static registry, trying dynamic loading", + service_name ? service_name : "unknown"); +#endif + // CORRECT PATTERN: Pass parameter object directly to class loader (same as msg_recv.c:258) impl_obj = axutil_class_loader_create_dll(env, impl_class_param); @@ -603,6 +795,9 @@ axis2_json_rpc_msg_recv_invoke_business_logic_sync( } } +#ifdef __ANDROID__ +response_ready: +#endif if (!json_response) { AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "[JSON RPC MSG RECV] No JSON response generated - creating default error JSON"); diff --git a/src/core/transport/http/receiver/stub_http_transport_receiver.c b/src/core/transport/http/receiver/stub_http_transport_receiver.c new file mode 100644 index 000000000..16f547675 --- /dev/null +++ b/src/core/transport/http/receiver/stub_http_transport_receiver.c @@ -0,0 +1,218 @@ +/* + * Stub HTTP Transport Receiver for Android Static Linking + * + * This is a minimal transport receiver stub that satisfies Axis2/C's dlopen + * requirements while actual HTTP receiving is handled by Apache httpd + * via mod_axis2 for the embedded server scenario. + */ + +#include <axis2_transport_receiver.h> +#include <axis2_const.h> + +typedef struct stub_http_receiver_impl +{ + axis2_transport_receiver_t receiver; + axis2_char_t *server_ip; + axis2_conf_ctx_t *conf_ctx; +} stub_http_receiver_impl_t; + +#define AXIS2_INTF_TO_IMPL(recv) ((stub_http_receiver_impl_t*)(recv)) + +static axis2_status_t AXIS2_CALL +stub_http_receiver_init( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env, + struct axis2_conf_ctx *conf_ctx, + struct axis2_transport_in_desc *transport_in) +{ + stub_http_receiver_impl_t *impl = AXIS2_INTF_TO_IMPL(transport_receiver); + (void)transport_in; + + impl->conf_ctx = conf_ctx; + + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_RECEIVER] init called"); + + return AXIS2_SUCCESS; +} + +static axis2_status_t AXIS2_CALL +stub_http_receiver_start( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env) +{ + (void)transport_receiver; + + /* Apache httpd handles the actual server start */ + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_RECEIVER] start called - Apache handles HTTP receiving"); + + return AXIS2_SUCCESS; +} + +static axis2_endpoint_ref_t* AXIS2_CALL +stub_http_receiver_get_reply_to_epr( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env, + const axis2_char_t *svc_name) +{ + (void)transport_receiver; + (void)env; + (void)svc_name; + return NULL; +} + +static axis2_endpoint_ref_t* AXIS2_CALL +stub_http_receiver_get_epr_for_service( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env, + const axis2_char_t *svc_name) +{ + (void)transport_receiver; + (void)env; + (void)svc_name; + return NULL; +} + +static axis2_char_t* AXIS2_CALL +stub_http_receiver_get_server_ip( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env) +{ + stub_http_receiver_impl_t *impl = AXIS2_INTF_TO_IMPL(transport_receiver); + (void)env; + return impl->server_ip; +} + +static void AXIS2_CALL +stub_http_receiver_set_server_ip( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env, + axis2_char_t *ip) +{ + stub_http_receiver_impl_t *impl = AXIS2_INTF_TO_IMPL(transport_receiver); + + if (impl->server_ip) + { + AXIS2_FREE(env->allocator, impl->server_ip); + } + impl->server_ip = ip ? axutil_strdup(env, ip) : NULL; + + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_RECEIVER] set_server_ip: %s", ip ? ip : "NULL"); +} + +static struct axis2_conf_ctx* AXIS2_CALL +stub_http_receiver_get_conf_ctx( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env) +{ + stub_http_receiver_impl_t *impl = AXIS2_INTF_TO_IMPL(transport_receiver); + (void)env; + return impl->conf_ctx; +} + +static axis2_bool_t AXIS2_CALL +stub_http_receiver_is_running( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env) +{ + (void)transport_receiver; + (void)env; + /* Apache httpd handles the actual running state */ + return AXIS2_TRUE; +} + +static void AXIS2_CALL +stub_http_receiver_set_is_application_client_side( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env, + axis2_bool_t is_application_client_side) +{ + (void)transport_receiver; + (void)env; + (void)is_application_client_side; +} + +static axis2_status_t AXIS2_CALL +stub_http_receiver_stop( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env) +{ + (void)transport_receiver; + + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_RECEIVER] stop called"); + + return AXIS2_SUCCESS; +} + +static void AXIS2_CALL +stub_http_receiver_free( + axis2_transport_receiver_t *transport_receiver, + const axutil_env_t *env) +{ + stub_http_receiver_impl_t *impl = AXIS2_INTF_TO_IMPL(transport_receiver); + + if (impl) + { + if (impl->server_ip) + { + AXIS2_FREE(env->allocator, impl->server_ip); + } + AXIS2_FREE(env->allocator, impl); + } +} + +static const axis2_transport_receiver_ops_t stub_http_receiver_ops = { + stub_http_receiver_init, + stub_http_receiver_start, + stub_http_receiver_get_reply_to_epr, + stub_http_receiver_get_epr_for_service, + stub_http_receiver_get_server_ip, + stub_http_receiver_set_server_ip, + stub_http_receiver_get_conf_ctx, + stub_http_receiver_is_running, + stub_http_receiver_set_is_application_client_side, + stub_http_receiver_stop, + stub_http_receiver_free +}; + +AXIS2_EXTERN int +axis2_get_instance( + void **inst, + const axutil_env_t *env) +{ + stub_http_receiver_impl_t *impl = NULL; + + impl = (stub_http_receiver_impl_t*)AXIS2_MALLOC(env->allocator, + sizeof(stub_http_receiver_impl_t)); + + if (!impl) + { + AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE); + return AXIS2_FAILURE; + } + + memset(impl, 0, sizeof(stub_http_receiver_impl_t)); + impl->receiver.ops = &stub_http_receiver_ops; + *inst = (void*)&(impl->receiver); + + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_RECEIVER] Created stub HTTP transport receiver instance"); + + return AXIS2_SUCCESS; +} + +AXIS2_EXTERN int +axis2_remove_instance( + void *inst, + const axutil_env_t *env) +{ + if (inst) + { + axis2_transport_receiver_t *receiver = (axis2_transport_receiver_t*)inst; + stub_http_receiver_free(receiver, env); + } + return AXIS2_SUCCESS; +} diff --git a/src/core/transport/http/sender/stub_http_transport_sender.c b/src/core/transport/http/sender/stub_http_transport_sender.c new file mode 100644 index 000000000..0fa77e289 --- /dev/null +++ b/src/core/transport/http/sender/stub_http_transport_sender.c @@ -0,0 +1,117 @@ +/* + * Stub HTTP Transport Sender for Android Static Linking + * + * This is a minimal transport sender stub that satisfies Axis2/C's dlopen + * requirements while actual response writing is handled by apache2_worker + * and the message receiver for HTTP/2 JSON-RPC. + */ + +#include <axis2_transport_sender.h> +#include <axis2_const.h> + +typedef struct stub_http_sender_impl +{ + axis2_transport_sender_t sender; +} stub_http_sender_impl_t; + +#define AXIS2_INTF_TO_IMPL(sender) ((stub_http_sender_impl_t*)(sender)) + +static axis2_status_t AXIS2_CALL +stub_http_sender_init( + axis2_transport_sender_t *transport_sender, + const axutil_env_t *env, + struct axis2_conf_ctx *conf_ctx, + struct axis2_transport_out_desc *transport_out) +{ + (void)transport_sender; + (void)env; + (void)conf_ctx; + (void)transport_out; + return AXIS2_SUCCESS; +} + +static axis2_status_t AXIS2_CALL +stub_http_sender_invoke( + axis2_transport_sender_t *transport_sender, + const axutil_env_t *env, + struct axis2_msg_ctx *msg_ctx) +{ + (void)transport_sender; + (void)msg_ctx; + + /* For HTTP/2 JSON-RPC, response is already written by apache2_worker + * and the message receiver. This stub just returns success. */ + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_SENDER] invoke called - response handled by apache2_worker"); + + return AXIS2_SUCCESS; +} + +static axis2_status_t AXIS2_CALL +stub_http_sender_cleanup( + axis2_transport_sender_t *transport_sender, + const axutil_env_t *env, + struct axis2_msg_ctx *msg_ctx) +{ + (void)transport_sender; + (void)env; + (void)msg_ctx; + return AXIS2_SUCCESS; +} + +static void AXIS2_CALL +stub_http_sender_free( + axis2_transport_sender_t *transport_sender, + const axutil_env_t *env) +{ + stub_http_sender_impl_t *impl = AXIS2_INTF_TO_IMPL(transport_sender); + if (impl) + { + AXIS2_FREE(env->allocator, impl); + } +} + +static const axis2_transport_sender_ops_t stub_http_sender_ops = { + stub_http_sender_init, + stub_http_sender_invoke, + stub_http_sender_cleanup, + stub_http_sender_free +}; + +AXIS2_EXTERN int +axis2_get_instance( + void **inst, + const axutil_env_t *env) +{ + stub_http_sender_impl_t *impl = NULL; + + impl = (stub_http_sender_impl_t*)AXIS2_MALLOC(env->allocator, + sizeof(stub_http_sender_impl_t)); + + if (!impl) + { + AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE); + return AXIS2_FAILURE; + } + + impl->sender.ops = &stub_http_sender_ops; + *inst = (void*)&(impl->sender); + + AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, + "[STUB_SENDER] Created stub HTTP transport sender instance"); + + return AXIS2_SUCCESS; +} + +AXIS2_EXTERN int +axis2_remove_instance( + void *inst, + const axutil_env_t *env) +{ + if (inst) + { + axis2_transport_sender_t *sender = (axis2_transport_sender_t*)inst; + stub_http_sender_free(sender, env); + } + return AXIS2_SUCCESS; +} diff --git a/src/core/transport/http/server/apache2/apache2_worker.c b/src/core/transport/http/server/apache2/apache2_worker.c index 045299bb6..94e4adc8b 100644 --- a/src/core/transport/http/server/apache2/apache2_worker.c +++ b/src/core/transport/http/server/apache2/apache2_worker.c @@ -31,6 +31,7 @@ #include <axutil_url.h> #include <http_core.h> #include <http_protocol.h> +#include <http_log.h> #include <axiom_soap.h> #include <axutil_class_loader.h> #include <axutil_string_util.h> @@ -38,6 +39,14 @@ #include <axiom_mtom_sending_callback.h> #include <axis2_apache2_request_processor.h> +/* Android logging macro - use INFO level for trace messages */ +#ifdef __ANDROID__ +#include <android/log.h> +#define AXIS2_WORKER_LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "Axis2-worker", fmt, ##__VA_ARGS__) +#else +#define AXIS2_WORKER_LOG(fmt, ...) ((void)0) /* No-op on non-Android */ +#endif + #define READ_SIZE 2048 static axis2_status_t @@ -224,6 +233,12 @@ axis2_apache2_worker_process_request( axis2_status_t status = AXIS2_FAILURE; axutil_hash_t *headers = NULL; + if (!apache2_worker || !env || !request) { + return DECLINED; + } + + AXIS2_WORKER_LOG("Processing: %s", request->uri ? request->uri : "NULL"); + AXIS2_ENV_CHECK(env, AXIS2_CRITICAL_FAILURE); AXIS2_PARAM_CHECK(env->error, request, AXIS2_CRITICAL_FAILURE); @@ -262,10 +277,14 @@ axis2_apache2_worker_process_request( } request->content_type = content_type; - out_desc = axis2_conf_get_transport_out(axis2_conf_ctx_get_conf(apache2_worker->conf_ctx, env), - env, AXIS2_TRANSPORT_ENUM_HTTP); - in_desc = axis2_conf_get_transport_in(axis2_conf_ctx_get_conf(apache2_worker->conf_ctx, env), - env, AXIS2_TRANSPORT_ENUM_HTTP); + { + axis2_conf_t *conf = axis2_conf_ctx_get_conf(apache2_worker->conf_ctx, env); + if (!conf) { + return DECLINED; + } + out_desc = axis2_conf_get_transport_out(conf, env, AXIS2_TRANSPORT_ENUM_HTTP); + in_desc = axis2_conf_get_transport_in(conf, env, AXIS2_TRANSPORT_ENUM_HTTP); + } { axis2_transport_receiver_t *receiver = NULL; receiver = axis2_transport_in_desc_get_recv(in_desc, env); diff --git a/src/core/transport/http/server/apache2/mod_axis2.c b/src/core/transport/http/server/apache2/mod_axis2.c index f5936596f..02e9f9f8f 100644 --- a/src/core/transport/http/server/apache2/mod_axis2.c +++ b/src/core/transport/http/server/apache2/mod_axis2.c @@ -28,6 +28,14 @@ #include <sys/types.h> #include <unistd.h> #include <strings.h> + +/* Android logging macro - use INFO level for trace messages */ +#ifdef __ANDROID__ +#include <android/log.h> +#define AXIS2_ANDROID_LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "Axis2-mod", fmt, ##__VA_ARGS__) +#else +#define AXIS2_ANDROID_LOG(fmt, ...) ((void)0) /* No-op on non-Android */ +#endif #include <axutil_log_default.h> #include <axutil_thread_pool.h> /* REVOLUTIONARY: Conditional axiom include - only for SOAP processing */ @@ -46,6 +54,24 @@ #include <axis2_conf_ctx.h> #include <axis2_conf_init.h> +#ifdef __ANDROID__ +/* + * Android Static Service References + * + * These extern declarations force the linker to include the statically + * linked service implementations. The services themselves are registered + * via the static service registry in axis2_json_rpc_msg_recv.c. + */ +#include <json-c/json.h> +extern json_object* camera_control_service_invoke_json( + const axutil_env_t *env, json_object *json_request); + +/* Force linker to include the service - __attribute__((used)) prevents compiler from optimizing away this unreferenced array */ +static void* axis2_static_service_refs[] __attribute__((used)) = { + (void*)camera_control_service_invoke_json +}; +#endif + /* Configuration structure populated by apache2.conf */ typedef struct axis2_config_rec { @@ -66,28 +92,37 @@ apr_global_mutex_t *global_mutex = NULL; /* DEBUGGING: Signal handler for crash debugging */ static void axis2_segfault_handler(int sig) { - void *array[10]; - size_t size; - char **strings; - size_t i; - /* Log crash information to Apache error log */ ap_log_error(APLOG_MARK, APLOG_CRIT, APR_SUCCESS, NULL, "[Axis2] CRASH DETECTED: Signal %d received (SEGFAULT) - PID: %d", sig, getpid()); - /* Get backtrace */ - size = backtrace(array, 10); - strings = backtrace_symbols(array, size); +#ifndef __ANDROID__ + /* backtrace functions are not available on Android (Bionic libc) */ + { + void *array[10]; + size_t size; + char **strings; + size_t i; - ap_log_error(APLOG_MARK, APLOG_CRIT, APR_SUCCESS, NULL, - "[Axis2] BACKTRACE: Obtained %zd stack frames", size); + /* Get backtrace */ + size = backtrace(array, 10); + strings = backtrace_symbols(array, size); - for (i = 0; i < size; i++) { ap_log_error(APLOG_MARK, APLOG_CRIT, APR_SUCCESS, NULL, - "[Axis2] BACKTRACE[%zd]: %s", i, strings[i]); - } + "[Axis2] BACKTRACE: Obtained %zd stack frames", size); + + for (i = 0; i < size; i++) { + ap_log_error(APLOG_MARK, APLOG_CRIT, APR_SUCCESS, NULL, + "[Axis2] BACKTRACE[%zd]: %s", i, strings[i]); + } - free(strings); + free(strings); + } +#else + /* Android: Simple crash logging without backtrace */ + ap_log_error(APLOG_MARK, APLOG_CRIT, APR_SUCCESS, NULL, + "[Axis2] Backtrace not available on Android"); +#endif /* Exit gracefully to avoid infinite loops */ _exit(1); @@ -205,14 +240,10 @@ axis2_create_svr( apr_pool_t * p, server_rec * s) { - /* DEBUGGING: Log server configuration creation */ - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, s, - "[Axis2] DEBUG: axis2_create_svr ENTRY - creating server configuration"); - axis2_config_rec_t *conf = apr_palloc(p, sizeof(*conf)); if (!conf) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, s, - "[Axis2] DEBUG: axis2_create_svr FAILED - memory allocation failed"); + "[Axis2] Failed to allocate server configuration"); return NULL; } @@ -222,8 +253,6 @@ axis2_create_svr( conf->axis2_global_pool_size = 0; conf->max_log_file_size = 1; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, s, - "[Axis2] DEBUG: axis2_create_svr SUCCESS - server configuration created"); return conf; } @@ -233,27 +262,18 @@ axis2_set_repo_path( void *dummy, const char *arg) { - /* DEBUGGING: Log directive processing */ - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_repo_path ENTRY - processing Axis2RepoPath directive: %s", arg); - axis2_config_rec_t *conf = NULL; const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if(err != NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_repo_path FAILED - context error: %s", err); return err; } conf = (axis2_config_rec_t *)ap_get_module_config(cmd->server->module_config, &axis2_module); if (!conf) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_repo_path FAILED - could not get module config"); return "Failed to get module configuration"; } - conf->axis2_repo_path = apr_pstrdup(cmd->pool, arg); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_repo_path SUCCESS - set repo path: %s", arg); + /* Resolve relative path to absolute path based on ServerRoot */ + conf->axis2_repo_path = ap_server_root_relative(cmd->pool, arg); return NULL; } @@ -263,28 +283,18 @@ axis2_set_log_file( void *dummy, const char *arg) { - /* DEBUGGING: Log directive processing */ - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_file ENTRY - processing Axis2LogFile directive: %s", arg); - axis2_config_rec_t *conf = NULL; const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if(err != NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_file FAILED - context error: %s", err); return err; } conf = (axis2_config_rec_t *)ap_get_module_config(cmd->server->module_config, &axis2_module); if (!conf) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_file FAILED - could not get module config"); return "Failed to get module configuration"; } conf->axutil_log_file = apr_pstrdup(cmd->pool, arg); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_file SUCCESS - set log file: %s", arg); return NULL; } @@ -294,28 +304,18 @@ axis2_set_max_log_file_size( void *dummy, const char *arg) { - /* DEBUGGING: Log directive processing */ - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_max_log_file_size ENTRY - processing Axis2MaxLogFileSize directive: %s", arg); - axis2_config_rec_t *conf = NULL; const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if(err != NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_max_log_file_size FAILED - context error: %s", err); return err; } conf = (axis2_config_rec_t *)ap_get_module_config(cmd->server->module_config, &axis2_module); if (!conf) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_max_log_file_size FAILED - could not get module config"); return "Failed to get module configuration"; } conf->max_log_file_size = 1024 * 1024 * atoi(arg); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_max_log_file_size SUCCESS - set max log file size: %s", arg); return NULL; } @@ -343,95 +343,55 @@ axis2_set_log_level( void *dummy, const char *arg) { - /* DEBUGGING: Log directive processing - this is likely where the crash occurs */ - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level ENTRY - processing Axis2LogLevel directive: %s", arg); - char *str; axutil_log_levels_t level = AXIS2_LOG_LEVEL_DEBUG; axis2_config_rec_t *conf = NULL; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - about to check command context"); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if(err != NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level FAILED - context error: %s", err); return err; } - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - about to get module config"); conf = (axis2_config_rec_t *)ap_get_module_config(cmd->server->module_config, &axis2_module); if (!conf) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level FAILED - could not get module config"); return "Failed to get module configuration"; } - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - about to call ap_getword_conf"); str = ap_getword_conf(cmd->pool, &arg); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - ap_getword_conf returned: %s", str ? str : "NULL"); if(str) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - about to check string: '%s'", str); - if(strcmp(str, "info") == 0) { level = AXIS2_LOG_LEVEL_INFO; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'info' level"); } else if(strcmp(str, "debug") == 0) { level = AXIS2_LOG_LEVEL_DEBUG; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'debug' level"); } else if(strcmp(str, "error") == 0) { level = AXIS2_LOG_LEVEL_ERROR; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'error' level"); } else if(strcmp(str, "warn") == 0) { level = AXIS2_LOG_LEVEL_WARNING; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'warn' level"); } else if(strcmp(str, "crit") == 0) { level = AXIS2_LOG_LEVEL_CRITICAL; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'crit' level"); } else if(strcmp(str, "user") == 0) { level = AXIS2_LOG_LEVEL_USER; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'user' level"); } else if(strcmp(str, "trace") == 0) { level = AXIS2_LOG_LEVEL_TRACE; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - matched 'trace' level"); - } - else - { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level - no match for '%s', using default debug level", str); } } conf->log_level = level; - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, cmd->server, - "[Axis2] DEBUG: axis2_set_log_level SUCCESS - set log level to %d, returning", level); return NULL; } @@ -455,17 +415,17 @@ axis2_set_svc_url_prefix( return NULL; } -/* REVOLUTIONARY HTTP/2 Request Interception - Enhanced Stream Processing */ +/* HTTP/2 Request Interception - Enhanced Stream Processing */ static int axis2_fixups( request_rec * req) { /* Only process /services/* requests */ - if (!req->uri || strncmp(req->uri, "/services/", 10) != 0) { + if (!req || !req->uri || strncmp(req->uri, "/services/", 10) != 0) { return DECLINED; } - /* REVOLUTIONARY HTTP/2 JSON Processing - Enhanced breed apart stream optimization */ + /* HTTP/2 JSON Processing - direct handler routing */ if (req->protocol && strstr(req->protocol, "HTTP/2")) { const char *content_type = req->content_type ? req->content_type : @@ -473,42 +433,30 @@ axis2_fixups( if (content_type && strstr(content_type, "application/json")) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, - "REVOLUTIONARY: HTTP/2 JSON stream detected in fixups - Optimizing for breed apart processing"); - /* Force this request to use axis2_module handler */ req->handler = apr_pstrdup(req->pool, "axis2_module"); - /* REVOLUTIONARY: Enhanced HTTP/2 processing flags */ - apr_table_set(req->headers_in, "X-Axis2-JSON-Mode", "revolutionary"); - apr_table_set(req->headers_in, "X-Axis2-HTTP2-Request", "breed-apart"); + /* HTTP/2 processing flags */ + apr_table_set(req->headers_in, "X-Axis2-JSON-Mode", "enabled"); + apr_table_set(req->headers_in, "X-Axis2-HTTP2-Request", "true"); apr_table_set(req->headers_in, "X-Axis2-Processing", "direct-response"); - apr_table_set(req->headers_in, "X-Axis2-Architecture", "option-b-enhanced"); - /* HTTP/2 Performance optimization flags */ - apr_table_set(req->headers_in, "X-HTTP2-Multiplexing", "enabled"); - apr_table_set(req->headers_in, "X-Pipeline-Bypass", "soap-transformation"); - - /* REVOLUTIONARY: Stream priority hints for HTTP/2 multiplexing */ + /* Stream priority hints for HTTP/2 multiplexing */ const char *service_path = req->uri + 10; /* Skip "/services/" */ if (service_path && strlen(service_path) > 0) { - /* Set stream priority based on service type */ if (strstr(service_path, "BigData") || strstr(service_path, "H2")) { apr_table_set(req->headers_in, "X-Stream-Priority", "high-performance"); } else { apr_table_set(req->headers_in, "X-Stream-Priority", "standard"); } } - - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, - "REVOLUTIONARY: HTTP/2 JSON stream optimized - Revolutionary processing flags set"); } } return DECLINED; } -/* The sample content handler */ +/* Request handler */ static int axis2_handler( request_rec * req) @@ -521,27 +469,17 @@ axis2_handler( apr_allocator_t *local_allocator = NULL; apr_pool_t *local_pool = NULL; - const char *content_type_header = apr_table_get(req->headers_in, "Content-Type"); - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, - "DEBUG: mod_axis2 axis2_handler ENTRY - method: %s, content_type: %s, uri: %s, protocol: %s, header_content_type: %s", - req->method ? req->method : "NULL", - req->content_type ? req->content_type : "NULL", - req->uri ? req->uri : "NULL", - req->protocol ? req->protocol : "NULL", - content_type_header ? content_type_header : "NULL"); + if (!req) { + return DECLINED; + } - if(strcmp(req->handler, "axis2_module")) + /* Check handler */ + if (!req->handler || strcmp(req->handler, "axis2_module")) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, - "DEBUG: mod_axis2 DECLINED - handler is %s, not axis2_module", req->handler); return DECLINED; } - - - - - + AXIS2_ANDROID_LOG("Processing request: %s", req->uri ? req->uri : "NULL"); /* Set up the read policy from the client. */ if((rv = ap_setup_client_block(req, REQUEST_CHUNKED_DECHUNK)) != OK) @@ -553,15 +491,6 @@ axis2_handler( apr_allocator_create(&local_allocator); apr_pool_create_ex(&local_pool, NULL, NULL, local_allocator); - /*thread_env = axutil_init_thread_env(axutil_env);*/ - - /*axutil_env->allocator->current_pool = (void *) req->pool; - rv = AXIS2_APACHE2_WORKER_PROCESS_REQUEST(axis2_worker, axutil_env, req);*/ - - /* create new allocator for this request */ - /*allocator = (axutil_allocator_t *) apr_palloc(req->pool, - sizeof(axutil_allocator_t));*/ - allocator = (axutil_allocator_t *)apr_palloc(local_pool, sizeof(axutil_allocator_t)); if(!allocator) @@ -578,23 +507,17 @@ axis2_handler( error = axutil_error_create(allocator); thread_env = axutil_env_create_with_error_log_thread_pool(allocator, error, axutil_env->log, - axutil_env-> thread_pool); + axutil_env->thread_pool); thread_env->allocator = allocator; thread_env->set_session_fn = axis2_set_session; thread_env->get_session_fn = axis2_get_session; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, - "DEBUG: mod_axis2 calling AXIS2_APACHE2_WORKER_PROCESS_REQUEST - uri: %s, content_type: %s", - req->uri ? req->uri : "NULL", - req->content_type ? req->content_type : "NULL"); - rv = AXIS2_APACHE2_WORKER_PROCESS_REQUEST(axis2_worker, thread_env, req); - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, - "DEBUG: mod_axis2 AXIS2_APACHE2_WORKER_PROCESS_REQUEST returned: %d", rv); - if(AXIS2_CRITICAL_FAILURE == rv) { + apr_pool_destroy(local_pool); + apr_allocator_destroy(local_allocator); return HTTP_INTERNAL_SERVER_ERROR; } @@ -697,14 +620,28 @@ axis2_post_config( return OK; } - /* DEBUGGING: Enable engine initialization with comprehensive logging */ + /* Android: Skip shared memory initialization + * SELinux policy restricts apr_shm_create() for unprivileged apps. + * We run in single-process mode (-X) anyway, so shared memory is unnecessary. + */ +#ifdef __ANDROID__ ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: *** POST_CONFIG FUNCTION ENTRY *** - post_config called, enabling engine initialization"); + "[Axis2] Android mode - using pool allocation (no shared memory)"); + + allocator = (axutil_allocator_t *)apr_palloc(pconf, sizeof(axutil_allocator_t)); + if (!allocator) { + ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, svr_rec, + "[Axis2] Error creating allocator for Android"); + exit(APEXIT_CHILDFATAL); + } + allocator->malloc_fn = axis2_module_malloc; + allocator->realloc = axis2_module_realloc; + allocator->free_fn = axis2_module_free; + allocator->global_pool_ref = 0; + allocator->local_pool = (void *)pconf; + allocator->current_pool = (void *)pconf; + allocator->global_pool = (void *)pconf; - /* Skip shared memory initialization for HTTP2_JSON_ONLY_MODE - use direct engine init */ -#ifdef HTTP2_JSON_ONLY_MODE - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: HTTP2_JSON_ONLY_MODE - skipping shared memory, proceeding to engine init"); goto engine_init; #endif #if APR_HAS_SHARED_MEMORY @@ -764,10 +701,8 @@ axis2_post_config( allocator->current_pool = (void *) rmm; allocator->global_pool = (void *) rmm; - /* REVOLUTIONARY HTTP2_JSON_ONLY_MODE: Direct engine initialization without shared memory */ + /* Engine initialization */ engine_init: - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: engine_init - beginning core Axis2/C initialization"); #ifdef HTTP2_JSON_ONLY_MODE /* For HTTP/2 JSON mode: Use simple pool-based allocator instead of shared memory */ @@ -779,21 +714,11 @@ engine_init: allocator->local_pool = (void *)pconf; allocator->current_pool = (void *)pconf; allocator->global_pool = (void *)pconf; - - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: HTTP2_JSON_ONLY_MODE allocator created using APR pool"); #endif - /* REVOLUTIONARY: Skip axiom init for HTTP/2 JSON-only mode - * Initialize XML readers only when SOAP processing is needed - */ + /* Initialize XML readers only when SOAP processing is needed */ #ifndef HTTP2_JSON_ONLY_MODE axiom_xml_reader_init(); - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: axiom_xml_reader_init completed for SOAP mode"); -#else - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: Skipping axiom_xml_reader_init in HTTP2_JSON_ONLY_MODE"); #endif axutil_error_init(); @@ -841,80 +766,57 @@ engine_init: axutil_env->set_session_fn = axis2_set_session; axutil_env->get_session_fn = axis2_get_session; - /* REVOLUTIONARY: Create temporary engine to trigger HTTP service provider registration */ - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: Creating temporary engine to register HTTP service provider"); - + /* Create temporary engine to trigger HTTP service provider registration */ axis2_conf_ctx_t *temp_conf_ctx = axis2_build_conf_ctx(axutil_env, conf->axis2_repo_path); if (!temp_conf_ctx) { + int err_code = axutil_env->error ? axutil_env->error->error_number : -1; + const axis2_char_t *err_msg = axutil_env->error ? + axutil_error_get_message(axutil_env->error) : "unknown"; ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, svr_rec, - "[Axis2] Error creating temporary configuration context for service provider registration"); + "[Axis2] Error creating configuration context - error_code=%d, message=%s", + err_code, err_msg ? err_msg : "NULL"); exit(APEXIT_CHILDFATAL); } axis2_engine_t *temp_engine = axis2_engine_create(axutil_env, temp_conf_ctx); if (!temp_engine) { ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, svr_rec, - "[Axis2] Error creating temporary engine for service provider registration"); + "[Axis2] Error creating engine for service provider registration"); exit(APEXIT_CHILDFATAL); } - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: Temporary engine created successfully - HTTP service provider should now be registered"); - /* Clean up temporary engine - service provider registration persists */ axis2_engine_free(temp_engine, axutil_env); axis2_conf_ctx_free(temp_conf_ctx, axutil_env); - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: About to create axis2_apache2_worker after service provider registration"); - - /* DEBUGGING: Validate parameters before function call */ - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: Validating parameters - axutil_env=%p, repo_path=%s", - (void*)axutil_env, conf->axis2_repo_path ? conf->axis2_repo_path : "NULL"); - if (!axutil_env) { ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, svr_rec, - "[Axis2] CRITICAL: axutil_env is NULL before worker creation"); + "[Axis2] Environment is NULL before worker creation"); exit(APEXIT_CHILDFATAL); } if (!axutil_env->log) { ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, svr_rec, - "[Axis2] CRITICAL: axutil_env->log is NULL before worker creation"); + "[Axis2] Log is NULL before worker creation"); exit(APEXIT_CHILDFATAL); } - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: Parameters validated, calling axis2_apache2_worker_create..."); - axis2_worker = axis2_apache2_worker_create(axutil_env, conf->axis2_repo_path); - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: axis2_apache2_worker_create returned: %p", (void*)axis2_worker); - if (!axis2_worker) { ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, svr_rec, - "[Axis2] Error creating mod_axis2 apache2 worker - function returned NULL"); + "[Axis2] Error creating mod_axis2 apache2 worker"); exit(APEXIT_CHILDFATAL); } ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: Worker creation SUCCESSFUL - axis2_worker=%p", (void*)axis2_worker); - - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: axis2_apache2_worker created successfully"); + "[Axis2] Initialization complete - worker created successfully"); } #endif - /* REVOLUTIONARY: Engine initialization complete - HTTP service provider should now be available */ - ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, svr_rec, - "[Axis2] DEBUG: post_config completed successfully - HTTP service provider registered"); - return OK; } @@ -941,9 +843,7 @@ axis2_module_init( return; } - /* REVOLUTIONARY: Skip axiom init for HTTP/2 JSON-only mode - * Initialize XML readers only when SOAP processing is needed - */ + /* Skip axiom init for HTTP/2 JSON-only mode */ #ifndef HTTP2_JSON_ONLY_MODE axiom_xml_reader_init(); #endif @@ -1037,35 +937,18 @@ static void axis2_register_hooks( apr_pool_t * p) { - /* DEBUGGING: Install comprehensive signal handlers to catch all crashes */ + /* Install signal handlers for crash debugging */ signal(SIGSEGV, axis2_segfault_handler); signal(SIGFPE, axis2_segfault_handler); signal(SIGILL, axis2_segfault_handler); signal(SIGABRT, axis2_segfault_handler); signal(SIGTERM, axis2_segfault_handler); signal(SIGBUS, axis2_segfault_handler); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, NULL, - "[Axis2] DEBUG: Comprehensive signal handlers installed (SIGSEGV, SIGFPE, SIGILL, SIGABRT, SIGTERM, SIGBUS)"); - - /* DEBUGGING: Log hook registration phase */ - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, NULL, - "[Axis2] DEBUG: axis2_register_hooks ENTRY - about to register hooks"); ap_hook_post_config(axis2_post_config, NULL, NULL, APR_HOOK_MIDDLE); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, NULL, - "[Axis2] DEBUG: post_config hook registered successfully"); - ap_hook_fixups(axis2_fixups, NULL, NULL, APR_HOOK_FIRST); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, NULL, - "[Axis2] DEBUG: fixups hook registered successfully"); - ap_hook_handler(axis2_handler, NULL, NULL, APR_HOOK_MIDDLE); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, NULL, - "[Axis2] DEBUG: handler hook registered successfully"); - ap_hook_child_init(axis2_module_init, NULL, NULL, APR_HOOK_MIDDLE); - ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, NULL, - "[Axis2] DEBUG: All hooks registered successfully - axis2_register_hooks COMPLETE"); } apr_status_t diff --git a/tools/md5/configure.ac b/tools/md5/configure.ac index 2007ce30e..860e5f41c 100644 --- a/tools/md5/configure.ac +++ b/tools/md5/configure.ac @@ -32,8 +32,22 @@ AC_PROG_MAKE_SET CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE -g" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -Wall -Wno-implicit-function-declaration" + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration" fi diff --git a/tools/tcpmon/configure.ac b/tools/tcpmon/configure.ac index 2ffa1b535..cafaa7796 100644 --- a/tools/tcpmon/configure.ac +++ b/tools/tcpmon/configure.ac @@ -32,8 +32,22 @@ AC_PROG_MAKE_SET CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -Wall -Wno-implicit-function-declaration -g" + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration -g" fi diff --git a/util/configure.ac b/util/configure.ac index bb1f3f71b..7c2b71e6d 100644 --- a/util/configure.ac +++ b/util/configure.ac @@ -60,16 +60,42 @@ dnl Checks for libraries. AC_CHECK_LIB(dl, dlopen) AC_CHECK_LIB(z, inflate) AC_CHECK_LIB(socket, socket) -AC_CHECK_LIB(pthread, pthread_key_create) -dnl This can be removed when the ftime call in -dnl ./util/src/platforms/unix/date_time_util_unix.c +dnl Skip pthread library check on Android (pthread is in libc) +case "$host" in + *android*) + dnl Android has pthread built into libc, no separate -lpthread needed + PTHREADLIBS="" + ;; + *) + AC_CHECK_LIB(pthread, pthread_key_create) + PTHREADLIBS="-lpthread" + ;; +esac +AC_SUBST(PTHREADLIBS) + +dnl This can be removed when the ftime call in +dnl ./util/src/platforms/unix/date_time_util_unix.c dnl is changed to call gettimeofday AC_CHECK_LIB(compat, ftime) CFLAGS="$CFLAGS -D_LARGEFILE64_SOURCE" + +dnl Detect Android target for conditional compilation +AC_MSG_CHECKING([for Android target]) +case "$host" in + *-linux-android*|*-android*) + android_build=yes + ;; + *) + android_build=no + ;; +esac +AC_MSG_RESULT([$android_build]) + if test "$GCC" = "yes"; then - CFLAGS="$CFLAGS -ansi -Wall -Wno-implicit-function-declaration -D_GNU_SOURCE" + dnl Use gnu99 for json-c compatibility + CFLAGS="$CFLAGS -std=gnu99 -Wall -Wno-implicit-function-declaration -D_GNU_SOURCE" fi AC_MSG_CHECKING(whether to set -Werror) diff --git a/util/include/platforms/unix/axutil_date_time_util_unix.h b/util/include/platforms/unix/axutil_date_time_util_unix.h index 997939530..2ae8e0429 100644 --- a/util/include/platforms/unix/axutil_date_time_util_unix.h +++ b/util/include/platforms/unix/axutil_date_time_util_unix.h @@ -22,7 +22,9 @@ #include <axutil_utils_defines.h> #include <stdio.h> #include <sys/types.h> +#ifndef __ANDROID__ #include <sys/timeb.h> +#endif #include <time.h> #ifdef __cplusplus diff --git a/util/include/platforms/unix/axutil_unix.h b/util/include/platforms/unix/axutil_unix.h index a0b00dcfc..a5bf13e68 100644 --- a/util/include/platforms/unix/axutil_unix.h +++ b/util/include/platforms/unix/axutil_unix.h @@ -102,7 +102,33 @@ extern "C" * Miscellaneous ****************************************************************/ #include <sys/time.h> +#ifndef __ANDROID__ #include <sys/timeb.h> +#endif + +/* Android replacement for sys/timeb.h */ +#ifdef __ANDROID__ +/* Android doesn't have sys/timeb.h or ftime(), use clock_gettime() instead */ +struct axis2_android_timeb { + time_t time; + unsigned short millitm; + short timezone; + short dstflag; +}; +static inline void axis2_android_ftime(struct axis2_android_timeb *tb) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + tb->time = ts.tv_sec; + tb->millitm = ts.tv_nsec / 1000000; + tb->timezone = 0; + tb->dstflag = 0; +} +#define AXIS2_PLATFORM_GET_TIME_IN_MILLIS axis2_android_ftime +#define AXIS2_PLATFORM_TIMEB axis2_android_timeb +#else +#define AXIS2_PLATFORM_GET_TIME_IN_MILLIS ftime +#define AXIS2_PLATFORM_TIMEB timeb +#endif #include <errno.h> #include <sys/param.h> diff --git a/util/src/Makefile.am b/util/src/Makefile.am index 8a6998afe..cac0dbba6 100644 --- a/util/src/Makefile.am +++ b/util/src/Makefile.am @@ -55,7 +55,7 @@ libaxutil_la_SOURCES = hash.c \ digest_calc.c libaxutil_la_LIBADD = $(top_builddir)/src/platforms/unix/libaxis2_unix.la \ - -lpthread \ + @PTHREADLIBS@ \ @ZLIBLIBS@ libaxutil_la_LDFLAGS = -version-info $(VERSION_NO) diff --git a/util/src/platforms/unix/date_time_util_unix.c b/util/src/platforms/unix/date_time_util_unix.c index e0c4a2854..e56016f67 100644 --- a/util/src/platforms/unix/date_time_util_unix.c +++ b/util/src/platforms/unix/date_time_util_unix.c @@ -20,6 +20,12 @@ AXIS2_EXTERN int AXIS2_CALL axis2_platform_get_milliseconds() { +#ifdef __ANDROID__ + /* Android doesn't have sys/timeb.h or ftime(), use clock_gettime() instead */ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (int)(ts.tv_nsec / 1000000); +#else struct timeb t_current; int milliseconds; @@ -27,5 +33,5 @@ axis2_platform_get_milliseconds() milliseconds = t_current.millitm; return milliseconds; - +#endif } diff --git a/util/src/platforms/unix/thread_unix.c b/util/src/platforms/unix/thread_unix.c index d05a00dcf..571d06cda 100644 --- a/util/src/platforms/unix/thread_unix.c +++ b/util/src/platforms/unix/thread_unix.c @@ -122,7 +122,7 @@ axutil_thread_create( if(attr) { temp = &(attr->attr); - new->detached = axutil_threadattr_detach_get(temp); + new->detached = axutil_threadattr_detach_get(attr); }
