mirror of
https://gitcode.com/gh_mirrors/on/onvif.git
synced 2025-12-30 05:22:27 +00:00
Merge pull request #40 from lunasaw/dev-fix-obj
Implement lazy initialization and caching for ONVIF services
This commit is contained in:
@@ -45,12 +45,15 @@ public class OnvifDevice {
|
||||
private final URL url; // Example http://host:port, https://host, http://host, http://ip_address
|
||||
|
||||
private Device device;
|
||||
private Media media;
|
||||
private PTZ ptz;
|
||||
private ImagingPort imaging;
|
||||
private EventPortType events;
|
||||
private volatile Media media;
|
||||
private volatile PTZ ptz;
|
||||
private volatile ImagingPort imaging;
|
||||
private volatile EventPortType events;
|
||||
public final EventService eventService = new EventService();
|
||||
|
||||
// Cache capabilities to avoid repeated network requests
|
||||
private volatile Capabilities capabilities;
|
||||
|
||||
private static boolean verbose = false; // enable/disable logging of SOAP messages
|
||||
final SimpleSecurityHandler securityHandler;
|
||||
|
||||
@@ -112,56 +115,43 @@ public class OnvifDevice {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initalizes the addresses used for SOAP messages and to get the internal IP, if given IP is a
|
||||
* proxy.
|
||||
* Initialize ONVIF device connection, only creates Device service, other services use lazy initialization
|
||||
* This strategy significantly reduces initial memory usage and startup time
|
||||
*
|
||||
* @throws ConnectException Get thrown if device doesn't give answers to GetCapabilities()
|
||||
* @throws SOAPException
|
||||
* @throws ConnectException Thrown when device is inaccessible or invalid, doesn't respond to SOAP messages
|
||||
* @throws SOAPException SOAP related exceptions
|
||||
*/
|
||||
protected void init() throws ConnectException, SOAPException {
|
||||
logger.debug("Initializing ONVIF device connection: {}", url);
|
||||
|
||||
DeviceService deviceService = new DeviceService(null, DeviceService.SERVICE);
|
||||
|
||||
BindingProvider deviceServicePort = (BindingProvider) deviceService.getDevicePort();
|
||||
this.device =
|
||||
getServiceProxy(deviceServicePort, url.toString() + DEVICE_SERVICE).create(Device.class);
|
||||
|
||||
// resetSystemDateAndTime(); // don't modify the camera in a constructor.. :)
|
||||
// Use OnvifServiceFactory to create Device service proxy
|
||||
this.device = OnvifServiceFactory.createServiceProxy(
|
||||
deviceServicePort,
|
||||
url.toString() + DEVICE_SERVICE,
|
||||
Device.class,
|
||||
securityHandler,
|
||||
verbose
|
||||
);
|
||||
|
||||
Capabilities capabilities = this.device.getCapabilities(List.of(CapabilityCategory.ALL));
|
||||
if (capabilities == null) {
|
||||
// Get and cache capabilities to avoid subsequent repeated network requests
|
||||
this.capabilities = this.device.getCapabilities(List.of(CapabilityCategory.ALL));
|
||||
if (this.capabilities == null) {
|
||||
throw new ConnectException("Capabilities not reachable.");
|
||||
}
|
||||
|
||||
if (capabilities.getMedia() != null && capabilities.getMedia().getXAddr() != null) {
|
||||
this.media = new MediaService().getMediaPort();
|
||||
this.media =
|
||||
getServiceProxy((BindingProvider) media, capabilities.getMedia().getXAddr())
|
||||
.create(Media.class);
|
||||
}
|
||||
|
||||
if (capabilities.getPTZ() != null && capabilities.getPTZ().getXAddr() != null) {
|
||||
this.ptz = new PtzService().getPtzPort();
|
||||
this.ptz =
|
||||
getServiceProxy((BindingProvider) ptz, capabilities.getPTZ().getXAddr())
|
||||
.create(PTZ.class);
|
||||
}
|
||||
|
||||
if (capabilities.getImaging() != null && capabilities.getImaging().getXAddr() != null) {
|
||||
this.imaging = new ImagingService().getImagingPort();
|
||||
this.imaging =
|
||||
getServiceProxy((BindingProvider) imaging, capabilities.getImaging().getXAddr())
|
||||
.create(ImagingPort.class);
|
||||
}
|
||||
|
||||
if (capabilities.getEvents() != null && capabilities.getEvents().getXAddr() != null) {
|
||||
this.events = eventService.getEventPort();
|
||||
this.events =
|
||||
getServiceProxy((BindingProvider) events, capabilities.getEvents().getXAddr())
|
||||
.create(EventPortType.class);
|
||||
}
|
||||
logger.debug("Device initialization completed, capabilities cached, other services will be initialized on demand");
|
||||
// Note: Other services (Media, PTZ, Imaging, Events) now use lazy initialization
|
||||
// They will only be created on first access, significantly reducing memory usage
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use OnvifServiceFactory instead for better memory management and Schema caching
|
||||
* This method is retained only to ensure backward compatibility
|
||||
*/
|
||||
@Deprecated
|
||||
public JaxWsProxyFactoryBean getServiceProxy(BindingProvider servicePort, String serviceAddr) {
|
||||
|
||||
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
|
||||
@@ -236,22 +226,125 @@ public class OnvifDevice {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached device capabilities information
|
||||
* This information was obtained and cached during initialization to avoid repeated network requests
|
||||
*/
|
||||
public Capabilities getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public PTZ getPtz() {
|
||||
initPtzService();
|
||||
return ptz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of PTZ service
|
||||
* Uses double-checked locking pattern to ensure thread safety
|
||||
*/
|
||||
private void initPtzService() {
|
||||
if (ptz == null && capabilities.getPTZ() != null && capabilities.getPTZ().getXAddr() != null) {
|
||||
synchronized (this) {
|
||||
if (ptz == null) {
|
||||
logger.debug("Lazy initializing PTZ service");
|
||||
PtzService ptzService = new PtzService();
|
||||
BindingProvider ptzServicePort = (BindingProvider) ptzService.getPtzPort();
|
||||
this.ptz = OnvifServiceFactory.createServiceProxy(
|
||||
ptzServicePort,
|
||||
capabilities.getPTZ().getXAddr(),
|
||||
PTZ.class,
|
||||
securityHandler,
|
||||
verbose
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Media getMedia() {
|
||||
initMediaService();
|
||||
return media;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of Media service
|
||||
* Uses double-checked locking pattern to ensure thread safety
|
||||
*/
|
||||
private void initMediaService() {
|
||||
if (media == null && capabilities.getMedia() != null && capabilities.getMedia().getXAddr() != null) {
|
||||
synchronized (this) {
|
||||
if (media == null) {
|
||||
logger.debug("Lazy initializing Media service");
|
||||
MediaService mediaService = new MediaService();
|
||||
BindingProvider mediaServicePort = (BindingProvider) mediaService.getMediaPort();
|
||||
this.media = OnvifServiceFactory.createServiceProxy(
|
||||
mediaServicePort,
|
||||
capabilities.getMedia().getXAddr(),
|
||||
Media.class,
|
||||
securityHandler,
|
||||
verbose
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImagingPort getImaging() {
|
||||
initImagingService();
|
||||
return imaging;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of Imaging service
|
||||
* Uses double-checked locking pattern to ensure thread safety
|
||||
*/
|
||||
private void initImagingService() {
|
||||
if (imaging == null && capabilities.getImaging() != null && capabilities.getImaging().getXAddr() != null) {
|
||||
synchronized (this) {
|
||||
if (imaging == null) {
|
||||
logger.debug("Lazy initializing Imaging service");
|
||||
ImagingService imagingService = new ImagingService();
|
||||
BindingProvider imagingServicePort = (BindingProvider) imagingService.getImagingPort();
|
||||
this.imaging = OnvifServiceFactory.createServiceProxy(
|
||||
imagingServicePort,
|
||||
capabilities.getImaging().getXAddr(),
|
||||
ImagingPort.class,
|
||||
securityHandler,
|
||||
verbose
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EventPortType getEvents() {
|
||||
initEventsService();
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of Events service
|
||||
* Uses double-checked locking pattern to ensure thread safety
|
||||
*/
|
||||
private void initEventsService() {
|
||||
if (events == null && capabilities.getEvents() != null && capabilities.getEvents().getXAddr() != null) {
|
||||
synchronized (this) {
|
||||
if (events == null) {
|
||||
logger.debug("Lazy initializing Events service");
|
||||
BindingProvider eventsServicePort = (BindingProvider) eventService.getEventPort();
|
||||
this.events = OnvifServiceFactory.createServiceProxy(
|
||||
eventsServicePort,
|
||||
capabilities.getEvents().getXAddr(),
|
||||
EventPortType.class,
|
||||
securityHandler,
|
||||
verbose
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime getDate() {
|
||||
return device.getSystemDateAndTime().getLocalDateTime();
|
||||
}
|
||||
@@ -281,9 +374,12 @@ public class OnvifDevice {
|
||||
|
||||
// returns http://host[:port]/path_for_snapshot
|
||||
public String getSnapshotUri(String profileToken) {
|
||||
MediaUri sceenshotUri = media.getSnapshotUri(profileToken);
|
||||
if (sceenshotUri != null) {
|
||||
return sceenshotUri.getUri();
|
||||
Media mediaService = getMedia();
|
||||
if (mediaService != null) {
|
||||
MediaUri sceenshotUri = mediaService.getSnapshotUri(profileToken);
|
||||
if (sceenshotUri != null) {
|
||||
return sceenshotUri.getUri();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -298,24 +394,34 @@ public class OnvifDevice {
|
||||
|
||||
// Get snapshot uri for profile with index
|
||||
public String getSnapshotUri(int index) {
|
||||
if (media.getProfiles().size() >= index)
|
||||
return getSnapshotUri(media.getProfiles().get(index).getToken());
|
||||
Media mediaService = getMedia();
|
||||
if (mediaService != null && mediaService.getProfiles().size() > index) {
|
||||
return getSnapshotUri(mediaService.getProfiles().get(index).getToken());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getStreamUri(int index) {
|
||||
return getStreamUri(media.getProfiles().get(index).getToken());
|
||||
Media mediaService = getMedia();
|
||||
if (mediaService != null && mediaService.getProfiles().size() > index) {
|
||||
return getStreamUri(mediaService.getProfiles().get(index).getToken());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// returns rtsp://host[:port]/path_for_rtsp
|
||||
public String getStreamUri(String profileToken) {
|
||||
StreamSetup streamSetup = new StreamSetup();
|
||||
Transport t = new Transport();
|
||||
t.setProtocol(TransportProtocol.RTSP);
|
||||
streamSetup.setTransport(t);
|
||||
streamSetup.setStream(StreamType.RTP_UNICAST);
|
||||
MediaUri rtsp = media.getStreamUri(streamSetup, profileToken);
|
||||
return rtsp != null ? rtsp.getUri() : "";
|
||||
Media mediaService = getMedia();
|
||||
if (mediaService != null) {
|
||||
StreamSetup streamSetup = new StreamSetup();
|
||||
Transport t = new Transport();
|
||||
t.setProtocol(TransportProtocol.RTSP);
|
||||
streamSetup.setTransport(t);
|
||||
streamSetup.setStream(StreamType.RTP_UNICAST);
|
||||
MediaUri rtsp = mediaService.getStreamUri(streamSetup, profileToken);
|
||||
return rtsp != null ? rtsp.getUri() : "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static boolean isVerbose() {
|
||||
@@ -325,4 +431,39 @@ public class OnvifDevice {
|
||||
public static void setVerbose(boolean verbose) {
|
||||
OnvifDevice.verbose = verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if service is initialized
|
||||
*/
|
||||
public boolean isServiceInitialized(String serviceName) {
|
||||
switch (serviceName.toLowerCase()) {
|
||||
case "media": return media != null;
|
||||
case "ptz": return ptz != null;
|
||||
case "imaging": return imaging != null;
|
||||
case "events": return events != null;
|
||||
case "device": return device != null;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of initialized services
|
||||
*/
|
||||
public int getInitializedServicesCount() {
|
||||
int count = 0;
|
||||
if (device != null) count++;
|
||||
if (media != null) count++;
|
||||
if (ptz != null) count++;
|
||||
if (imaging != null) count++;
|
||||
if (events != null) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources and clear cache (static method, affects all instances)
|
||||
* Recommended to call when application shuts down
|
||||
*/
|
||||
public static void cleanupResources() {
|
||||
OnvifServiceFactory.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
147
onvif-java/src/main/java/de/onvif/soap/OnvifServiceFactory.java
Normal file
147
onvif-java/src/main/java/de/onvif/soap/OnvifServiceFactory.java
Normal file
@@ -0,0 +1,147 @@
|
||||
package de.onvif.soap;
|
||||
|
||||
import jakarta.xml.ws.BindingProvider;
|
||||
import org.apache.cxf.binding.soap.Soap12;
|
||||
import org.apache.cxf.binding.soap.SoapBindingConfiguration;
|
||||
import org.apache.cxf.endpoint.Client;
|
||||
import org.apache.cxf.frontend.ClientProxy;
|
||||
import org.apache.cxf.interceptor.LoggingInInterceptor;
|
||||
import org.apache.cxf.interceptor.LoggingOutInterceptor;
|
||||
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
|
||||
import org.apache.cxf.transport.http.HTTPConduit;
|
||||
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Service proxy factory that caches and reuses WSDL Schema parsing results.
|
||||
* Significantly reduces memory usage and initialization time through caching mechanism.
|
||||
*
|
||||
* @author ONVIF Optimization Team
|
||||
*/
|
||||
public class OnvifServiceFactory {
|
||||
private static final Logger logger = LoggerFactory.getLogger(OnvifServiceFactory.class);
|
||||
|
||||
// Core: Cache parsed proxy factory configurations using Map
|
||||
private static final Map<String, JaxWsProxyFactoryBean> proxyCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Creates service proxy using cached Schema parsing results
|
||||
*
|
||||
* @param servicePort Service port binding provider
|
||||
* @param serviceAddr Service address
|
||||
* @param serviceClass Service class type
|
||||
* @param securityHandler Security handler
|
||||
* @param verbose Whether to enable verbose logging
|
||||
* @return Service proxy instance
|
||||
*/
|
||||
public static synchronized <T> T createServiceProxy(
|
||||
BindingProvider servicePort,
|
||||
String serviceAddr,
|
||||
Class<T> serviceClass,
|
||||
SimpleSecurityHandler securityHandler,
|
||||
boolean verbose) {
|
||||
|
||||
// Use service class name as cache key
|
||||
String cacheKey = serviceClass.getName();
|
||||
|
||||
JaxWsProxyFactoryBean proxyFactory = proxyCache.get(cacheKey);
|
||||
if (proxyFactory == null) {
|
||||
logger.debug("First-time creating service proxy {} - parsing and caching Schema", serviceClass.getSimpleName());
|
||||
// Parse Schema and cache on first creation
|
||||
proxyFactory = createProxyFactory(servicePort, securityHandler, verbose);
|
||||
proxyCache.put(cacheKey, proxyFactory);
|
||||
} else {
|
||||
logger.debug("Reusing cached service proxy configuration {} - skipping Schema parsing", serviceClass.getSimpleName());
|
||||
}
|
||||
|
||||
// Set specific address for each instance - create new factory instance to avoid concurrency issues
|
||||
JaxWsProxyFactoryBean instanceFactory = cloneProxyFactory(proxyFactory);
|
||||
if (serviceAddr != null) {
|
||||
instanceFactory.setAddress(serviceAddr);
|
||||
}
|
||||
|
||||
return instanceFactory.create(serviceClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create basic configuration for proxy factory
|
||||
*/
|
||||
private static JaxWsProxyFactoryBean createProxyFactory(
|
||||
BindingProvider servicePort,
|
||||
SimpleSecurityHandler securityHandler,
|
||||
boolean verbose) {
|
||||
|
||||
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
|
||||
proxyFactory.getHandlers();
|
||||
|
||||
proxyFactory.setServiceClass(servicePort.getClass());
|
||||
|
||||
SoapBindingConfiguration config = new SoapBindingConfiguration();
|
||||
config.setVersion(Soap12.getInstance());
|
||||
proxyFactory.setBindingConfig(config);
|
||||
|
||||
Client deviceClient = ClientProxy.getClient(servicePort);
|
||||
|
||||
if (verbose) {
|
||||
// Enable SOAP message logging (for debugging/development only)
|
||||
proxyFactory.getOutInterceptors().add(new LoggingOutInterceptor());
|
||||
proxyFactory.getInInterceptors().add(new LoggingInInterceptor());
|
||||
}
|
||||
|
||||
HTTPConduit http = (HTTPConduit) deviceClient.getConduit();
|
||||
if (securityHandler != null) {
|
||||
proxyFactory.getHandlers().add(securityHandler);
|
||||
}
|
||||
|
||||
HTTPClientPolicy httpClientPolicy = http.getClient();
|
||||
httpClientPolicy.setConnectionTimeout(36000);
|
||||
httpClientPolicy.setReceiveTimeout(32000);
|
||||
httpClientPolicy.setAllowChunking(false);
|
||||
|
||||
return proxyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone proxy factory to create independent instances
|
||||
*/
|
||||
private static JaxWsProxyFactoryBean cloneProxyFactory(JaxWsProxyFactoryBean original) {
|
||||
JaxWsProxyFactoryBean clone = new JaxWsProxyFactoryBean();
|
||||
|
||||
// Copy basic configuration
|
||||
clone.setServiceClass(original.getServiceClass());
|
||||
clone.setBindingConfig(original.getBindingConfig());
|
||||
|
||||
// Copy handlers
|
||||
clone.getHandlers().addAll(original.getHandlers());
|
||||
clone.getInInterceptors().addAll(original.getInInterceptors());
|
||||
clone.getOutInterceptors().addAll(original.getOutInterceptors());
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache - recommended to call when application shuts down
|
||||
*/
|
||||
public static void clearCache() {
|
||||
logger.info("Clearing OnvifServiceFactory cache, releasing {} cached entries", proxyCache.size());
|
||||
proxyCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current cache size
|
||||
*/
|
||||
public static int getCacheSize() {
|
||||
return proxyCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specified service type is already cached
|
||||
*/
|
||||
public static boolean isCached(Class<?> serviceClass) {
|
||||
return proxyCache.containsKey(serviceClass.getName());
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,22 @@ public class PullPointSubscriptionHandler {
|
||||
eventWs.createPullPointSubscription(cpps);
|
||||
|
||||
final String serviceAddress = getWSAAddress(resp.getSubscriptionReference());
|
||||
pullPointSubscription = device.getServiceProxy((BindingProvider) device.eventService.getEventPort(), serviceAddress).create(PullPointSubscription.class);
|
||||
subscriptionManager = device.getServiceProxy((BindingProvider) device.eventService.getEventPort(), serviceAddress).create(SubscriptionManager.class);
|
||||
// Use OnvifServiceFactory instead of deprecated getServiceProxy method
|
||||
BindingProvider eventServicePort = (BindingProvider) device.eventService.getEventPort();
|
||||
pullPointSubscription = OnvifServiceFactory.createServiceProxy(
|
||||
eventServicePort,
|
||||
serviceAddress,
|
||||
PullPointSubscription.class,
|
||||
device.securityHandler,
|
||||
OnvifDevice.isVerbose()
|
||||
);
|
||||
subscriptionManager = OnvifServiceFactory.createServiceProxy(
|
||||
eventServicePort,
|
||||
serviceAddress,
|
||||
SubscriptionManager.class,
|
||||
device.securityHandler,
|
||||
OnvifDevice.isVerbose()
|
||||
);
|
||||
|
||||
final Client pullPointSubscriptionProxy = ClientProxy.getClient(pullPointSubscription);
|
||||
final Client subscriptionManagerProxy = ClientProxy.getClient(subscriptionManager);
|
||||
@@ -102,7 +116,7 @@ public class PullPointSubscriptionHandler {
|
||||
var messageIdHdr = new Header(messageID, messageIDEl);
|
||||
var toHdr = new Header(to, toEl);
|
||||
var replyToHdr = new Header(replyTo, replyToEl);
|
||||
|
||||
|
||||
headers.clear();
|
||||
headers.add(actionHdr);
|
||||
headers.add(messageIdHdr);
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package org.onvif.client;
|
||||
|
||||
import de.onvif.soap.OnvifDevice;
|
||||
import de.onvif.soap.OnvifServiceFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ONVIF Device Memory Optimization Demo
|
||||
*
|
||||
* Demonstrates the effects of new lazy initialization and Schema caching features:
|
||||
* 1. Schema Caching: Avoid repeated WSDL parsing
|
||||
* 2. Lazy Initialization: Create services only when needed
|
||||
* 3. Capabilities Caching: Avoid repeated network requests
|
||||
*
|
||||
* @author ONVIF Optimization Team
|
||||
*/
|
||||
public class MemoryOptimizationDemo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MemoryOptimizationDemo.class);
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
LOG.info("=== ONVIF Device Memory Optimization Demo ===");
|
||||
|
||||
OnvifCredentials creds = GetTestDevice.getOnvifCredentials(args);
|
||||
if (creds == null) {
|
||||
LOG.error("Please provide valid ONVIF device credentials");
|
||||
return;
|
||||
}
|
||||
|
||||
// Demo 1: Single device memory optimization
|
||||
demonstrateSingleDeviceOptimization(creds);
|
||||
|
||||
// Demo 2: Multi-device Schema caching effects
|
||||
demonstrateMultiDeviceCaching(creds);
|
||||
|
||||
// Demo 3: Lazy initialization effects
|
||||
demonstrateLazyInitialization(creds);
|
||||
|
||||
// Clean up resources
|
||||
OnvifDevice.cleanupResources();
|
||||
LOG.info("=== Demo Completed ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* Demonstrate memory optimization effects for single device
|
||||
*/
|
||||
private static void demonstrateSingleDeviceOptimization(OnvifCredentials creds) {
|
||||
LOG.info("\n--- Demo 1: Single Device Memory Optimization ---");
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
long startMemory = getUsedMemory();
|
||||
|
||||
// Create device instance
|
||||
OnvifDevice device = new OnvifDevice(creds.getHost(), creds.getUser(), creds.getPassword());
|
||||
|
||||
long initTime = System.currentTimeMillis() - startTime;
|
||||
long initMemory = getUsedMemory() - startMemory;
|
||||
|
||||
LOG.info("🚀 Device initialization completed:");
|
||||
LOG.info(" ⏱️ Initialization time: {}ms", initTime);
|
||||
LOG.info(" 🧠 Initial memory usage: {}MB", initMemory / 1024 / 1024);
|
||||
LOG.info(" 📊 Initialized services: {}/5", device.getInitializedServicesCount());
|
||||
LOG.info(" 📋 Device info: {}", device.getDeviceInfo());
|
||||
|
||||
// Access services on demand
|
||||
LOG.info("\n📺 Accessing Media service...");
|
||||
if (device.getMedia() != null) {
|
||||
LOG.info(" ✅ Media service initialized (lazy loading)");
|
||||
LOG.info(" 📊 Currently initialized services: {}/5", device.getInitializedServicesCount());
|
||||
}
|
||||
|
||||
LOG.info("\n🎮 Checking PTZ service...");
|
||||
if (device.getPtz() != null) {
|
||||
LOG.info(" ✅ PTZ service initialized (lazy loading)");
|
||||
} else {
|
||||
LOG.info(" ❌ PTZ service not available");
|
||||
}
|
||||
|
||||
LOG.info("\n📊 Final statistics:");
|
||||
LOG.info(" Initialized services: {}/5", device.getInitializedServicesCount());
|
||||
LOG.info(" Total memory usage: {}MB", (getUsedMemory() - startMemory) / 1024 / 1024);
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Device connection failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Demonstrate multi-device Schema caching effects
|
||||
*/
|
||||
private static void demonstrateMultiDeviceCaching(OnvifCredentials creds) {
|
||||
LOG.info("\n--- Demo 2: Multi-Device Schema Caching Effects ---");
|
||||
|
||||
List<OnvifDevice> devices = new ArrayList<>();
|
||||
long totalStartTime = System.currentTimeMillis();
|
||||
long startMemory = getUsedMemory();
|
||||
|
||||
try {
|
||||
// Create multiple device instances to demonstrate caching effects
|
||||
int deviceCount = 3; // Create 3 connections to the same device for demo purposes
|
||||
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
long deviceStartTime = System.currentTimeMillis();
|
||||
|
||||
OnvifDevice device = new OnvifDevice(creds.getHost(), creds.getUser(), creds.getPassword());
|
||||
devices.add(device);
|
||||
|
||||
long deviceTime = System.currentTimeMillis() - deviceStartTime;
|
||||
LOG.info("📱 Device {} initialization time: {}ms (cache {})",
|
||||
i + 1, deviceTime, OnvifServiceFactory.getCacheSize() > 0 ? "active" : "inactive");
|
||||
}
|
||||
|
||||
long totalTime = System.currentTimeMillis() - totalStartTime;
|
||||
long totalMemory = getUsedMemory() - startMemory;
|
||||
|
||||
LOG.info("\n📈 Multi-device statistics:");
|
||||
LOG.info(" Device count: {}", deviceCount);
|
||||
LOG.info(" Total initialization time: {}ms", totalTime);
|
||||
LOG.info(" Average initialization time: {}ms", totalTime / deviceCount);
|
||||
LOG.info(" Total memory usage: {}MB", totalMemory / 1024 / 1024);
|
||||
LOG.info(" Average memory usage: {}MB", totalMemory / deviceCount / 1024 / 1024);
|
||||
LOG.info(" Schema cache entries: {}", OnvifServiceFactory.getCacheSize());
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Multi-device creation failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Demonstrate lazy initialization effects
|
||||
*/
|
||||
private static void demonstrateLazyInitialization(OnvifCredentials creds) {
|
||||
LOG.info("\n--- Demo 3: Lazy Initialization Effects ---");
|
||||
|
||||
try {
|
||||
OnvifDevice device = new OnvifDevice(creds.getHost(), creds.getUser(), creds.getPassword());
|
||||
|
||||
LOG.info("🏗️ Device creation completed, checking service initialization status:");
|
||||
logServiceStatus(device);
|
||||
|
||||
LOG.info("\n📺 First access to Media service...");
|
||||
device.getMedia();
|
||||
logServiceStatus(device);
|
||||
|
||||
LOG.info("\n🎮 First access to PTZ service...");
|
||||
device.getPtz();
|
||||
logServiceStatus(device);
|
||||
|
||||
LOG.info("\n🖼️ First access to Imaging service...");
|
||||
device.getImaging();
|
||||
logServiceStatus(device);
|
||||
|
||||
LOG.info("\n📡 First access to Events service...");
|
||||
device.getEvents();
|
||||
logServiceStatus(device);
|
||||
|
||||
LOG.info("\n🎯 Lazy initialization demo completed");
|
||||
LOG.info(" Advantage: Resources are allocated only when services are actually needed");
|
||||
LOG.info(" Result: Significantly reduced initial memory usage and startup time");
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Lazy initialization demo failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log service initialization status
|
||||
*/
|
||||
private static void logServiceStatus(OnvifDevice device) {
|
||||
LOG.info(" 📊 Service initialization status:");
|
||||
LOG.info(" Device: {} | Media: {} | PTZ: {} | Imaging: {} | Events: {}",
|
||||
device.isServiceInitialized("device") ? "✅" : "❌",
|
||||
device.isServiceInitialized("media") ? "✅" : "❌",
|
||||
device.isServiceInitialized("ptz") ? "✅" : "❌",
|
||||
device.isServiceInitialized("imaging") ? "✅" : "❌",
|
||||
device.isServiceInitialized("events") ? "✅" : "❌"
|
||||
);
|
||||
LOG.info(" Total: {}/5 services initialized", device.getInitializedServicesCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current used memory
|
||||
*/
|
||||
private static long getUsedMemory() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
runtime.gc(); // Suggest garbage collection for more accurate memory readings
|
||||
return runtime.totalMemory() - runtime.freeMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format memory size
|
||||
*/
|
||||
private static String formatMemory(long bytes) {
|
||||
return String.format("%.2f MB", bytes / 1024.0 / 1024.0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user