-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue 29712 analytics collectors layer (#29791)
First draft for analytics collectors layer --------- Co-authored-by: freddyDOTCMS <[email protected]> Co-authored-by: Jose Castro <[email protected]> Co-authored-by: freddyDOTCMS <[email protected]>
- Loading branch information
1 parent
b4acc93
commit 2c9e54a
Showing
33 changed files
with
1,318 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.dotcms.analytics; | ||
|
||
import com.dotmarketing.beans.Host; | ||
import com.dotmarketing.business.APILocator; | ||
import com.dotmarketing.cms.urlmap.UrlMapContext; | ||
import com.dotmarketing.util.Logger; | ||
import com.dotmarketing.util.PageMode; | ||
import io.vavr.control.Try; | ||
|
||
import static com.dotcms.exception.ExceptionUtil.getErrorMessage; | ||
|
||
/** | ||
* This utility class exposes common-use methods for the Analytics APIs. | ||
* | ||
* @author Jose Castro | ||
* @since Sep 13th, 2024 | ||
*/ | ||
public class Util { | ||
|
||
private Util() { | ||
// Singleton | ||
} | ||
/** | ||
* Based on the specified URL Map Context, determines whether a given incoming URL maps to a URL | ||
* Mapped content or not. | ||
* | ||
* @param urlMapContext UrlMapContext object containing the following information: | ||
* @return If the URL maps to URL Mapped content, returns {@code true}. | ||
*/ | ||
public static boolean isUrlMap(final UrlMapContext urlMapContext) { | ||
return Try.of(() -> APILocator.getURLMapAPI().isUrlPattern(urlMapContext)) | ||
.onFailure(e -> Logger.error(Util.class, String.format("Failed to check for URL Mapped content for page '%s': %s", | ||
urlMapContext.getUri(), getErrorMessage(e)), e)) | ||
.getOrElse(false); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
dotCMS/src/main/java/com/dotcms/analytics/track/collectors/AsyncVanitiesCollector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.dotcms.analytics.track.collectors; | ||
|
||
import com.dotcms.analytics.track.matchers.VanitiesRequestMatcher; | ||
import com.dotcms.vanityurl.model.CachedVanityUrl; | ||
import com.dotcms.visitor.filter.characteristics.BaseCharacter; | ||
import com.dotmarketing.beans.Host; | ||
import com.dotmarketing.business.APILocator; | ||
import com.dotmarketing.filters.CMSFilter; | ||
import com.dotmarketing.filters.Constants; | ||
import com.dotmarketing.portlets.contentlet.business.HostAPI; | ||
import com.dotmarketing.util.UtilMethods; | ||
import io.vavr.control.Try; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* This asynchronized collector collects the page/asset information based on the vanity URL previous loaded on the | ||
* @author jsanca | ||
*/ | ||
public class AsyncVanitiesCollector implements Collector { | ||
|
||
private final HostAPI hostAPI; | ||
private final Map<CMSFilter.IAm, Collector> match = new HashMap<>(); | ||
|
||
public AsyncVanitiesCollector() { | ||
this(APILocator.getHostAPI()); | ||
} | ||
|
||
public AsyncVanitiesCollector(final HostAPI hostAPI) { | ||
|
||
this.hostAPI = hostAPI; | ||
|
||
match.put(CMSFilter.IAm.PAGE, new PagesCollector()); | ||
match.put(CMSFilter.IAm.FILE, new FilesCollector()); | ||
} | ||
|
||
@Override | ||
public boolean test(CollectorContextMap collectorContextMap) { | ||
final CachedVanityUrl cachedVanityUrl = (CachedVanityUrl)collectorContextMap.get(Constants.VANITY_URL_OBJECT); | ||
|
||
return VanitiesRequestMatcher.VANITIES_MATCHER_ID.equals(collectorContextMap.getRequestMatcher().getId()) && | ||
UtilMethods.isSet(cachedVanityUrl) && cachedVanityUrl.isForward(); | ||
} | ||
|
||
|
||
@Override | ||
public CollectorPayloadBean collect(final CollectorContextMap collectorContextMap, | ||
final CollectorPayloadBean collectorPayloadBean) { | ||
|
||
// this will be a new event | ||
final CachedVanityUrl cachedVanityUrl = (CachedVanityUrl) collectorContextMap.get(Constants.VANITY_URL_OBJECT); | ||
|
||
final Host currentHost = (Host)collectorContextMap.get("currentHost"); | ||
final Long languageId = (Long)collectorContextMap.get("langId"); | ||
|
||
final Host site = Try.of(()->this.hostAPI.find(currentHost.getIdentifier(), APILocator.systemUser(), | ||
false)).get(); | ||
final CMSFilter.IAm whoIAM = BaseCharacter.resolveResourceType(cachedVanityUrl.forwardTo, site, languageId); | ||
|
||
if (UtilMethods.isSet(whoIAM)) { | ||
|
||
final CollectorContextMap innerCollectorContextMap = new WrapperCollectorContextMap(collectorContextMap, | ||
Map.of("uri", cachedVanityUrl.forwardTo)); | ||
match.get(whoIAM).collect(innerCollectorContextMap, collectorPayloadBean); | ||
} | ||
|
||
collectorPayloadBean.put("comeFromVanityURL", "true"); | ||
return collectorPayloadBean; | ||
} | ||
|
||
@Override | ||
public boolean isAsync() { | ||
return true; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.dotcms.analytics.track.collectors; | ||
|
||
import com.dotcms.enterprise.cluster.ClusterFactory; | ||
import com.dotcms.util.FunctionUtils; | ||
import com.dotmarketing.business.APILocator; | ||
|
||
import java.time.Instant; | ||
import java.time.ZoneId; | ||
import java.time.ZonedDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.util.Objects; | ||
|
||
public class BasicProfileCollector implements Collector { | ||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"); | ||
@Override | ||
public boolean test(CollectorContextMap collectorContextMap) { | ||
|
||
return true; // every one needs a basic profile | ||
} | ||
|
||
@Override | ||
public CollectorPayloadBean collect(final CollectorContextMap collectorContextMap, | ||
final CollectorPayloadBean collectorPayloadBean) { | ||
|
||
final String requestId = (String)collectorContextMap.get("requestId"); | ||
final Long time = (Long)collectorContextMap.get("time"); | ||
final String clusterId = (String)collectorContextMap.get("cluster"); | ||
final String serverId = (String)collectorContextMap.get("server"); | ||
final String sessionId = (String)collectorContextMap.get("session"); | ||
final Boolean sessionNew = (Boolean)collectorContextMap.get("sessionNew"); | ||
|
||
final Long timestamp = FunctionUtils.getOrDefault(Objects.nonNull(time), () -> time, System::currentTimeMillis); | ||
final Instant instant = Instant.ofEpochMilli(timestamp); | ||
final ZonedDateTime zonedDateTimeUTC = instant.atZone(ZoneId.of("UTC")); | ||
|
||
collectorPayloadBean.put("request_id", requestId); | ||
collectorPayloadBean.put("utc_time", FORMATTER.format(zonedDateTimeUTC)); | ||
collectorPayloadBean.put("cluster", | ||
FunctionUtils.getOrDefault(Objects.nonNull(clusterId), ()->clusterId, ClusterFactory::getClusterId)); | ||
collectorPayloadBean.put("server", | ||
FunctionUtils.getOrDefault(Objects.nonNull(serverId), ()->serverId,()->APILocator.getServerAPI().readServerId())); | ||
collectorPayloadBean.put("sessionId", sessionId); | ||
collectorPayloadBean.put("sessionNew", sessionNew); | ||
return collectorPayloadBean; | ||
} | ||
|
||
@Override | ||
public boolean isAsync() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public boolean isEventCreator(){ | ||
return false; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
dotCMS/src/main/java/com/dotcms/analytics/track/collectors/CharacterCollectorContextMap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.dotcms.analytics.track.collectors; | ||
|
||
import com.dotcms.analytics.track.matchers.RequestMatcher; | ||
import com.dotcms.visitor.filter.characteristics.Character; | ||
|
||
import java.io.Serializable; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* This Context Map has the character map | ||
* @author jsanca | ||
*/ | ||
public class CharacterCollectorContextMap implements CollectorContextMap { | ||
|
||
private final Map<String, Object> contextMap = new HashMap<>(); | ||
private final RequestMatcher requestMatcher; | ||
private final Map<String, Serializable> characterMap; | ||
|
||
public CharacterCollectorContextMap(final Character character, | ||
final RequestMatcher requestMatcher, | ||
final Map<String, Object> contextMap) { | ||
|
||
this.characterMap = character.getMap(); | ||
this.requestMatcher = requestMatcher; | ||
this.contextMap.putAll(contextMap); | ||
} | ||
|
||
|
||
|
||
@Override | ||
public Object get(final String key) { | ||
|
||
if (this.characterMap.containsKey(key)) { | ||
return this.characterMap.get(key); | ||
} | ||
|
||
if (this.contextMap.containsKey(key)) { | ||
return this.contextMap.get(key); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
||
@Override | ||
public RequestMatcher getRequestMatcher() { | ||
return this.requestMatcher; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
dotCMS/src/main/java/com/dotcms/analytics/track/collectors/Collector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.dotcms.analytics.track.collectors; | ||
|
||
/** | ||
* A collector command basically puts information into a collector payload bean | ||
* @author jsanca | ||
*/ | ||
public interface Collector { | ||
|
||
/** | ||
* Test if the collector should run | ||
* @param collectorContextMap | ||
* @return | ||
*/ | ||
boolean test(final CollectorContextMap collectorContextMap); | ||
/** | ||
* This method is called in order to fire the collector | ||
* @param collectorContextMap | ||
* @param collectorPayloadBean | ||
* @return CollectionCollectorPayloadBean | ||
*/ | ||
CollectorPayloadBean collect(final CollectorContextMap collectorContextMap, | ||
final CollectorPayloadBean collectorPayloadBean); | ||
|
||
/** | ||
* True if the collector should run async | ||
* @return boolean | ||
*/ | ||
default boolean isAsync() { | ||
return false; | ||
} | ||
|
||
/** | ||
* Return an id for the Collector, by default returns the class name. | ||
* @return | ||
*/ | ||
default String getId() { | ||
|
||
return this.getClass().getName(); | ||
} | ||
|
||
default boolean isEventCreator(){ | ||
return true; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
dotCMS/src/main/java/com/dotcms/analytics/track/collectors/CollectorContextMap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dotcms.analytics.track.collectors; | ||
|
||
import com.dotcms.analytics.track.matchers.RequestMatcher; | ||
|
||
public interface CollectorContextMap { | ||
|
||
Object get(String key); | ||
RequestMatcher getRequestMatcher(); // since we do not have the previous step phase we need to keep this as an object, but will be a RequestMatcher | ||
} |
17 changes: 17 additions & 0 deletions
17
dotCMS/src/main/java/com/dotcms/analytics/track/collectors/CollectorPayloadBean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.dotcms.analytics.track.collectors; | ||
|
||
import java.io.Serializable; | ||
import java.util.Map; | ||
|
||
/** | ||
* Encapsulate the basic signature for a collector payload bean | ||
* @author jsanca | ||
*/ | ||
public interface CollectorPayloadBean { | ||
|
||
CollectorPayloadBean put(String key, Serializable value); | ||
Serializable get(String key); | ||
Map<String, Serializable> toMap(); | ||
|
||
CollectorPayloadBean add(CollectorPayloadBean other); | ||
} |
Oops, something went wrong.