Java tutorial
///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.business.teamcal.servlet; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.lang.StringUtils; import org.apache.log4j.MDC; import org.joda.time.DateTime; import org.projectforge.ProjectForgeApp; import org.projectforge.business.multitenancy.TenantRegistry; import org.projectforge.business.multitenancy.TenantRegistryMap; import org.projectforge.business.teamcal.TeamCalConfig; import org.projectforge.business.teamcal.common.CalendarHelper; import org.projectforge.business.teamcal.model.CalendarFeedConst; import org.projectforge.business.teamcal.service.TeamCalCalendarFeedHook; import org.projectforge.business.teamcal.service.TeamCalService; import org.projectforge.business.timesheet.TimesheetDO; import org.projectforge.business.timesheet.TimesheetDao; import org.projectforge.business.timesheet.TimesheetFilter; import org.projectforge.business.user.ProjectForgeGroup; import org.projectforge.business.user.UserGroupCache; import org.projectforge.business.user.service.UserService; import org.projectforge.common.StringHelper; import org.projectforge.framework.access.AccessChecker; import org.projectforge.framework.access.AccessException; import org.projectforge.framework.calendar.ICal4JUtils; import org.projectforge.framework.persistence.user.api.ThreadLocalUserContext; import org.projectforge.framework.persistence.user.entities.PFUserDO; import org.projectforge.framework.time.DayHolder; import org.projectforge.framework.utils.NumberHelper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import net.fortuna.ical4j.data.CalendarOutputter; import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.model.TimeZone; import net.fortuna.ical4j.model.ValidationException; import net.fortuna.ical4j.model.component.VEvent; import net.fortuna.ical4j.model.property.CalScale; import net.fortuna.ical4j.model.property.Description; import net.fortuna.ical4j.model.property.Location; import net.fortuna.ical4j.model.property.ProdId; import net.fortuna.ical4j.model.property.Version; /** * Feed Servlet, which generates a 'text/calendar' output of the last four mounts. Currently relevant informations are * date, start- and stop time and last but not least the location of an event. * * @author Kai Reinhard (k.reinhard@micromata.de) */ @WebServlet("/export/ProjectForge.ics") public class CalendarAboServlet extends HttpServlet { private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(CalendarAboServlet.class); private static final long serialVersionUID = 1480433876190009435L; /** * setup event is needed for empty calendars */ public static final String SETUP_EVENT = "SETUP EVENT"; @Autowired private TimesheetDao timesheetDao; @Autowired private AccessChecker accessChecker; private WebApplicationContext springContext; @Autowired private UserService userService; @Autowired private TeamCalService teamCalService; @Autowired private TeamCalCalendarFeedHook teamCalCalendarFeedHook; @Override public void init(final ServletConfig config) throws ServletException { super.init(config); springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext()); final AutowireCapableBeanFactory beanFactory = springContext.getAutowireCapableBeanFactory(); beanFactory.autowireBean(this); } @Override protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { if (ProjectForgeApp.getInstance().isUpAndRunning() == false) { log.error( "System isn't up and running, CalendarFeed call denied. The system is may-be in start-up phase or in maintenance mode."); resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); return; } PFUserDO user = null; String logMessage = null; try { MDC.put("ip", (Object) req.getRemoteAddr()); MDC.put("session", (Object) req.getSession().getId()); if (StringUtils.isBlank(req.getParameter("user")) || StringUtils.isBlank(req.getParameter("q"))) { resp.sendError(HttpStatus.SC_BAD_REQUEST); log.error("Bad request, parameters user and q not given. Query string is: " + req.getQueryString()); return; } final String encryptedParams = req.getParameter("q"); final Integer userId = NumberHelper.parseInteger(req.getParameter("user")); if (userId == null) { log.error("Bad request, parameter user is not an integer: " + req.getQueryString()); return; } user = TenantRegistryMap.getInstance().getTenantRegistry().getUserGroupCache().getUser(userId); if (user == null) { log.error("Bad request, user not found: " + req.getQueryString()); return; } ThreadLocalUserContext.setUser(getUserGroupCache(), user); MDC.put("user", (Object) user.getUsername()); final String decryptedParams = userService.decrypt(userId, encryptedParams); if (decryptedParams == null) { log.error( "Bad request, can't decrypt parameter q (may-be the user's authentication token was changed): " + req.getQueryString()); return; } final Map<String, String> params = StringHelper.getKeyValues(decryptedParams, "&"); final Calendar calendar = createCal(params, userId, params.get("token"), params.get(CalendarFeedConst.PARAM_NAME_TIMESHEET_USER)); final StringBuffer buf = new StringBuffer(); boolean first = true; for (final Map.Entry<String, String> entry : params.entrySet()) { if ("token".equals(entry.getKey()) == true) { continue; } first = StringHelper.append(buf, first, entry.getKey(), ", "); buf.append("=").append(entry.getValue()); } logMessage = buf.toString(); log.info("Getting calendar entries for: " + logMessage); if (calendar == null) { resp.sendError(HttpStatus.SC_BAD_REQUEST); log.error("Bad request, can't find calendar."); return; } resp.setContentType("text/calendar"); final CalendarOutputter output = new CalendarOutputter(false); try { output.output(calendar, resp.getOutputStream()); } catch (final ValidationException ex) { ex.printStackTrace(); } } finally { log.info("Finished request: " + logMessage); ThreadLocalUserContext.setUser(getUserGroupCache(), null); MDC.remove("ip"); MDC.remove("session"); if (user != null) { MDC.remove("user"); } } } /** * creates a calendar for the user, identified by his name and authentication key. * * @param params * @param userName * @param userKey * @return a calendar, null if authentication fails */ private Calendar createCal(final Map<String, String> params, final Integer userId, final String authKey, final String timesheetUserParam) { final PFUserDO loggedInUser = userService.getUserByAuthenticationToken(userId, authKey); if (loggedInUser == null) { return null; } PFUserDO timesheetUser = null; if (StringUtils.isNotBlank(timesheetUserParam) == true) { final Integer timesheetUserId = NumberHelper.parseInteger(timesheetUserParam); if (timesheetUserId != null) { if (timesheetUserId.equals(loggedInUser.getId()) == false) { log.error("Not yet allowed: all users are only allowed to download their own time-sheets."); return null; } timesheetUser = TenantRegistryMap.getInstance().getTenantRegistry().getUserGroupCache() .getUser(timesheetUserId); if (timesheetUser == null) { log.error("Time-sheet user with id '" + timesheetUserParam + "' not found."); return null; } } } // creating a new calendar final Calendar calendar = new Calendar(); final Locale locale = ThreadLocalUserContext.getLocale(); calendar.getProperties().add(new ProdId( "-//" + loggedInUser.getDisplayUsername() + "//ProjectForge//" + locale.toString().toUpperCase())); calendar.getProperties().add(Version.VERSION_2_0); calendar.getProperties().add(CalScale.GREGORIAN); // setup event is needed for empty calendars calendar.getComponents().add(new VEvent(new net.fortuna.ical4j.model.Date(0), SETUP_EVENT)); // adding events for (final VEvent event : getEvents(params, timesheetUser)) { calendar.getComponents().add(event); } return calendar; } /** * builds the list of events * * @return */ private List<VEvent> getEvents(final Map<String, String> params, PFUserDO timesheetUser) { final PFUserDO loggedInUser = ThreadLocalUserContext.getUser(); if (loggedInUser == null) { throw new AccessException("No logged-in-user found!"); } final List<VEvent> events = new ArrayList<VEvent>(); final TimeZone timezone = ICal4JUtils.getUserTimeZone(); final java.util.Calendar cal = java.util.Calendar.getInstance(ThreadLocalUserContext.getTimeZone()); boolean eventsExist = false; final List<VEvent> list = teamCalCalendarFeedHook.getEvents(params, timezone); if (list != null && list.size() > 0) { events.addAll(list); eventsExist = true; } if (timesheetUser != null) { if (loggedInUser.getId().equals(timesheetUser.getId()) == false && isOtherUsersAllowed() == false) { // Only project managers, controllers and administrative staff is allowed to subscribe time-sheets of other users. log.warn("User tried to get time-sheets of other user: " + timesheetUser); timesheetUser = loggedInUser; } // initializes timesheet filter final TimesheetFilter filter = new TimesheetFilter(); filter.setUserId(timesheetUser.getId()); filter.setDeleted(false); filter.setStopTime(cal.getTime()); // calculates the offset of the calendar final int offset = cal.get(java.util.Calendar.MONTH) - CalendarFeedConst.PERIOD_IN_MONTHS; if (offset < 0) { setCalDate(cal, cal.get(java.util.Calendar.YEAR) - 1, 12 + offset); } else { setCalDate(cal, cal.get(java.util.Calendar.YEAR), offset); } filter.setStartTime(cal.getTime()); final List<TimesheetDO> timesheetList = timesheetDao.getList(filter); // iterate over all timesheets and adds each event to the calendar for (final TimesheetDO timesheet : timesheetList) { final String uid = TeamCalConfig.get().createTimesheetUid(timesheet.getId()); String summary; if (eventsExist == true) { summary = CalendarHelper.getTitle(timesheet) + " (ts)"; } else { summary = CalendarHelper.getTitle(timesheet); } final VEvent vEvent = ICal4JUtils.createVEvent(timesheet.getStartTime(), timesheet.getStopTime(), uid, summary); if (StringUtils.isNotBlank(timesheet.getDescription()) == true) { vEvent.getProperties().add(new Description(timesheet.getDescription())); } if (StringUtils.isNotBlank(timesheet.getLocation()) == true) { vEvent.getProperties().add(new Location(timesheet.getLocation())); } events.add(vEvent); } } final String holidays = params.get(CalendarFeedConst.PARAM_NAME_HOLIDAYS); if ("true".equals(holidays) == true) { DateTime holidaysFrom = new DateTime(ThreadLocalUserContext.getDateTimeZone()); holidaysFrom = holidaysFrom.dayOfYear().withMinimumValue().millisOfDay().withMinimumValue() .minusYears(2); final DateTime holidayTo = holidaysFrom.plusYears(6); events.addAll(teamCalService.getConfiguredHolidaysAsVEvent(holidaysFrom, holidayTo)); } final String weeksOfYear = params.get(CalendarFeedConst.PARAM_NAME_WEEK_OF_YEARS); if ("true".equals(weeksOfYear) == true) { final DayHolder from = new DayHolder(); from.setBeginOfYear().add(java.util.Calendar.YEAR, -2).setBeginOfWeek(); final DayHolder to = new DayHolder(from); to.add(java.util.Calendar.YEAR, 6); final DayHolder current = new DayHolder(from); int paranoiaCounter = 0; do { final VEvent vEvent = ICal4JUtils.createVEvent(current.getDate(), current.getDate(), "pf-weekOfYear" + current.getYear() + "-" + paranoiaCounter, ThreadLocalUserContext.getLocalizedString("calendar.weekOfYearShortLabel") + " " + current.getWeekOfYear(), true); events.add(vEvent); current.add(java.util.Calendar.WEEK_OF_YEAR, 1); if (++paranoiaCounter > 500) { log.warn( "Dear developer, please have a look here, paranoiaCounter exceeded! Aborting calculation of weeks of year."); } } while (current.before(to) == true); } // Integer hrPlanningUserId = NumberHelper.parseInteger(params.get(PARAM_NAME_HR_PLANNING)); // if (hrPlanningUserId != null) { // if (loggedInUser.getId().equals(hrPlanningUserId) == false && isOtherUsersAllowed() == false) { // // Only project managers, controllers and administrative staff is allowed to subscribe time-sheets of other users. // log.warn("User tried to get time-sheets of other user: " + timesheetUser); // hrPlanningUserId = loggedInUser.getId(); // } // final HRPlanningDao hrPlanningDao = Registry.instance().getDao(HRPlanningDao.class); // final HRPlanningEventsProvider hrPlanningEventsProvider = new HRPlanningEventsProvider(new CalendarFilter().setShowPlanning(true) // .setTimesheetUserId(hrPlanningUserId), hrPlanningDao); // DateTime planningFrom = new DateTime(ThreadLocalUserContext.getDateTimeZone()); // planningFrom = planningFrom.dayOfYear().withMinimumValue().millisOfDay().withMinimumValue().minusYears(1); // final DateTime planningTo = planningFrom.plusYears(4); // for (final Event event : hrPlanningEventsProvider.getEvents(planningFrom, planningTo)) { // final Date fromDate = event.getStart().toDate(); // final Date toDate = event.getEnd() != null ? event.getEnd().toDate() : fromDate; // final VEvent vEvent = ICal4JUtils.createVEvent(fromDate, toDate, "pf-hr-planning" + event.getId(), event.getTitle(), true); // events.add(vEvent); // } // } return events; } /** * sets the calendar to a special date. Used to calculate the year offset of an negative time period. When the time * period is set to 4 month and the current month is at the begin of a year, the year-number must be decremented by * one * * @param cal * @param year * @param mounth */ private void setCalDate(final java.util.Calendar cal, final int year, final int mounth) { cal.clear(); cal.set(java.util.Calendar.YEAR, year); cal.set(java.util.Calendar.MONTH, mounth); } private boolean isOtherUsersAllowed() { return accessChecker.isLoggedInUserMemberOfGroup(ProjectForgeGroup.FINANCE_GROUP, ProjectForgeGroup.CONTROLLING_GROUP, ProjectForgeGroup.PROJECT_MANAGER); } private TenantRegistry getTenantRegistry() { return TenantRegistryMap.getInstance().getTenantRegistry(); } private UserGroupCache getUserGroupCache() { return getTenantRegistry().getUserGroupCache(); } }