diff --git a/src/main/java/io/ebean/ProfileLocation.java b/src/main/java/io/ebean/ProfileLocation.java
new file mode 100644
index 000000000..c7015ec7c
--- /dev/null
+++ b/src/main/java/io/ebean/ProfileLocation.java
@@ -0,0 +1,37 @@
+package io.ebean;
+
+/**
+ * A location for profiling transactions and queries.
+ *
+ * Typically represents a class method in the form of class file and line of code that started
+ * the transaction or invoked the query.
+ *
+ */
+public interface ProfileLocation {
+
+ /**
+ * Create and return a new ProfileLocation.
+ */
+ static ProfileLocation create() {
+ return XServiceProvider.profileLocationFactory().create();
+ }
+
+ /**
+ * Create and return a new ProfileLocation with a given lineNumber.
+ */
+ static ProfileLocation create(int lineNumber) {
+ return XServiceProvider.profileLocationFactory().create(lineNumber);
+ }
+
+ /**
+ * Create and return a new ProfileLocation with a given location.
+ */
+ static ProfileLocation create(String location) {
+ return XServiceProvider.profileLocationFactory().create(location);
+ }
+
+ /**
+ * Obtain the location description.
+ */
+ String obtain();
+}
diff --git a/src/main/java/io/ebean/Query.java b/src/main/java/io/ebean/Query.java
index 0ffb4503f..53499f9c6 100644
--- a/src/main/java/io/ebean/Query.java
+++ b/src/main/java/io/ebean/Query.java
@@ -1369,6 +1369,12 @@ public interface Query {
*/
Query setProfileId(int profileId);
+ /**
+ * Set the profile location of this query. This is used to relate query execution metrics
+ * back to a location like a specific line of code.
+ */
+ Query setProfileLocation(ProfileLocation profileLocation);
+
/**
* Set to true if this query should execute against the doc store.
*
diff --git a/src/main/java/io/ebean/RawSqlBuilder.java b/src/main/java/io/ebean/RawSqlBuilder.java
index 4cebe5832..3d1e4f4ca 100644
--- a/src/main/java/io/ebean/RawSqlBuilder.java
+++ b/src/main/java/io/ebean/RawSqlBuilder.java
@@ -21,7 +21,7 @@ public interface RawSqlBuilder {
* resultSet.
*/
static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
- return XServiceProvider.get().resultSet(resultSet, propertyNames);
+ return XServiceProvider.rawSql().resultSet(resultSet, propertyNames);
}
/**
@@ -30,7 +30,7 @@ public interface RawSqlBuilder {
* this query.
*/
static RawSqlBuilder unparsed(String sql) {
- return XServiceProvider.get().unparsed(sql);
+ return XServiceProvider.rawSql().unparsed(sql);
}
/**
@@ -47,7 +47,7 @@ public interface RawSqlBuilder {
*
*/
static RawSqlBuilder parse(String sql) {
- return XServiceProvider.get().parsed(sql);
+ return XServiceProvider.rawSql().parsed(sql);
}
/**
diff --git a/src/main/java/io/ebean/TxScope.java b/src/main/java/io/ebean/TxScope.java
index 76bb55582..bebe6a31d 100644
--- a/src/main/java/io/ebean/TxScope.java
+++ b/src/main/java/io/ebean/TxScope.java
@@ -52,6 +52,8 @@ public final class TxScope {
private ArrayList> noRollbackFor;
+ private ProfileLocation profileLocation;
+
/**
* Helper method to create a TxScope with REQUIRES.
*/
@@ -185,6 +187,21 @@ public final class TxScope {
return this;
}
+ /**
+ * Return the profile location.
+ */
+ public ProfileLocation getProfileLocation() {
+ return profileLocation;
+ }
+
+ /**
+ * Set the profile location.
+ */
+ public TxScope setProfileLocation(ProfileLocation profileLocation) {
+ this.profileLocation = profileLocation;
+ return this;
+ }
+
/**
* Return the batch mode.
*/
diff --git a/src/main/java/io/ebean/UpdateQuery.java b/src/main/java/io/ebean/UpdateQuery.java
index 98bb7b610..b4633a462 100644
--- a/src/main/java/io/ebean/UpdateQuery.java
+++ b/src/main/java/io/ebean/UpdateQuery.java
@@ -148,6 +148,12 @@ public interface UpdateQuery {
*/
UpdateQuery setRaw(String propertyExpression, Object... values);
+ /**
+ * Set the profile location of this update query. This is used to relate query execution metrics
+ * back to a location like a specific line of code.
+ */
+ UpdateQuery setProfileLocation(ProfileLocation profileLocation);
+
/**
* Return the query expression list to add predicates to.
*/
diff --git a/src/main/java/io/ebean/XServiceProvider.java b/src/main/java/io/ebean/XServiceProvider.java
index 4de4be54c..518e1b3af 100644
--- a/src/main/java/io/ebean/XServiceProvider.java
+++ b/src/main/java/io/ebean/XServiceProvider.java
@@ -1,5 +1,6 @@
package io.ebean;
+import io.ebean.service.SpiProfileLocationFactory;
import io.ebean.service.SpiRawSqlService;
import java.util.Iterator;
@@ -10,9 +11,11 @@ import java.util.ServiceLoader;
*/
class XServiceProvider {
- private static SpiRawSqlService builder = init();
+ private static SpiRawSqlService rawSqlService = initRawSql();
- private static SpiRawSqlService init() {
+ private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
+
+ private static SpiRawSqlService initRawSql() {
Iterator loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
if (loader.hasNext()) {
@@ -21,10 +24,27 @@ class XServiceProvider {
throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
}
+ private static SpiProfileLocationFactory initProfileLocation() {
+
+ Iterator loader = ServiceLoader.load(SpiProfileLocationFactory.class).iterator();
+ if (loader.hasNext()) {
+ return loader.next();
+ }
+ throw new IllegalStateException("No service implementation found for SpiProfileLocationFactory?");
+ }
+
/**
* Return the RawSqlService implementation.
*/
- static SpiRawSqlService get() {
- return builder;
+ static SpiRawSqlService rawSql() {
+ return rawSqlService;
}
+
+ /**
+ * Return the RawSqlService implementation.
+ */
+ static SpiProfileLocationFactory profileLocationFactory() {
+ return profileLocationFactory;
+ }
+
}
diff --git a/src/main/java/io/ebean/service/SpiProfileLocationFactory.java b/src/main/java/io/ebean/service/SpiProfileLocationFactory.java
new file mode 100644
index 000000000..79d9b8f5a
--- /dev/null
+++ b/src/main/java/io/ebean/service/SpiProfileLocationFactory.java
@@ -0,0 +1,24 @@
+package io.ebean.service;
+
+import io.ebean.ProfileLocation;
+
+/**
+ * Factory for creating profile locations.
+ */
+public interface SpiProfileLocationFactory {
+
+ /**
+ * Create a profile location.
+ */
+ ProfileLocation create();
+
+ /**
+ * Create a profile location with a line number.
+ */
+ ProfileLocation create(int lineNumber);
+
+ /**
+ * Create a known location.
+ */
+ ProfileLocation create(String location);
+}
diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java
index d257a4056..ea5ef91bd 100644
--- a/src/main/java/io/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java
@@ -5,6 +5,7 @@ import io.ebean.EbeanServer;
import io.ebean.ExpressionList;
import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
+import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.bean.CallStack;
import io.ebean.bean.ObjectGraphNode;
@@ -173,6 +174,11 @@ public interface SpiQuery extends Query, TxnProfileEventCodes {
*/
short getProfileId();
+ /**
+ * Return the profile location for this query.
+ */
+ ProfileLocation getProfileLocation();
+
/**
* Check for a single "equal to" expression for the Id.
*/
diff --git a/src/main/java/io/ebeaninternal/api/SpiTransaction.java b/src/main/java/io/ebeaninternal/api/SpiTransaction.java
index 66d7f1fc9..64d261f88 100644
--- a/src/main/java/io/ebeaninternal/api/SpiTransaction.java
+++ b/src/main/java/io/ebeaninternal/api/SpiTransaction.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
+import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
@@ -298,4 +299,14 @@ public interface SpiTransaction extends Transaction {
* Return the stream that profiling events are written to.
*/
ProfileStream profileStream();
+
+ /**
+ * Set the profile location for this transaction.
+ */
+ void setProfileLocation(ProfileLocation profileLocation);
+
+ /**
+ * Return the profile location for this transaction.
+ */
+ ProfileLocation getProfileLocation();
}
diff --git a/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java b/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
index a534898ce..5e0f7d415 100644
--- a/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
+++ b/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
+import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -54,6 +55,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.profileStream();
}
+ @Override
+ public void setProfileLocation(ProfileLocation profileLocation) {
+ transaction.setProfileLocation(profileLocation);
+ }
+
+ @Override
+ public ProfileLocation getProfileLocation() {
+ return transaction.getProfileLocation();
+ }
+
@Override
public void setTenantId(Object tenantId) {
transaction.setTenantId(tenantId);
diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
index d838c233e..30a722a45 100644
--- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
@@ -12,6 +12,7 @@ import io.ebean.FutureList;
import io.ebean.FutureRowCount;
import io.ebean.PagedList;
import io.ebean.PersistenceContextScope;
+import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.SqlQuery;
@@ -834,6 +835,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
break;
default:
transaction = transactionManager.createTransaction(txScope.getProfileId(), true, txScope.getIsolationLevel());
+ initNewTransaction(transaction, txScope);
}
}
@@ -844,6 +846,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return txnContainer;
}
+ private void initNewTransaction(SpiTransaction transaction, TxScope txScope) {
+
+ ProfileLocation profileLocation = txScope.getProfileLocation();
+ if (profileLocation != null) {
+ transaction.setProfileLocation(profileLocation);
+ }
+ }
+
private TxScope initTxScope(TxScope txScope) {
if (txScope == null) {
return new TxScope();
diff --git a/src/main/java/io/ebeaninternal/server/core/NoTransaction.java b/src/main/java/io/ebeaninternal/server/core/NoTransaction.java
index 58a2574d4..5b826035b 100644
--- a/src/main/java/io/ebeaninternal/server/core/NoTransaction.java
+++ b/src/main/java/io/ebeaninternal/server/core/NoTransaction.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
+import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -416,4 +417,14 @@ class NoTransaction implements SpiTransaction {
public ProfileStream profileStream() {
return null;
}
+
+ @Override
+ public void setProfileLocation(ProfileLocation profileLocation) {
+
+ }
+
+ @Override
+ public ProfileLocation getProfileLocation() {
+ return null;
+ }
}
diff --git a/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java b/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java
new file mode 100644
index 000000000..543f99c91
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java
@@ -0,0 +1,24 @@
+package io.ebeaninternal.server.profile;
+
+import io.ebean.ProfileLocation;
+
+/**
+ * Fixed / given location. Used internally for find by id and find all.
+ */
+class BasicProfileLocation implements ProfileLocation {
+
+ private final String location;
+
+ BasicProfileLocation(String location) {
+ this.location = location;
+ }
+
+ public String toString() {
+ return "location: " + location;
+ }
+
+ public String obtain() {
+ return location;
+ }
+
+}
diff --git a/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java b/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java
new file mode 100644
index 000000000..b64129b86
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java
@@ -0,0 +1,55 @@
+package io.ebeaninternal.server.profile;
+
+import io.ebean.ProfileLocation;
+
+/**
+ * Default profile location that uses stack trace.
+ */
+class DProfileLocation implements ProfileLocation {
+
+ private static final String IO_EBEAN = "io.ebean";
+
+ private static final String UNKNOWN = "unknown";
+
+ private String location;
+
+ private final int lineNumber;
+
+ DProfileLocation() {
+ this.lineNumber = 0;
+ }
+
+ DProfileLocation(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public String toString() {
+ return "location: " + location;
+ }
+
+ public String obtain() {
+ // atomic assignment so happy with this
+ if (location == null) {
+ location = create();
+ }
+ return location;
+ }
+
+ private String create() {
+ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+ for (int i = 3; i < trace.length; i++) {
+ if (!trace[i].getClassName().startsWith(IO_EBEAN)) {
+ return withLineNumber(trace[i].toString());
+ }
+ }
+ return UNKNOWN;
+ }
+
+ private String withLineNumber(String traceLine) {
+ if (lineNumber == 0 || traceLine.contains(":")) {
+ return traceLine;
+ } else {
+ return traceLine.substring(0, traceLine.length() - 1) + ":" + lineNumber + ")";
+ }
+ }
+}
diff --git a/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java b/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java
new file mode 100644
index 000000000..8d4e592d0
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java
@@ -0,0 +1,25 @@
+package io.ebeaninternal.server.profile;
+
+import io.ebean.ProfileLocation;
+import io.ebean.service.SpiProfileLocationFactory;
+
+/**
+ * Default implementation of the profile location factory.
+ */
+public class DProfileLocationFactory implements SpiProfileLocationFactory {
+
+ @Override
+ public ProfileLocation create() {
+ return new DProfileLocation();
+ }
+
+ @Override
+ public ProfileLocation create(int lineNumber) {
+ return new DProfileLocation(lineNumber);
+ }
+
+ @Override
+ public ProfileLocation create(String location) {
+ return new BasicProfileLocation(location);
+ }
+}
diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 562ee9d9b..29c7409ae 100644
--- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -14,6 +14,7 @@ import io.ebean.OrderBy;
import io.ebean.OrderBy.Property;
import io.ebean.PagedList;
import io.ebean.PersistenceContextScope;
+import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.RawSql;
@@ -257,6 +258,8 @@ public class DefaultOrmQuery implements SpiQuery {
*/
private short profileId;
+ private ProfileLocation profileLocation;
+
public DefaultOrmQuery(BeanDescriptor desc, EbeanServer server, ExpressionFactory expressionFactory) {
this.beanDescriptor = desc;
this.beanType = desc.getBeanType();
@@ -304,6 +307,12 @@ public class DefaultOrmQuery implements SpiQuery {
return this;
}
+ @Override
+ public Query setProfileLocation(ProfileLocation profileLocation) {
+ this.profileLocation = profileLocation;
+ return this;
+ }
+
@Override
public boolean isAutoTunable() {
return nativeSql == null && beanDescriptor.isAutoTunable();
@@ -1732,4 +1741,9 @@ public class DefaultOrmQuery implements SpiQuery {
public OrmUpdateProperties getUpdateProperties() {
return updateProperties;
}
+
+ @Override
+ public ProfileLocation getProfileLocation() {
+ return profileLocation;
+ }
}
diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultUpdateQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultUpdateQuery.java
index b99250331..f9189e88b 100644
--- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultUpdateQuery.java
+++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultUpdateQuery.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.querydefn;
import io.ebean.ExpressionList;
+import io.ebean.ProfileLocation;
import io.ebean.UpdateQuery;
/**
@@ -41,6 +42,12 @@ public class DefaultUpdateQuery implements UpdateQuery {
return this;
}
+ @Override
+ public UpdateQuery setProfileLocation(ProfileLocation profileLocation) {
+ query.setProfileLocation(profileLocation);
+ return this;
+ }
+
@Override
public ExpressionList where() {
return query.where();
diff --git a/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java
index e17444252..3699eb575 100644
--- a/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java
+++ b/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.transaction;
+import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -92,6 +93,16 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
return null;
}
+ @Override
+ public void setProfileLocation(ProfileLocation profileLocation) {
+ throw new IllegalStateException(notExpectedMessage);
+ }
+
+ @Override
+ public ProfileLocation getProfileLocation() {
+ return null;
+ }
+
@Override
public boolean isSkipCache() {
return false;
diff --git a/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
index 8dfc4abf1..f9b4034bb 100644
--- a/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
+++ b/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
@@ -1,15 +1,16 @@
package io.ebeaninternal.server.transaction;
+import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
-import io.ebean.bean.PersistenceContext;
import io.ebean.annotation.PersistBatch;
+import io.ebean.bean.PersistenceContext;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
-import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.SpiProfileTransactionEvent;
+import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEvent;
import io.ebeaninternal.api.TxnProfileEventCodes;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
@@ -173,6 +174,8 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
private final ProfileStream profileStream;
+ protected ProfileLocation profileLocation;
+
/**
* Create without ProfileStream option (no profiling).
*/
@@ -230,6 +233,16 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
return profileStream;
}
+ @Override
+ public void setProfileLocation(ProfileLocation profileLocation) {
+ this.profileLocation = profileLocation;
+ }
+
+ @Override
+ public ProfileLocation getProfileLocation() {
+ return profileLocation;
+ }
+
/**
* Overridden in AutoCommitJdbcTransaction as that expects to run/operate with autocommit true.
*/
diff --git a/src/main/resources/META-INF/services/io.ebean.service.SpiProfileLocationFactory b/src/main/resources/META-INF/services/io.ebean.service.SpiProfileLocationFactory
new file mode 100644
index 000000000..b370147ab
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.ebean.service.SpiProfileLocationFactory
@@ -0,0 +1 @@
+io.ebeaninternal.server.profile.DProfileLocationFactory
diff --git a/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java b/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java
new file mode 100644
index 000000000..14c3b9b22
--- /dev/null
+++ b/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java
@@ -0,0 +1,17 @@
+package io.ebeaninternal.server.profile;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BasicProfileLocationTest {
+
+ @Test
+ public void obtain() {
+
+ DProfileLocation loc = new DProfileLocation(12);
+ String obtain = loc.obtain();
+
+ assertThat(obtain).endsWith(":12)");
+ }
+}
diff --git a/src/test/java/org/tests/model/basic/finder/CustomerFinder.java b/src/test/java/org/tests/model/basic/finder/CustomerFinder.java
index 7a7cb0352..63bedcfff 100644
--- a/src/test/java/org/tests/model/basic/finder/CustomerFinder.java
+++ b/src/test/java/org/tests/model/basic/finder/CustomerFinder.java
@@ -35,8 +35,9 @@ public class CustomerFinder extends Finder {
* Find customer by unique name.
*/
public Customer byName(String name) {
-
- return query().where().eq("name", name).findOne();
+ return query().where()
+ .eq("name", name)
+ .findOne();
}
public List byNameStatus(String nameStartsWith, Customer.Status status) {
diff --git a/src/test/java/org/tests/profile/ProfileLocationTest.java b/src/test/java/org/tests/profile/ProfileLocationTest.java
new file mode 100644
index 000000000..49499f8b5
--- /dev/null
+++ b/src/test/java/org/tests/profile/ProfileLocationTest.java
@@ -0,0 +1,21 @@
+package org.tests.profile;
+
+import io.ebean.ProfileLocation;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProfileLocationTest {
+
+ private static ProfileLocation loc = ProfileLocation.create(12);
+
+ @Test
+ public void test() {
+
+ assertThat(doIt()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:19)");
+ }
+
+ private String doIt() {
+ return loc.obtain();
+ }
+}