diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c6f6ae574..ae0025b5607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,21 +1,23 @@ # CHANGELOG ## Version 2.0.2 -- Fix #616, added a way to have real time SDK Logs when logging on File. -- Fix #609, fixes the inaccurate timestamp recorded with JMX Metrics. +- Fix [#577](https://github.com/Microsoft/ApplicationInsights-Java/issues/577), removed HttpFactory class as it was not being used. +- Fixed issue with sessionId not being set in request telemetry due to date parsing issues. +- Fix [#616](https://github.com/Microsoft/ApplicationInsights-Java/issues/616), added a way to have real time SDK Logs when logging on File. +- Fix [#609](https://github.com/Microsoft/ApplicationInsights-Java/issues/609), fixes the inaccurate timestamp recorded with JMX Metrics. - Added a way to configure MaxInstantRetries from XML. - Added the ability to have cold SDK initialization (no logs except critical logAlways messages) - Fix issue when dependency start time was not being recorded correctly. -- Fixed #533 HTTP Dependency Telemetry now matches with .NET SDK. +- Fixed [#533](https://github.com/Microsoft/ApplicationInsights-Java/issues/533) HTTP Dependency Telemetry now matches with .NET SDK. - Introduced public method `httpMethodFinishedWithPath(String identifier, String method, String path, String correlationId, String uri, String target, int result, long delta)` to support instrumentation of Path of URI in HTTP requests. - `httpMethodFinished(String identifier, String method, String correlationId, String uri, String target, int result, int delta)` is now marked as deprecated - Logger Messages now being pushed as custom dimension when reporting exceptions via Loggers. (#400) - Enhanced Log4j2 appender to support basic parameters including Filters, Layouts and includeException. (#348) - Fixed PageView telemetry data not being reported. -- Fixed Issue #526 (NPE in MapUtil.copy()) -- Fixed Issue #513 (Memory leak in SDKShutdownActivity). This fix upgrades our Servlet version from 2.5 to 3.0. The SDK must now be run on an application server supporting Servlet 3.0. -- Fixed Issue #504 (SDK initialization happens twice) to improve startup performance. +- Fixed Issue [#526](https://github.com/Microsoft/ApplicationInsights-Java/issues/526) (NPE in MapUtil.copy()) +- Fixed Issue [#513](https://github.com/Microsoft/ApplicationInsights-Java/issues/513) (Memory leak in SDKShutdownActivity). This fix upgrades our Servlet version from 2.5 to 3.0. The SDK must now be run on an application server supporting Servlet 3.0. +- Fixed Issue [#504](https://github.com/Microsoft/ApplicationInsights-Java/issues/504) (SDK initialization happens twice) to improve startup performance. ## Version 2.0.1 - Fix Inconsistency in artifact names in POM files diff --git a/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/HttpCookieFactory.java b/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/HttpCookieFactory.java deleted file mode 100644 index 1eaf7df6d35..00000000000 --- a/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/HttpCookieFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * ApplicationInsights-Java - * Copyright (c) Microsoft Corporation - * All rights reserved. - * - * MIT License - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the ""Software""), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE - * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -package com.microsoft.applicationinsights.web.internal.cookies; - -import java.util.Date; -import javax.servlet.http.Cookie; -import com.microsoft.applicationinsights.extensibility.context.SessionContext; -import com.microsoft.applicationinsights.internal.util.DateTimeUtils; -import com.microsoft.applicationinsights.web.internal.RequestTelemetryContext; - -/** - * Created by yonisha on 2/26/2015. - */ -public class HttpCookieFactory { - - private static final int COOKIE_VERSION = 1; - public static String COOKIE_PATH_ALL_URL = "/"; - - // region Public - - /** - * Generates session http cookie. - * @param context The context. - * @param sessionContext The request session context. - * @param sessionTimeoutInMinutes The session timeout in minutes. - * @return Session http cookie. - */ - public static Cookie generateSessionHttpCookie( - RequestTelemetryContext context, SessionContext sessionContext, int sessionTimeoutInMinutes) { - Date renewalDate = DateTimeUtils.getDateTimeNow(); - - String formattedCookie = SessionCookie.formatCookie(new String[] { - sessionContext.getId(), - String.valueOf(context.getSessionCookie().getSessionAcquisitionDate().getTime()), - String.valueOf(renewalDate.getTime()) - }); - - Cookie cookie = new Cookie(SessionCookie.COOKIE_NAME, formattedCookie); - - cookie.setMaxAge(sessionTimeoutInMinutes * 60); - - setCommonProperties(cookie); - - return cookie; - } - - /** - * Generates user http cookie. - * @param context The context. - * @return User http cookie. - */ - public static Cookie generateUserHttpCookie(RequestTelemetryContext context) { - String formattedCookie = UserCookie.formatCookie(new String[] { - context.getUserCookie().getUserId(), - DateTimeUtils.formatAsRoundTripDate(context.getUserCookie().getAcquisitionDate()) - }); - - Cookie cookie = new Cookie(UserCookie.COOKIE_NAME, formattedCookie); - cookie.setMaxAge(Integer.MAX_VALUE); - - setCommonProperties(cookie); - - return cookie; - } - - // endregion Public - - // region Private - - private static void setCommonProperties(Cookie cookie) { - cookie.setPath(COOKIE_PATH_ALL_URL); - cookie.setVersion(COOKIE_VERSION); - } - - // endregion Private -} diff --git a/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookie.java b/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookie.java index cf4193e11a2..889da5254e5 100644 --- a/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookie.java +++ b/web/src/main/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookie.java @@ -21,56 +21,22 @@ package com.microsoft.applicationinsights.web.internal.cookies; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; import javax.servlet.http.Cookie; -import com.microsoft.applicationinsights.internal.util.DateTimeUtils; /** * Created by yonisha on 2/4/2015. */ public class SessionCookie extends com.microsoft.applicationinsights.web.internal.cookies.Cookie { - // region Consts - - private static final int UPDATE_TIMEOUT_IN_MINUTES = 5; public static final String COOKIE_NAME = "ai_session"; - public static final int SESSION_DEFAULT_EXPIRATION_TIMEOUT_IN_MINUTES = 30; - - // endregion Consts - - // region Members - private String sessionId; - private Date acquisitionDate; - private Date renewalDate; - - private enum CookieFields { - SESSION_ID(0), - SESSION_ACQUISITION_DATE(1), - SESSION_LAST_UPDATE_DATE(2); - - private final int value; - - CookieFields(int value) { - this.value = value; - } - - public int getValue() { return value; } - } - - // endregion Members - - // region Ctor /** * Constructs new SessionCookie object from the given cookie. * @param cookie The http servlet cookie. - * @throws Exception Thrown when the cookie information cannot be parsed. */ - public SessionCookie(Cookie cookie) throws Exception { - parseCookie(cookie); + public SessionCookie(Cookie cookie) { + this(parseCookie(cookie)); } /** @@ -78,23 +44,9 @@ public SessionCookie(Cookie cookie) throws Exception { * @param sessionId The session ID. */ public SessionCookie(String sessionId) { - String now = String.valueOf(System.currentTimeMillis()); - String[] cookieRawValues = new String[] { sessionId, now, now }; - String formattedCookie = SessionCookie.formatCookie(cookieRawValues); - - Cookie cookie = new Cookie(COOKIE_NAME, formattedCookie); - - try { - parseCookie(cookie); - } catch (Exception e) { - // This exception is not expected in any case. - } + this.sessionId = sessionId; } - // endregion Ctor - - // region Public - /** * Gets the session id. * @return The session id. @@ -103,99 +55,18 @@ public String getSessionId() { return sessionId; } - /** - * Gets the session acquisition date. - * @return The session acquisition date. - */ - public Date getSessionAcquisitionDate() { - return acquisitionDate; - } - - /** - * Gets the session renewal date. - * @return The session renewal date. - */ - public Date getSessionRenewalDate() { - return renewalDate; - } - - /** - * Determines if the session has expired. - * @param sessionTimeoutInMinutes The session timeout in minutes. - * @return True if the session has expired, false otherwise. - */ - public boolean isSessionExpired(int sessionTimeoutInMinutes) { - Date expirationDate = DateTimeUtils.addToDate( - this.getSessionRenewalDate(), - Calendar.MINUTE, - sessionTimeoutInMinutes); - Date now = new Date(); - - return now.after(expirationDate); - } - - /** - * Returns a value indicating whether the session cookie is up-to-date. - * Session cookie is considered up-to-date when the last renewal time is less than - * {@literal UPDATE_TIMEOUT_IN_MINUTES} minutes. - * @return True if the session cookie up-to-date. - */ - public boolean isSessionCookieUpToDate() { - Date expectedRenewalTime = DateTimeUtils.addToDate( - this.getSessionRenewalDate(), - Calendar.MINUTE, - UPDATE_TIMEOUT_IN_MINUTES); - Date now = new Date(); - - return now.before(expectedRenewalTime); - } - - // endregion Public - - // region Private - /** * Parses the given cookie. * @param cookie The cookie contains the session information. - * @throws Exception Thrown when the cookie information cannot be parsed. + * @return sessionId */ - private void parseCookie(Cookie cookie) throws Exception { - String[] split = cookie.getValue().split(RAW_COOKIE_SPLIT_DELIMITER); - - if (split.length < CookieFields.values().length) { - - // TODO: dedicated exception - String errorMessage = String.format("Session cookie is not in the correct format: %s", cookie.getValue()); - - throw new Exception(errorMessage); - } - - try { - sessionId = split[CookieFields.SESSION_ID.getValue()]; - acquisitionDate = parseDateWithBackwardCompatibility(split[CookieFields.SESSION_ACQUISITION_DATE.getValue()]); - renewalDate = parseDateWithBackwardCompatibility(split[CookieFields.SESSION_LAST_UPDATE_DATE.getValue()]); - } catch (Exception e) { - String errorMessage = String.format("Failed to parse session cookie with exception: %s", e.toString()); - - // TODO: dedicated exception - throw new Exception(errorMessage); + private static String parseCookie(Cookie cookie) { + String value = cookie.getValue(); + int idx = value.indexOf(RAW_COOKIE_DELIMITER); + if (idx >= 0) { + return value.substring(0, idx); + } else { + return value; } } - - /** - * JavaScript SDK was changed to store dates as long, rather than a readable date string. - * For backward compatibility, we first try to parse with the new format (time represented by long) and then backward - * compatibility for time represented by a string. - * @param dateStr The date to parse. - * @return The parsed date. - */ - private Date parseDateWithBackwardCompatibility(String dateStr) throws ParseException { - try { - return new Date(Long.parseLong(dateStr)); - } catch (NumberFormatException e) { - return DateTimeUtils.parseRoundTripDateString(dateStr); - } - } - - // endregion Private -} +} \ No newline at end of file diff --git a/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookieTests.java b/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookieTests.java index efa2b907e33..a70d98bf52e 100644 --- a/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookieTests.java +++ b/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/SessionCookieTests.java @@ -21,17 +21,12 @@ package com.microsoft.applicationinsights.web.internal.cookies; -import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.http.Cookie; import org.junit.Assert; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import com.microsoft.applicationinsights.internal.util.LocalStringsUtils; -import com.microsoft.applicationinsights.internal.util.DateTimeUtils; -import com.microsoft.applicationinsights.web.utils.HttpHelper; import com.microsoft.applicationinsights.extensibility.context.SessionContext; import com.microsoft.applicationinsights.web.internal.RequestTelemetryContext; @@ -47,8 +42,6 @@ public class SessionCookieTests { private static Cookie defaultCookie; private static String sessionId; - private static Date sessionAcquisitionTime; - private static Date sessionRenewalTime; private static SessionContext sessionContext; private static RequestTelemetryContext requestTelemetryContextMock; @@ -57,13 +50,9 @@ public class SessionCookieTests { @BeforeClass public static void initialize() throws Exception { sessionId = LocalStringsUtils.generateRandomId(true); - sessionAcquisitionTime = new Date(); - sessionRenewalTime = new Date(sessionAcquisitionTime.getTime() + 1000); - + String formattedCookie = SessionCookie.formatCookie(new String[] { - sessionId, - String.valueOf(sessionAcquisitionTime.getTime()), - String.valueOf(sessionRenewalTime.getTime()) + sessionId }); defaultCookie = new Cookie(SessionCookie.COOKIE_NAME, formattedCookie); @@ -77,107 +66,32 @@ public static void initialize() throws Exception { } // region Tests - @Rule - public ExpectedException thrown= ExpectedException.none(); - + @Test public void testCookieParsedSuccessfully() throws Exception { SessionCookie sessionCookie = new SessionCookie(defaultCookie); Assert.assertEquals("Wrong session ID", sessionId, sessionCookie.getSessionId()); - Assert.assertEquals("Wrong session acquisition time", sessionAcquisitionTime, sessionCookie.getSessionAcquisitionDate()); - Assert.assertEquals("Wrong session renewal time", sessionRenewalTime, sessionCookie.getSessionRenewalDate()); - } - - @Test - public void testCookieExpiration() throws Exception { - String expiredFormattedCookie = HttpHelper.getFormattedSessionCookie(true); - Cookie expiredCookie = new Cookie(SessionCookie.COOKIE_NAME, expiredFormattedCookie); - SessionCookie sessionCookie = new SessionCookie(expiredCookie); - - Assert.assertTrue("Expired session expected.", sessionCookie.isSessionExpired(SessionCookie.SESSION_DEFAULT_EXPIRATION_TIMEOUT_IN_MINUTES)); - } - - @Test - public void testCorruptedSessionAcquisitionTimeValueThrowsExceptionOnCookieParsing() throws Exception { - thrown.expect(Exception.class); - - String formattedCookie = SessionCookie.formatCookie(new String[] { - sessionId, - "corruptedAcquisitionTime", - String.valueOf(sessionRenewalTime.getTime()) - }); - - createSessionCookie(formattedCookie); - } - - @Test - public void testCorruptedSessionRenewalTimeValueThrowsExceptionOnCookieParsing() throws Exception { - thrown.expect(Exception.class); - - String formattedCookie = SessionCookie.formatCookie(new String[] { - sessionId, - "corruptedAcquisitionTime", - String.valueOf(sessionRenewalTime.getTime()) - }); - - createSessionCookie(formattedCookie); } @Test - public void testUnexpectedCookieValuesCountThrowsException() throws Exception { - thrown.expect(Exception.class); - + public void testSingleCookieValue() { String formattedCookie = SessionCookie.formatCookie(new String[]{ - "singleValueCookie" + sessionId }); - createSessionCookie(formattedCookie); - } - - @Test - public void testSessionHttpCookiePathSetForAllPages() { - Cookie cookie = HttpCookieFactory.generateSessionHttpCookie(requestTelemetryContextMock, sessionContext, 10); - - Assert.assertEquals("Path should catch all urls", HttpCookieFactory.COOKIE_PATH_ALL_URL, cookie.getPath()); - } - - @Test - public void testSessionHttpCookieSetMaxAge() { - final int sessionTimeoutInMinutes = 10; - - Cookie cookie = HttpCookieFactory.generateSessionHttpCookie(requestTelemetryContextMock, sessionContext, sessionTimeoutInMinutes); - - Assert.assertEquals(sessionTimeoutInMinutes * 60, cookie.getMaxAge()); - } - - @Test - public void testCookieParsedSuccessfullyWithBackwardCompatibilityForTimeFormats() throws Exception { - String expectedAcquisitionTime = DateTimeUtils.formatAsRoundTripDate(sessionAcquisitionTime); - String expectedRenewalTime = DateTimeUtils.formatAsRoundTripDate(sessionRenewalTime); - - String formattedCookie = SessionCookie.formatCookie(new String[] { - sessionId, - expectedAcquisitionTime, - expectedRenewalTime - }); - - Cookie cookie = new Cookie(SessionCookie.COOKIE_NAME, formattedCookie); - - SessionCookie sessionCookie = new SessionCookie(cookie); - + SessionCookie sessionCookie = createSessionCookie(formattedCookie); + Assert.assertEquals("Wrong session ID", sessionId, sessionCookie.getSessionId()); - Assert.assertEquals("Wrong session acquisition time", expectedAcquisitionTime, DateTimeUtils.formatAsRoundTripDate(sessionCookie.getSessionAcquisitionDate())); - Assert.assertEquals("Wrong session renewal time", expectedRenewalTime, DateTimeUtils.formatAsRoundTripDate(sessionCookie.getSessionRenewalDate())); } // endregion Tests // region Private - private void createSessionCookie(String cookieValue) throws Exception { - Cookie corruptedCookie = new Cookie(SessionCookie.COOKIE_NAME, cookieValue); - new SessionCookie(corruptedCookie); + private SessionCookie createSessionCookie(String cookieValue) { + Cookie cookie = new Cookie(SessionCookie.COOKIE_NAME, cookieValue); + return new SessionCookie(cookie); } // endregion Private diff --git a/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/UserCookieTests.java b/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/UserCookieTests.java index aeef732b4f4..e21ef411b8b 100644 --- a/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/UserCookieTests.java +++ b/web/src/test/java/com/microsoft/applicationinsights/web/internal/cookies/UserCookieTests.java @@ -102,20 +102,6 @@ public void testUnexpectedCookieValuesCountThrowsException() throws Exception { createUserCookie(formattedCookie); } - @Test - public void testUserHttpCookiePathSetForAllPages() { - Cookie cookie = HttpCookieFactory.generateUserHttpCookie(requestTelemetryContextMock); - - Assert.assertEquals("Path should catch all urls", HttpCookieFactory.COOKIE_PATH_ALL_URL, cookie.getPath()); - } - - @Test - public void testUserHttpCookieSetMaxAge() { - Cookie cookie = HttpCookieFactory.generateUserHttpCookie(requestTelemetryContextMock); - - Assert.assertEquals("Cookie age should be set to max value.", Integer.MAX_VALUE, cookie.getMaxAge()); - } - // endregion Tests // region Private