// This happens very early - check it. static { try { log = LogFactory.getLog(Servlet.class); } catch (Exception ex) { System.err.println("Exception creating the logger"); System.err.println("Commons logging jar files in WEB-INF/lib/?"); System.err.println(ex.getMessage()); // ex.printStackTrace(System.err) ; } }
/** @author Javier Paniza */ public class DescriptionsListTag extends TagSupport { private static Log log = LogFactory.getLog(DescriptionsListTag.class); private String reference; public int doStartTag() throws JspException { try { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); ModuleContext context = (ModuleContext) request.getSession().getAttribute("context"); String viewObject = request.getParameter("viewObject"); viewObject = (viewObject == null || viewObject.equals("")) ? "xava_view" : viewObject; View view = (View) context.get(request, viewObject); MetaReference metaReference = view.getMetaReference(reference).cloneMetaReference(); metaReference.setName(reference); String prefix = request.getParameter("propertyPrefix"); prefix = prefix == null ? "" : prefix; String application = request.getParameter("application"); String module = request.getParameter("module"); String referenceKey = Ids.decorate(application, module, prefix + reference); request.setAttribute(referenceKey, metaReference); String editorURL = "reference.jsp?referenceKey=" + referenceKey + "&onlyEditor=true&frame=false&composite=false&descriptionsList=true"; String editorPrefix = Module.isPortlet() ? "/WEB-INF/jsp/xava/" : "/xava/"; try { pageContext.include(editorPrefix + editorURL); } catch (ServletException ex) { Throwable cause = ex.getRootCause() == null ? ex : ex.getRootCause(); log.error(cause.getMessage(), cause); pageContext.include(editorPrefix + "editors/notAvailableEditor.jsp"); } catch (Exception ex) { log.error(ex.getMessage(), ex); pageContext.include(editorPrefix + "editors/notAvailableEditor.jsp"); } } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new JspException(XavaResources.getString("descriptionsList_tag_error", reference)); } return SKIP_BODY; } public String getReference() { return reference; } public void setReference(String property) { this.reference = property; } }
/** * To generate automatically reports from list mode. * * <p>Uses JasperReports. * * @author Javier Paniza */ public class GenerateReportServlet extends HttpServlet { private static Log log = LogFactory.getLog(GenerateReportServlet.class); public static class TableModelDecorator implements TableModel { private TableModel original; private List metaProperties; private boolean withValidValues = false; private Locale locale; private boolean labelAsHeader = false; private HttpServletRequest request; private boolean format = false; // format or no the values. If format = true, all values to the report are String private Integer columnCountLimit; public TableModelDecorator( HttpServletRequest request, TableModel original, List metaProperties, Locale locale, boolean labelAsHeader, boolean format, Integer columnCountLimit) throws Exception { this.request = request; this.original = original; this.metaProperties = metaProperties; this.locale = locale; this.withValidValues = calculateWithValidValues(); this.labelAsHeader = labelAsHeader; this.format = format; this.columnCountLimit = columnCountLimit; } private boolean calculateWithValidValues() { Iterator it = metaProperties.iterator(); while (it.hasNext()) { MetaProperty m = (MetaProperty) it.next(); if (m.hasValidValues()) return true; } return false; } private MetaProperty getMetaProperty(int i) { return (MetaProperty) metaProperties.get(i); } public int getRowCount() { return original.getRowCount(); } public int getColumnCount() { return columnCountLimit == null ? original.getColumnCount() : columnCountLimit; } public String getColumnName(int c) { return labelAsHeader ? getMetaProperty(c).getLabel(locale) : Strings.change(getMetaProperty(c).getQualifiedName(), ".", "_"); } public Class getColumnClass(int c) { return original.getColumnClass(c); } public boolean isCellEditable(int row, int column) { return original.isCellEditable(row, column); } public Object getValueAt(int row, int column) { if (isFormat()) return getValueWithWebEditorsFormat(row, column); else return getValueWithoutWebEditorsFormat(row, column); } private Object getValueWithoutWebEditorsFormat(int row, int column) { Object r = original.getValueAt(row, column); if (r instanceof Boolean) { if (((Boolean) r).booleanValue()) return XavaResources.getString(locale, "yes"); return XavaResources.getString(locale, "no"); } if (withValidValues) { MetaProperty p = getMetaProperty(column); if (p.hasValidValues()) { return p.getValidValueLabel(locale, original.getValueAt(row, column)); } } if (r instanceof java.util.Date) { MetaProperty p = getMetaProperty(column); // In order to use the type declared by the developer // and not the one returned by JDBC or the JPA engine if (java.sql.Time.class.isAssignableFrom(p.getType())) { return DateFormat.getTimeInstance(DateFormat.SHORT, locale).format(r); } if (java.sql.Timestamp.class.isAssignableFrom(p.getType())) { DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); return dateFormat.format(r); } return DateFormat.getDateInstance(DateFormat.SHORT, locale).format(r); } if (r instanceof BigDecimal) { return formatBigDecimal(r, locale); } return r; } private Object getValueWithWebEditorsFormat(int row, int column) { Object r = original.getValueAt(row, column); MetaProperty metaProperty = getMetaProperty(column); String result = WebEditors.format(this.request, metaProperty, r, null, "", true); if (isHtml(result)) { // this avoids that the report shows html content result = WebEditors.format(this.request, metaProperty, r, null, "", false); } return result; } public void setValueAt(Object value, int row, int column) { original.setValueAt(value, row, column); } public void addTableModelListener(TableModelListener l) { original.addTableModelListener(l); } public void removeTableModelListener(TableModelListener l) { original.removeTableModelListener(l); } private boolean isHtml(String value) { return value.matches("<.*>"); } public boolean isFormat() { return format; } public void setFormat(boolean format) { this.format = format; } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Locales.setCurrent(request); if (Users.getCurrent() == null) { // for a bug in websphere portal 5.1 with Domino LDAP Users.setCurrent((String) request.getSession().getAttribute("xava.user")); } request.getParameter("application"); // for a bug in websphere 5.1 request.getParameter("module"); // for a bug in websphere 5.1 Tab tab = (Tab) request.getSession().getAttribute("xava_reportTab"); int[] selectedRowsNumber = (int[]) request.getSession().getAttribute("xava_selectedRowsReportTab"); Map[] selectedKeys = (Map[]) request.getSession().getAttribute("xava_selectedKeysReportTab"); int[] selectedRows = getSelectedRows(selectedRowsNumber, selectedKeys, tab); request.getSession().removeAttribute("xava_selectedRowsReportTab"); Integer columnCountLimit = (Integer) request.getSession().getAttribute("xava_columnCountLimitReportTab"); request.getSession().removeAttribute("xava_columnCountLimitReportTab"); setDefaultSchema(request); String user = (String) request.getSession().getAttribute("xava_user"); request.getSession().removeAttribute("xava_user"); Users.setCurrent(user); String uri = request.getRequestURI(); if (uri.endsWith(".pdf")) { InputStream is; JRDataSource ds; Map parameters = new HashMap(); synchronized (tab) { tab.setRequest(request); parameters.put("Title", tab.getTitle()); parameters.put("Organization", getOrganization()); parameters.put("Date", getCurrentDate()); for (String totalProperty : tab.getTotalPropertiesNames()) { parameters.put(totalProperty + "__TOTAL__", getTotal(request, tab, totalProperty)); } TableModel tableModel = getTableModel(request, tab, selectedRows, false, true, null); tableModel.getValueAt(0, 0); if (tableModel.getRowCount() == 0) { generateNoRowsPage(response); return; } is = getReport(request, response, tab, tableModel, columnCountLimit); ds = new JRTableModelDataSource(tableModel); } JasperPrint jprint = JasperFillManager.fillReport(is, parameters, ds); response.setContentType("application/pdf"); response.setHeader( "Content-Disposition", "inline; filename=\"" + getFileName(tab) + ".pdf\""); JasperExportManager.exportReportToPdfStream(jprint, response.getOutputStream()); } else if (uri.endsWith(".csv")) { String csvEncoding = XavaPreferences.getInstance().getCSVEncoding(); if (!Is.emptyString(csvEncoding)) { response.setCharacterEncoding(csvEncoding); } response.setContentType("text/x-csv"); response.setHeader( "Content-Disposition", "inline; filename=\"" + getFileName(tab) + ".csv\""); synchronized (tab) { tab.setRequest(request); response .getWriter() .print( TableModels.toCSV( getTableModel(request, tab, selectedRows, true, false, columnCountLimit))); } } else { throw new ServletException( XavaResources.getString("report_type_not_supported", "", ".pdf .csv")); } } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new ServletException(XavaResources.getString("report_error")); } finally { request.getSession().removeAttribute("xava_reportTab"); } } private void generateNoRowsPage(HttpServletResponse response) throws Exception { response.setContentType("text/html"); response.getWriter().println("<html><head><title>"); response.getWriter().println(XavaResources.getString("no_rows_report_message_title")); response .getWriter() .println( "</title></head><body style='font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;'>"); response.getWriter().println("<h1 style='font-size:22px;'>"); response.getWriter().println(XavaResources.getString("no_rows_report_message_title")); response.getWriter().println("</h1>"); response.getWriter().println("<p style='font-size:16px;'>"); response.getWriter().println(XavaResources.getString("no_rows_report_message_detail")); response.getWriter().println("</p></body></html>"); } private String getCurrentDate() { return java.text.DateFormat.getDateInstance(DateFormat.MEDIUM, Locales.getCurrent()) .format(new java.util.Date()); } private String getFileName(Tab tab) { String now = new SimpleDateFormat("yyyyMMdd_HHmm").format(new Date()); return tab.getTitle() + " " + now; } private Object getTotal(HttpServletRequest request, Tab tab, String totalProperty) { Object total = tab.getTotal(totalProperty); return WebEditors.format( request, tab.getMetaProperty(totalProperty), total, new Messages(), null, true); } private void setDefaultSchema(HttpServletRequest request) { String hibernateDefaultSchemaTab = (String) request.getSession().getAttribute("xava_hibernateDefaultSchemaTab"); if (hibernateDefaultSchemaTab != null) { request.getSession().removeAttribute("xava_hibernateDefaultSchemaTab"); XHibernate.setDefaultSchema(hibernateDefaultSchemaTab); } String jpaDefaultSchemaTab = (String) request.getSession().getAttribute("xava_jpaDefaultSchemaTab"); if (jpaDefaultSchemaTab != null) { request.getSession().removeAttribute("xava_jpaDefaultSchemaTab"); XPersistence.setDefaultSchema(jpaDefaultSchemaTab); } } protected String getOrganization() throws MissingResourceException, XavaException { return ReportParametersProviderFactory.getInstance().getOrganization(); } private InputStream getReport( HttpServletRequest request, HttpServletResponse response, Tab tab, TableModel tableModel, Integer columnCountLimit) throws ServletException, IOException { StringBuffer suri = new StringBuffer(); suri.append("/xava/jasperReport"); suri.append("?language="); suri.append(Locales.getCurrent().getLanguage()); suri.append("&widths="); suri.append(Arrays.toString(getWidths(tableModel))); if (columnCountLimit != null) { suri.append("&columnCountLimit="); suri.append(columnCountLimit); } response.setCharacterEncoding(XSystem.getEncoding()); return Servlets.getURIAsStream(request, response, suri.toString()); } private int[] getWidths(TableModel tableModel) { int[] widths = new int[tableModel.getColumnCount()]; for (int r = 0; r < Math.min(tableModel.getRowCount(), 500); r++) { // 500 is not for performance, but for using only a sample of data with huge table for (int c = 0; c < tableModel.getColumnCount(); c++) { Object o = tableModel.getValueAt(r, c); if (o instanceof String) { String s = ((String) o).trim(); if (s.length() > widths[c]) widths[c] = s.length(); } } } return widths; } private TableModel getTableModel( HttpServletRequest request, Tab tab, int[] selectedRows, boolean labelAsHeader, boolean format, Integer columnCountLimit) throws Exception { TableModel data = null; if (selectedRows != null && selectedRows.length > 0) { data = new SelectedRowsXTableModel(tab.getTableModel(), selectedRows); } else { data = tab.getAllDataTableModel(); } return new TableModelDecorator( request, data, tab.getMetaProperties(), Locales.getCurrent(), labelAsHeader, format, columnCountLimit); } private static Object formatBigDecimal(Object number, Locale locale) { NumberFormat nf = NumberFormat.getNumberInstance(locale); nf.setMinimumFractionDigits(2); return nf.format(number); } private int[] getSelectedRows(int[] selectedRowsNumber, Map[] selectedRowsKeys, Tab tab) { if (selectedRowsKeys == null || selectedRowsKeys.length == 0) return new int[0]; // selectedRowsNumber is the most performant so we use it when possible else if (selectedRowsNumber.length == selectedRowsKeys.length) return selectedRowsNumber; else { // find the rows from the selectedKeys // This has a poor performance, but it covers the case when the selected // rows are not loaded for the tab, something that can occurs if the user // select rows and afterwards reorder the list. try { int[] s = new int[selectedRowsKeys.length]; List selectedKeys = Arrays.asList(selectedRowsKeys); int end = tab.getTableModel().getTotalSize(); int x = 0; for (int i = 0; i < end; i++) { Map key = (Map) tab.getTableModel().getObjectAt(i); if (selectedKeys.contains(key)) { s[x] = i; x++; } } return s; } catch (Exception ex) { log.warn(XavaResources.getString("fails_selected"), ex); throw new XavaException("fails_selected"); } } } }
/** @author Johan Lindquist */ public class BaseController { private Log _log = LogFactory.getLog(BaseController.class); @Autowired protected JahSpotifyService _jahSpotifyService; @Value(value = "${jahspotify.web.controller.default-media-expires-duration}") private int _defaultMediaExpirationTime; protected void writeResponse( final HttpServletResponse httpServletResponse, final SimpleStatusResponse simpleStatusResponse) { Gson gson = new Gson(); try { httpServletResponse.setContentType("application/json; charset=utf-8"); _log.debug("Serializing: " + simpleStatusResponse); final PrintWriter writer = httpServletResponse.getWriter(); gson.toJson(simpleStatusResponse.getResponseStatus(), writer); writer.flush(); writer.close(); } catch (Exception e) { _log.error("Error while writing response: " + e.getMessage(), e); } } protected void writeMediaNotReadable(final HttpServletResponse httpServletResponse) { SimpleStatusResponse simpleStatusResponse = new SimpleStatusResponse(); simpleStatusResponse.setResponseStatus(ResponseStatus.RESOURCE_NOT_FOUND); writeResponse(httpServletResponse, simpleStatusResponse); } protected void writeResponseGeneric( final HttpServletResponse httpServletResponse, final Object object) { this.writeResponseGenericWithDate(httpServletResponse, null, object); } protected void writeResponseGenericWithDate( final HttpServletResponse httpServletResponse, final Date lastModified, final Object object) { writeResponseGenericWithDate( httpServletResponse, lastModified, _defaultMediaExpirationTime, object); } protected void writeResponseGenericWithDate( final HttpServletResponse httpServletResponse, final Date lastModified, final int expirationTime, final Object object) { Gson gson = new Gson(); try { httpServletResponse.setContentType("application/json; charset=utf-8"); if (lastModified != null) { httpServletResponse.addHeader("Expires", createDateHeader(expirationTime)); httpServletResponse.addHeader("Last-Modified", toHttpDate(lastModified)); } _log.debug("Serializing: " + object); final PrintWriter writer = httpServletResponse.getWriter(); gson.toJson(object, writer); writer.flush(); writer.close(); } catch (Exception e) { _log.error("Error while writing response: " + e.getMessage(), e); } } protected String createDateHeader(int expires) { final Calendar utc = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")); utc.add(Calendar.SECOND, expires); return toHttpDate(utc.getTime()); } protected String toHttpDate(Date date) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z"); return simpleDateFormat.format(date); } protected Link retrieveLink(final HttpServletRequest httpServletRequest) { String uri = httpServletRequest .getRequestURI() .substring(httpServletRequest.getRequestURI().lastIndexOf("/") + 1); _log.debug("Extracted URI: " + uri); return Link.create(uri); } protected <T> T readRequest(final HttpServletRequest httpServletRequest, final Class<T> classOfT) throws IOException { final BufferedReader br = new BufferedReader(httpServletRequest.getReader()); Gson gson = new Gson(); return gson.fromJson(br, classOfT); } public void writeErrorResponse(final HttpServletResponse httpServletResponse, final Exception e) { SimpleStatusResponse simpleStatusResponse = new SimpleStatusResponse(); simpleStatusResponse.setResponseStatus(ResponseStatus.INTERNAL_ERROR); writeResponse(httpServletResponse, simpleStatusResponse); } }
/** * Created with IntelliJ IDEA. User: ciobi Date: 2013-06-15 Time: 09:56 * * <p> */ public class ReaderHandler extends WebAppContext { public static final Log LOG = LogFactory.getLog(ReaderHandler.class); // ttt1 option that on http only redirects to https, for all paths public static final String ACTION_LOGIN = "******"; public static final String ACTION_SIGNUP = "signup"; public static final String ACTION_CHANGE_PASSWORD = "******"; public static final String ACTION_CHANGE_SETTINGS = "change_settings"; public static final String ACTION_ADD_FEED = "add_feed"; public static final String ACTION_REMOVE_FEED = "remove_feed"; public static final String ACTION_UPDATE_FEED_LIST = "update_feed_list"; // for ordering, //ttt2 public static final String PATH_LOGIN = "******" + ACTION_LOGIN; public static final String PATH_CHANGE_PASSWORD = "******" + ACTION_CHANGE_PASSWORD; public static final String PATH_CHANGE_SETTINGS = "/" + ACTION_CHANGE_SETTINGS; public static final String PATH_SIGNUP = "/" + ACTION_SIGNUP; public static final String PATH_ADD_FEED = "/" + ACTION_ADD_FEED; public static final String PATH_REMOVE_FEED = "/" + ACTION_REMOVE_FEED; public static final String PATH_UPDATE_FEED_LIST = "/" + ACTION_UPDATE_FEED_LIST; public static final String PATH_ERROR = "/error"; public static final String PATH_LOGOUT = "/logout"; public static final String PATH_SETTINGS = "/settings"; public static final String PATH_FEEDS = "/feeds"; public static final String PATH_FEED = "/feed"; public static final String PATH_ADMIN = "/admin"; public static final String PATH_FEED_ADMIN = "/feed_admin"; public static final String PATH_OPEN_ARTICLE = "/open_article/"; // !!! it's easier to end this one with a slash // params we use to send strings to the JSPs or to get user input in POST, via // request.getParameter(), or both public static final String PARAM_USER_ID = "userId"; public static final String PARAM_USER_NAME = "name"; public static final String PARAM_EMAIL = "email"; public static final String PARAM_CURRENT_PASSWORD = "******"; public static final String PARAM_PASSWORD = "******"; public static final String PARAM_PASSWORD_CONFIRM = "passwordConfirm"; public static final String PARAM_PATH = "path"; // public static final String PARAM_ERROR = "error"; public static final String PARAM_REMEMBER_ACCOUNT = "rememberAccount"; public static final String PARAM_NEW_FEED_URL = "feedUrl"; public static final String PARAM_FEED_ID = "feedId"; public static final String PARAM_ITEMS_PER_PAGE = "itemsPerPage"; public static final String PARAM_STYLE = "style"; public static final String PARAM_FEED_DATE_FORMAT = "feedDateFormat"; // variable names, used to give JSPs access to Java objects in the handler via // request.getAttribute(() public static final String VAR_USER = "******"; public static final String VAR_LOGIN_INFO = "loginInfo"; public static final String VAR_USER_DB = "userDb"; public static final String VAR_FEED_DB = "feedDb"; public static final String VAR_ARTICLE_DB = "articleDb"; public static final String VAR_READ_ARTICLES_COLL_DB = "readArticlesCollDb"; public static final String BROWSER_ID = "browserId"; public static final String SESSION_ID = "sessionId"; private LoginInfo.DB loginInfoDb; private User.DB userDb; private Feed.DB feedDb; private Article.DB articleDb; private ReadArticlesColl.DB readArticlesCollDb; private UserHelpers userHelpers; private boolean isInJar = Utils.isInJar(); private static class ReaderErrorHandler extends ErrorHandler { @Override // !!! note that this gets called for missing pages, but not if exceptions are thrown; // exceptions are handled separately public void handle( String target, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException { request.setHandled(true); httpServletResponse .getWriter() .println( String.format("<h1>Page doesn't exist: %s</h1>", request.getUri().getDecodedPath())); } } private static HashMap<String, String> PATH_MAPPING = new HashMap<>(); static { PATH_MAPPING.put("", "home_page"); PATH_MAPPING.put(PATH_LOGIN, "login"); PATH_MAPPING.put(PATH_LOGOUT, "login"); // !!! after logout we get redirected to /login PATH_MAPPING.put(PATH_SIGNUP, "signup"); PATH_MAPPING.put(PATH_ERROR, "error"); PATH_MAPPING.put(PATH_FEED_ADMIN, "feed_admin"); PATH_MAPPING.put(PATH_SETTINGS, "settings"); PATH_MAPPING.put(PATH_FEEDS, "feeds"); PATH_MAPPING.put(PATH_FEED + "/*", "feed"); PATH_MAPPING.put(PATH_ADMIN, "admin"); } public ReaderHandler(LowLevelDbAccess lowLevelDbAccess, String webDir) { loginInfoDb = new LoginInfo.DB(lowLevelDbAccess); userDb = new User.DB(lowLevelDbAccess); feedDb = new Feed.DB(lowLevelDbAccess); articleDb = new Article.DB(lowLevelDbAccess); readArticlesCollDb = new ReadArticlesColl.DB(lowLevelDbAccess); userHelpers = new UserHelpers(loginInfoDb, userDb); setContextPath("/"); File warPath = new File(webDir); setWar(warPath.getAbsolutePath()); if (isInJar) { for (Map.Entry<String, String> entry : PATH_MAPPING.entrySet()) { addPrebuiltJsp(entry.getKey(), "jsp." + entry.getValue().replaceAll("_", "_005f") + "_jsp"); } } else { for (Map.Entry<String, String> entry : PATH_MAPPING.entrySet()) { addServlet( new ServletHolder(new RedirectServlet("/" + entry.getValue() + ".jsp")), entry.getKey()); } } setErrorHandler(new ReaderErrorHandler()); } private void addPrebuiltJsp(String path, String className) { try { Class clazz = Class.forName( className); // ttt2 see if possible to not use this, preferably without doing // redirections like RedirectServlet Object obj = clazz.newInstance(); addServlet(new ServletHolder((Servlet) obj), path); LOG.info("Added prebuilt JSP: " + obj.toString()); } catch (Exception e) { LOG.fatal(String.format("Failed to load prebuilt JSP for %s and %s", path, className), e); } } @Override public void doHandle( String target, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { LOG.info("handling " + target); // !!! doHandle() is called twice for a request when using redirectiion, first time with // request.getPathInfo() // set to the URI and target set to the path, then with request.getPathInfo() set to null and // target set to the .jsp try { // request.setHandled(true); boolean secured; if (request.getScheme().equals("https")) { secured = true; } else if (request.getScheme().equals("http")) { secured = false; } else { httpServletResponse .getWriter() .println( String.format( "<h1>Unknown scheme %s at %s</h1>", request.getScheme(), request.getUri().getDecodedPath())); return; } if (request.getMethod().equals("GET")) { if (isInJar || target.endsWith(".jsp")) { // !!! when not in jar there's no need to do anything about params if it's not a .jsp, // as this will get called again for the corresponding .jsp if (prepareForJspGet(target, request, httpServletResponse, secured)) { return; } } if (target.startsWith(PATH_OPEN_ARTICLE)) { handleOpenArticle(request, httpServletResponse, target); return; } super.doHandle(target, request, httpServletRequest, httpServletResponse); LOG.info("handling of " + target + " went to super"); // httpServletResponse.setDateHeader("Date", System.currentTimeMillis()); //ttt2 review // these, probably not use // httpServletResponse.setDateHeader("Expires", System.currentTimeMillis() + 60000); return; } if (request.getMethod().equals("POST")) { if (request.getUri().getDecodedPath().equals(PATH_LOGIN)) { handleLoginPost(request, httpServletResponse, secured); } else if (request.getUri().getDecodedPath().equals(PATH_SIGNUP)) { handleSignupPost(request, httpServletResponse); } else if (request.getUri().getDecodedPath().equals(PATH_CHANGE_PASSWORD)) { handleChangePasswordPost(request, httpServletResponse); } else if (request.getUri().getDecodedPath().equals(PATH_UPDATE_FEED_LIST)) { handleUpdateFeedListPost(request, httpServletResponse); } else if (request.getUri().getDecodedPath().equals(PATH_ADD_FEED)) { handleAddFeedPost(request, httpServletResponse); } else if (request.getUri().getDecodedPath().equals(PATH_REMOVE_FEED)) { handleRemoveFeedPost(request, httpServletResponse); } else if (request.getUri().getDecodedPath().equals(PATH_CHANGE_SETTINGS)) { handleChangeSettingsPost(request, httpServletResponse); } } /*{ // for tests only; httpServletResponse.getWriter().println(String.format("<h1>Unable to process request %s</h1>", request.getUri().getDecodedPath())); request.setHandled(true); }*/ } catch (Exception e) { LOG.error("Error processing request", e); try { // redirectToError(e.toString(), request, httpServletResponse); //!!! redirectToError leads // to infinite loop, probably related to // the fact that we get 2 calls for a regular request when redirecting httpServletResponse .getWriter() .println( String.format( "<h1>Unable to process request %s</h1>", // ttt1 generate some HTML request.getUri().getDecodedPath())); request.setHandled(true); } catch (Exception e1) { LOG.error("Error redirecting", e1); } } } /** * Normally sets the path and a few attributes that the JSPs are likely to need. Also verifies the * login information. If necessary, just redirects to the login page. * * @param target * @param request * @param httpServletResponse * @param secured * @return true if the request is already handled so the .jsp shouldn't get called * @throws Exception */ private boolean prepareForJspGet( String target, Request request, HttpServletResponse httpServletResponse, boolean secured) throws Exception { LoginInfo.SessionInfo sessionInfo = UserHelpers.getSessionInfo(request); LOG.info( String.format( "hndl - %s ; %s; %s ; %s", target, request.getPathInfo(), request.getMethod(), secured ? "secured" : "not secured")); String path = request.getUri().getDecodedPath(); boolean redirectToLogin = path.equals(PATH_LOGOUT); LoginInfo loginInfo = null; if (sessionInfo.isNull()) { redirectToLogin = true; LOG.info("Null session info. Logging in again."); } else { loginInfo = loginInfoDb.get( sessionInfo.browserId, sessionInfo.sessionId); // ttt2 use a cache, to avoid going to DB if (loginInfo == null || loginInfo.expiresOn < System.currentTimeMillis()) { LOG.info("Session has expired. Logging in again. Info: " + loginInfo); redirectToLogin = true; } } if (!path.equals(PATH_LOGIN) && !path.equals(PATH_SIGNUP) && !path.equals(PATH_ERROR)) { if (redirectToLogin) { // ttt2 perhaps store URI, to return to it after login logOut(sessionInfo.browserId); addLoginParams(request, loginInfo); httpServletResponse.sendRedirect(PATH_LOGIN); return true; } User user = userDb.get(loginInfo.userId); if (user == null) { WebUtils.redirectToError("Unknown user", request, httpServletResponse); return true; } if (!user.active) { WebUtils.redirectToError("Account is not active", request, httpServletResponse); return true; } request.setAttribute(VAR_FEED_DB, feedDb); request.setAttribute(VAR_USER_DB, userDb); request.setAttribute(VAR_ARTICLE_DB, articleDb); request.setAttribute(VAR_READ_ARTICLES_COLL_DB, readArticlesCollDb); request.setAttribute(VAR_USER, user); request.setAttribute(VAR_LOGIN_INFO, loginInfo); MultiMap<String> params = new MultiMap<>(); params.put(PARAM_PATH, path); request.setParameters(params); } if (path.equals(PATH_LOGIN)) { addLoginParams(request, loginInfo); } return false; } private void handleOpenArticle( Request request, HttpServletResponse httpServletResponse, String target) throws Exception { try { int k1 = target.indexOf('/', 1); int k2 = target.indexOf('/', k1 + 1); String feedId = target.substring(k1 + 1, k2); String strSeq = target.substring(k2 + 1); int seq = Integer.parseInt(strSeq); Article article = articleDb.get(feedId, seq); LoginInfo loginInfo = userHelpers.getLoginInfo(request); // ttt2 using the link from a non-authenticated browser causes a NPE; maybe do something // better, e.g. sign up ReadArticlesColl readArticlesColl = readArticlesCollDb.get(loginInfo.userId, feedId); if (readArticlesColl == null) { readArticlesColl = new ReadArticlesColl(loginInfo.userId, feedId); } if (!readArticlesColl.isRead(seq)) { readArticlesColl.markRead(seq, Config.getConfig().maxSizeForReadArticles); readArticlesCollDb.add(readArticlesColl); } String s = URIUtil.encodePath(article.url) .replace("%3F", "?") .replace("%23", "#"); // ttt2 see how to do this right httpServletResponse.sendRedirect(s); } catch (Exception e) { WebUtils.showResult( String.format("Failed to get article for path %s. %s", target, e), "/", request, httpServletResponse); } } private void handleSignupPost(Request request, HttpServletResponse httpServletResponse) throws Exception { String userId = request.getParameter(PARAM_USER_ID); String userName = request.getParameter(PARAM_USER_NAME); String email = request.getParameter(PARAM_EMAIL); String stringPassword = request.getParameter(PARAM_PASSWORD); String stringPasswordConfirm = request.getParameter(PARAM_PASSWORD_CONFIRM); if (!stringPassword.equals(stringPasswordConfirm)) { WebUtils.redirectToError( "Mismatch between password and password confirmation", request, httpServletResponse); return; } SecureRandom secureRandom = new SecureRandom(); String salt = "" + secureRandom.nextLong(); byte[] password = User.computeHashedPassword(stringPassword, salt); User user = userDb.get(userId); if (user != null) { WebUtils.redirectToError( "There already exists a user with the ID " + userId, request, httpServletResponse); return; } user = new User( userId, userName, password, salt, email, new ArrayList<String>(), Config.getConfig().activateAccountsAtCreation, false); // ttt2 add confirmation by email, captcha, ... List<String> fieldErrors = user.checkFields(); if (!fieldErrors.isEmpty()) { StringBuilder bld = new StringBuilder("Invalid values when trying to create user with ID ") .append(userId) .append("<br/>"); for (String s : fieldErrors) { bld.append(s).append("<br/>"); } WebUtils.redirectToError(bld.toString(), request, httpServletResponse); return; } // ttt2 2 clients can add the same userId simultaneously userDb.add(user); httpServletResponse.sendRedirect("/"); } private void handleChangePasswordPost(Request request, HttpServletResponse httpServletResponse) throws Exception { LoginInfo loginInfo = userHelpers.getLoginInfo(request); if (loginInfo == null) { WebUtils.redirectToError("Couldn't determine the current user", request, httpServletResponse); return; } String userId = loginInfo.userId; String stringCrtPassword = request.getParameter(PARAM_CURRENT_PASSWORD); String stringNewPassword = request.getParameter(PARAM_PASSWORD); String stringNewPasswordConfirm = request.getParameter(PARAM_PASSWORD_CONFIRM); if (!stringNewPassword.equals(stringNewPasswordConfirm)) { showResult( "Mismatch between password and password confirmation", PATH_SETTINGS, request, httpServletResponse); return; } User user = userDb.get( userId); // ttt1 crashes for wrong ID; 2013.07.20 - no longer have an idea what this is // about if (user == null) { WebUtils.redirectToError("Couldn't find the current user", request, httpServletResponse); return; } if (!user.checkPassword(stringCrtPassword)) { showResult("Incorrect current password", PATH_SETTINGS, request, httpServletResponse); return; } SecureRandom secureRandom = new SecureRandom(); String salt = "" + secureRandom.nextLong(); byte[] password = User.computeHashedPassword(stringNewPassword, salt); user.salt = salt; user.password = password; // ttt3 2 clients can change the password simultaneously userDb.add(user); // httpServletResponse.sendRedirect(PATH_SETTINGS); showResult("Password changed", PATH_SETTINGS, request, httpServletResponse); } private void handleChangeSettingsPost(Request request, HttpServletResponse httpServletResponse) throws Exception { LoginInfo loginInfo = userHelpers.getLoginInfo(request); if (loginInfo == null) { WebUtils.redirectToError("Couldn't determine the current user", request, httpServletResponse); return; } String stringItemsPerPage = request.getParameter(PARAM_ITEMS_PER_PAGE); try { loginInfo.itemsPerPage = Integer.parseInt(stringItemsPerPage); } catch (Exception e) { showResult( "Error trying to set the items per page. Expected integer value but got " + stringItemsPerPage, PATH_SETTINGS, request, httpServletResponse); return; } loginInfo.style = request.getParameter(PARAM_STYLE); loginInfo.feedDateFormat = request.getParameter(PARAM_FEED_DATE_FORMAT); // ttt2 validate, better in JSP loginInfoDb.add(loginInfo); // httpServletResponse.sendRedirect(PATH_SETTINGS); showResult("Settings changed", "/", request, httpServletResponse); } private void handleUpdateFeedListPost(Request request, HttpServletResponse httpServletResponse) throws Exception { LOG.info("updating feed list"); // ttt2 implement httpServletResponse.sendRedirect(PATH_FEED_ADMIN); } private void handleAddFeedPost(Request request, HttpServletResponse httpServletResponse) throws Exception { LOG.info("adding feed"); User user = userHelpers.getUser(request); try { if (user == null) { LOG.error("User not found"); return; } String url = request.getParameter(PARAM_NEW_FEED_URL); // ttt1 add some validation; probably best try to actually get data, set the title, ... if (url == null || url.equals("")) { LOG.error("New feed not specified"); // ttt1 show some error return; } MessageDigest digest = MessageDigest.getInstance("MD5"); String feedId = PrintUtils.byteArrayAsUrlString(digest.digest(url.getBytes("UTF-8"))); feedId = feedId.substring(0, Config.getConfig().feedIdSize); Feed feed = feedDb.get(feedId); if (feed == null) { feed = new Feed(feedId, url); feedDb.add(feed); } if (user.feedIds.contains(feedId)) { LOG.error(String.format("Trying to add existing feed %s to user %s", feedId, user)); } else { user.feedIds.add(feedId); userDb.updateFeeds(user); } } finally { httpServletResponse.sendRedirect(PATH_FEED_ADMIN); } } private void handleRemoveFeedPost(Request request, HttpServletResponse httpServletResponse) throws Exception { LOG.info("removing feed"); User user = userHelpers.getUser(request); try { if (user == null) { LOG.error("User not found"); return; } String feedId = request.getParameter(PARAM_FEED_ID); LOG.info(String.format("Removing feed %s for user %s", feedId, user)); // ttt1 add some validation; probably best try to actually get data, set the title, ... if (feedId == null || feedId.equals("")) { LOG.error("feed not specified"); // ttt1 show some error return; } if (user.feedIds.remove( feedId)) { // ttt2 clean up the global feed table; that's probably better done if nobody // accesses a feed for 3 months or so userDb.updateFeeds(user); LOG.info(String.format("Removed feed %s for user %s", feedId, user)); } else { LOG.info(String.format("No feed found with ID %s for user %s", feedId, user)); } } finally { httpServletResponse.sendRedirect(PATH_FEED_ADMIN); } } private void handleLoginPost( Request request, HttpServletResponse httpServletResponse, boolean secured) throws Exception { String userId = request.getParameter(PARAM_USER_ID); String password = request.getParameter(PARAM_PASSWORD); String rememberAccountStr = request.getParameter(PARAM_REMEMBER_ACCOUNT); boolean rememberAccount = Boolean.parseBoolean(rememberAccountStr); LoginInfo.SessionInfo sessionInfo = UserHelpers.getSessionInfo(request); logOut(sessionInfo.browserId); User user = userDb.get(userId); if (user == null) { WebUtils.redirectToError("User " + userId + " not found", request, httpServletResponse); return; } if (!user.checkPassword(password)) { WebUtils.redirectToError("Invalid password", request, httpServletResponse); return; } if (!user.active) { WebUtils.redirectToError( "Account for User " + userId + " needs to be activated", request, httpServletResponse); return; } LOG.info("Logged in user " + userId); sessionInfo.sessionId = null; if (sessionInfo.browserId == null) { sessionInfo.browserId = getRandomId(); } else { for (LoginInfo loginInfo : loginInfoDb.getLoginsForBrowser(sessionInfo.browserId)) { if (userId.equals(loginInfo.userId)) { sessionInfo.sessionId = loginInfo.sessionId; break; } } } long expireOn = System.currentTimeMillis() + Config.getConfig().loginExpireInterval; if (sessionInfo.sessionId == null) { sessionInfo.sessionId = getRandomId(); Config config = Config.getConfig(); loginInfoDb.add( new LoginInfo( sessionInfo.browserId, sessionInfo.sessionId, userId, expireOn, rememberAccount, config.defaultStyle, config.defaultItemsPerPage, config.defaultFeedDateFormat)); LOG.info(String.format("Logging in in a new session. User: %s", user)); } else { loginInfoDb.updateExpireTime(sessionInfo.browserId, sessionInfo.sessionId, expireOn); LOG.info(String.format("Logging in in an existing session. User: %s", user)); } WebUtils.saveCookies( httpServletResponse, secured, sessionInfo.browserId, sessionInfo.sessionId); httpServletResponse.sendRedirect("/"); } private String getRandomId() { SecureRandom secureRandom = new SecureRandom(); return "" + secureRandom.nextLong(); } private void addLoginParams(Request request, LoginInfo loginInfo) { MultiMap<String> params = new MultiMap<>(); if (loginInfo != null && loginInfo.rememberAccount) { params.put(PARAM_USER_ID, loginInfo.userId); } request.setParameters(params); } private void logOut(String browserId) throws Exception { // ttt2 the right way to do it is to go through all the sessions of the current browser, which // would require a new field and a new index; // not sure if it's worth it, but this would work: A logs in, forgets to log out, B delets the // cookies, logs in, A sees B is logged in, then B // restores the cookies and uses A's account if (browserId == null) { return; } List<LoginInfo> loginInfos = loginInfoDb.getLoginsForBrowser(browserId); long expireTarget = System.currentTimeMillis() - Utils.ONE_DAY; for (LoginInfo loginInfo : loginInfos) { if (loginInfo.expiresOn <= expireTarget) { LOG.info(String.format("LoginInfo %s is enough in the past", loginInfo)); } else { LOG.info(String.format("Logging out: %s", loginInfo)); loginInfoDb.updateExpireTime(browserId, loginInfo.sessionId, expireTarget); } } } public static class FeedInfo { public String feedId; public int maxSeq; public FeedInfo(String feedId, int maxSeq) { this.feedId = feedId; this.maxSeq = maxSeq; } } // !!! IDEA reports this as unused, but it is called from JSP public static FeedInfo getFeedInfo(String feedPath) { if (feedPath.startsWith(PATH_FEED + "/")) { try { if (feedPath.endsWith("/")) { feedPath = feedPath.substring(0, feedPath.length() - 1); } int k = PATH_FEED.length() + 1; int p = feedPath.indexOf('/', k); return p >= 0 ? new FeedInfo(feedPath.substring(k, p), Integer.parseInt(feedPath.substring(p + 1))) : new FeedInfo(feedPath.substring(k), -1); } catch (Exception e) { LOG.error("Exception trying to parse the feed info", e); } } LOG.error("Invalid path from feed: " + feedPath); return new FeedInfo("INVALID", -1); } // !!! IDEA reports this as unused, but it is called from JSP public static String getStyle(LoginInfo loginInfo) { StringBuilder bld = new StringBuilder(); bld.append("<style media=\"screen\" type=\"text/css\">\n\n"); if (loginInfo == null) { bld.append(Config.getConfig().defaultStyle); } else { bld.append(loginInfo.style); // ttt3 detect broken styles and return default } bld.append("</style>\n"); return bld.toString(); } /* private void jspCodeCheck() throws Exception { Article.DB articleDb; Request request; String path = ""; String feedId = ReaderHandler.getFeedId(path); int maxSeq = ReaderHandler.getSeq(path); Feed.DB feedDb = (Feed.DB)request.getAttribute(ReaderHandler.VAR_FEED_DB); Feed feed = feedDb.get(feedId); if (feed == null) { out.println("Feed " + feedId + " not found"); } else { if (maxSeq == -1) { maxSeq = feed.maxSeq; } if (maxSeq < 0) { out.println("Feed " + feedId + " is empty"); } else { ++maxSeq; LoginInfo loginInfo = (LoginInfo)request.getAttribute(ReaderHandler.VAR_LOGIN_INFO); int minSeq = Math.max(maxSeq - loginInfo.itemsPerPage, 0); List<Article> articles = articleDb.get(feedId, minSeq, maxSeq); for (Article article : articles) { out.println("<a href=\"" + article.url + "\">" + article.title + "</a><br/>"); } } } } //*/ }