/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jdev.miniprofiler;
import io.jdev.miniprofiler.storage.MapStorage;
import io.jdev.miniprofiler.storage.Storage;
import io.jdev.miniprofiler.user.UserProvider;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.UUID;
/**
* Support class for profiler providers. This provides most functionality
* that a profiler provider will need, except for saving and retrieving
* the current profiler.
*/
public abstract class BaseProfilerProvider implements ProfilerProvider {
private Storage storage = new MapStorage();
private String machineName = getDefaultHostname();
private UserProvider userProvider;
private ProfilerUiConfig uiConfig;
/**
* Called after a new profiler is created. Subclasses should
* store the passed-in profiler for later retrieval in calls
* to {@link #getCurrentProfiler()}.
*
* @param profiler the newly created profiler
*/
protected abstract void profilerCreated(Profiler profiler);
/**
* Called after a profiler has been stopped. Subsequent calls
* to {@link #getCurrentProfiler()} should return null.
*
* @param profiler the stopped profiler
*/
protected abstract void profilerStopped(Profiler profiler);
/**
* Return the current profiler, if any.
*
* @return The current profiler, or null if none.
*/
protected abstract Profiler lookupCurrentProfiler();
/**
* Returns the current MiniProfiler.
*/
@Override
public final Profiler getCurrentProfiler() {
Profiler p = lookupCurrentProfiler();
return p != null ? p : NullProfiler.INSTANCE;
}
/**
* Indicates whether there is currently a profiler in the context.
* @return true if there is a profiler currently running
*/
@Override
public boolean hasCurrentProfiler() {
return lookupCurrentProfiler() != null;
}
/**
* Create a new profiling session with Info profiling level.
*
* @param rootName A name for the session. Thiis is used as the name
* of the root timing node for the session, and could be
* the currently rendering URL or background job name.
* @return the newly created profiler
*/
@Override
public Profiler start(String rootName) {
return start(rootName, ProfileLevel.Info);
}
/**
* Create a new profiling session with Info profiling level.
*
* @param id the UUID to use
* @param rootName A name for the session. Thiis is used as the name
* of the root timing node for the session, and could be
* the currently rendering URL or background job name.
* @return the newly created profiler
*/
@Override
public Profiler start(UUID id, String rootName) {
return start(id, rootName, ProfileLevel.Info);
}
/**
* Create a new profiling session.
*
* @param rootName A name for the session. Thiis is used as the name
* of the root timing node for the session, and could be
* the currently rendering URL or background job name.
* @param level The level of detail to use when profiling
* @return the newly created profiler
*/
@Override
public Profiler start(String rootName, ProfileLevel level) {
return start(null, rootName, level);
}
/**
* Start a new profiling session with the given level, root name and UUID.
*
* @param id the UUID to use
* @param rootName the name of the root timing step. This might often be the uri of the current request.
* @param level the level of the profiling session
* @return the new profiler
*/
@Override
public Profiler start(UUID id, String rootName, ProfileLevel level) {
ProfilerImpl profiler = new ProfilerImpl(id, rootName, rootName, level, this);
profiler.setMachineName(machineName);
if (userProvider != null) {
profiler.setUser(userProvider.getUser());
}
profilerCreated(profiler);
return profiler;
}
/**
* Ends the current profiling session, if one exists.
*
* @param discardResults When true, clears the miniprofiler for this request, allowing profiling to
* be prematurely stopped and discarded. Useful for when a specific route does not need to be profiled.
*/
@Override
public void stopCurrentSession(boolean discardResults) {
Profiler currentProfiler = getCurrentProfiler();
if (currentProfiler != null) {
currentProfiler.stop(discardResults);
}
}
/**
* Marks the given profiling session as stopped.
*
* @param profilingSession the profiler to register as stopped
* @param discardResults When true, clears the miniprofiler for this request, allowing profiling to
* be prematurely stopped and discarded. Useful for when a specific route does not need to be profiled.
*/
@Override
public void stopSession(ProfilerImpl profilingSession, boolean discardResults) {
profilingSession.stop();
String currentUser = userProvider != null ? userProvider.getUser() : null;
if (currentUser != null && profilingSession.getUser() == null) {
// user wasn't available at profile start, but is now
profilingSession.setUser(currentUser);
}
profilerStopped(profilingSession);
if (!discardResults) {
saveProfiler(profilingSession);
}
}
private void saveProfiler(ProfilerImpl currentProfiler) {
storage.save(currentProfiler);
}
/**
* Set the profiler storage to be used by this provider.
* <p>The profiler storage is where profiler sessions are stored
* to be retrieved later, either by an AJAX call immediately
* after a page render, or for later debugging.</p>
* <p> By default, the storage property is set to an in-memory
* {@link MapStorage}.</p>
*
* @param storage the storage option to use
* @see Storage
*/
public void setStorage(Storage storage) {
this.storage = storage;
}
/**
* Return the current storage implementation.
*
* @return current storage
*/
public Storage getStorage() {
return storage;
}
/**
* Sets the machine name for the current machine. In unset this defaults
* to the local host name, as determined by {@link #getDefaultHostname()}.
*
* @param machineName the machine name to use
*/
public void setMachineName(String machineName) {
this.machineName = machineName;
}
/**
* Returnes the current machine name set
*
* @return the current machine name
*/
public String getMachineName() {
return machineName;
}
/**
* Sets the user provider for this profiler provider.
*
* @param userProvider The user provider to use.
* @see UserProvider
*/
public void setUserProvider(UserProvider userProvider) {
this.userProvider = userProvider;
}
/**
* Return the local host name, or the loopback name (usually "localhost")
* if it doesn't resolve in DNS.
*
* @return host name as returned by {@link InetAddress#getHostName()}
*/
public String getDefaultHostname() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
// local host name doesn't resolve, just use localhost
return "localhost";
}
}
/**
* Returns the {@link ProfilerUiConfig} associated with this provider.
*
* @return the provider's UI config
*/
@Override
public ProfilerUiConfig getUiConfig() {
if (uiConfig == null) {
uiConfig = ProfilerUiConfig.create();
}
return uiConfig;
}
/**
* Sets the {@link ProfilerUiConfig} for this provider to use.
*
* @param uiConfig the UI config to use
*/
@Override
public void setUiConfig(ProfilerUiConfig uiConfig) {
this.uiConfig = uiConfig;
}
}