blob: ae0e82392ece43ea7e52f0ab158b06d0e815e8fe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.web.app;
import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
import static org.apache.openmeetings.util.OpenmeetingsVariables.wicketApplicationName;
import static org.apache.openmeetings.web.user.rooms.RoomEnterBehavior.getRoomUrlFragment;
import static org.apache.openmeetings.web.util.OmUrlFragment.PROFILE_MESSAGES;
import static org.red5.logging.Red5LoggerFactory.getLogger;
import static org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.openmeetings.IApplication;
import org.apache.openmeetings.core.remote.MainService;
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
import org.apache.openmeetings.db.dao.label.LabelDao;
import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.db.entity.room.Invitation;
import org.apache.openmeetings.db.entity.room.Room;
import org.apache.openmeetings.db.entity.room.Room.Right;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.openmeetings.db.entity.user.User.Type;
import org.apache.openmeetings.util.InitializationContainer;
import org.apache.openmeetings.web.pages.ActivatePage;
import org.apache.openmeetings.web.pages.MainPage;
import org.apache.openmeetings.web.pages.NotInitedPage;
import org.apache.openmeetings.web.pages.RecordingPage;
import org.apache.openmeetings.web.pages.ResetPage;
import org.apache.openmeetings.web.pages.SwfPage;
import org.apache.openmeetings.web.pages.auth.SignInPage;
import org.apache.openmeetings.web.pages.install.InstallWizardPage;
import org.apache.openmeetings.web.room.RoomSwfResourceReference;
import org.apache.openmeetings.web.user.dashboard.MyRoomsWidgetDescriptor;
import org.apache.openmeetings.web.user.dashboard.RssWidgetDescriptor;
import org.apache.openmeetings.web.user.dashboard.StartWidgetDescriptor;
import org.apache.openmeetings.web.user.dashboard.WelcomeWidgetDescriptor;
import org.apache.openmeetings.web.user.dashboard.admin.AdminWidgetDescriptor;
import org.apache.openmeetings.web.user.record.AviRecordingResourceReference;
import org.apache.openmeetings.web.user.record.FlvRecordingResourceReference;
import org.apache.openmeetings.web.user.record.JpgRecordingResourceReference;
import org.apache.openmeetings.web.user.record.Mp4RecordingResourceReference;
import org.apache.openmeetings.web.user.record.OggRecordingResourceReference;
import org.apache.openmeetings.web.util.ProfileImageResourceReference;
import org.apache.openmeetings.web.util.UserDashboardPersister;
import org.apache.wicket.Localizer;
import org.apache.wicket.Page;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.RuntimeConfigurationType;
import org.apache.wicket.ThreadContext;
import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession;
import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication;
import org.apache.wicket.core.request.handler.BookmarkableListenerInterfaceRequestHandler;
import org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler;
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
import org.apache.wicket.util.collections.ConcurrentHashSet;
import org.slf4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.wicketstuff.dashboard.WidgetRegistry;
import org.wicketstuff.dashboard.web.DashboardContext;
import org.wicketstuff.dashboard.web.DashboardContextInjector;
import org.wicketstuff.dashboard.web.DashboardSettings;
public class Application extends AuthenticatedWebApplication implements IApplication {
private static final Logger log = getLogger(Application.class, webAppRootKey);
private static boolean isInstalled;
private static ConcurrentHashMap<String, Client> ONLINE_USERS = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, Client> INVALID_SESSIONS = new ConcurrentHashMap<>();
private static ConcurrentHashMap<Long, Set<String>> ROOMS = new ConcurrentHashMap<>();
//additional maps for faster searching should be created
private DashboardContext dashboardContext;
private static Set<String> STRINGS_WITH_APP = new HashSet<>(); //FIXME need to be removed
private static String appName;
static {
STRINGS_WITH_APP.addAll(Arrays.asList("499", "500", "506", "511", "512", "513", "517", "532", "622", "804"
, "909", "952", "978", "981", "984", "989", "990", "999", "1151", "1155", "1157", "1158", "1194"));
}
@Override
protected void init() {
wicketApplicationName = super.getName();
getSecuritySettings().setAuthenticationStrategy(new OmAuthenticationStrategy());
//Add custom resource loader at the beginning, so it will be checked first in the
//chain of Resource Loaders, if not found it will search in Wicket's internal
//Resource Loader for a the property key
getResourceSettings().getStringResourceLoaders().add(0, new LabelResourceLoader());
super.init();
// register some widgets
dashboardContext = new DashboardContext();
dashboardContext.setDashboardPersister(new UserDashboardPersister());
WidgetRegistry widgetRegistry = dashboardContext.getWidgetRegistry();
widgetRegistry.registerWidget(new MyRoomsWidgetDescriptor());
widgetRegistry.registerWidget(new WelcomeWidgetDescriptor());
widgetRegistry.registerWidget(new StartWidgetDescriptor());
widgetRegistry.registerWidget(new RssWidgetDescriptor());
widgetRegistry.registerWidget(new AdminWidgetDescriptor());
// add dashboard context injector
getComponentInstantiationListeners().add(new DashboardContextInjector(dashboardContext));
DashboardSettings dashboardSettings = DashboardSettings.get();
dashboardSettings.setIncludeJQuery(false);
dashboardSettings.setIncludeJQueryUI(false);
getRootRequestMapperAsCompound().add(new NoVersionMapper(getHomePage()));
getRootRequestMapperAsCompound().add(new NoVersionMapper("notinited", NotInitedPage.class));
getRootRequestMapperAsCompound().add(new NoVersionMapper("swf", SwfPage.class));
getRootRequestMapperAsCompound().add(new NoVersionMapper("signin", getSignInPageClass()));
mountPage("install", InstallWizardPage.class);
mountPage("activate", ActivatePage.class);
mountPage("reset", ResetPage.class);
mountPage("/recording/${hash}", RecordingPage.class);
mountResource("/recordings/avi/${id}", new AviRecordingResourceReference());
mountResource("/recordings/flv/${id}", new FlvRecordingResourceReference());
mountResource("/recordings/mp4/${id}", new Mp4RecordingResourceReference());
mountResource("/recordings/ogg/${id}", new OggRecordingResourceReference());
mountResource("/recordings/jpg/${id}", new JpgRecordingResourceReference()); //should be in sync with VideoPlayer
mountResource("/room/swf/${id}", new RoomSwfResourceReference());
mountResource("/profile/${id}", new ProfileImageResourceReference());
}
private static class NoVersionMapper extends MountedMapper {
public NoVersionMapper(final Class<? extends IRequestablePage> pageClass) {
this("/", pageClass);
}
public NoVersionMapper(String mountPath, final Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
@Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
//Does nothing
}
@Override
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerInterfaceRequestHandler || requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
public static OmAuthenticationStrategy getAuthenticationStrategy() {
return (OmAuthenticationStrategy)get().getSecuritySettings().getAuthenticationStrategy();
}
@Override
public Class<? extends Page> getHomePage() {
return MainPage.class;
}
@Override
protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() {
return WebSession.class;
}
@Override
public Class<? extends WebPage> getSignInPageClass() {
return SignInPage.class;
}
public static Application get() {
return (Application) WebApplication.get();
}
public static DashboardContext getDashboardContext() {
return get().dashboardContext;
}
public static void addOnlineUser(Client c) {
log.debug("Adding online client: {}, room: {}", c.getUid(), c.getRoomId());
ONLINE_USERS.put(c.getUid(), c);
}
public static void removeOnlineUser(Client c) {
if (c != null) {
log.debug("Removing online client: {}, room: {}", c.getUid(), c.getRoomId());
ONLINE_USERS.remove(c.getUid());
}
}
@Override
public org.apache.openmeetings.db.entity.room.Client updateClient(org.apache.openmeetings.db.entity.room.Client rcl) {
Client client = getOnlineClient(rcl.getPublicSID());
if (rcl == null || client == null) {
return rcl;
}
rcl.setIsSuperModerator(client.hasRight(Right.superModerator));
rcl.setIsMod(client.hasRight(Right.moderator));
rcl.setIsBroadcasting(client.hasRight(Right.audio));
rcl.setCanVideo(client.hasRight(Right.video));
rcl.setCanDraw(client.hasRight(Right.whiteBoard));
return rcl;
}
public static Client getOnlineClient(String uid) {
return uid == null ? null : ONLINE_USERS.get(uid);
}
public static boolean isUserOnline(Long userId) {
boolean isUserOnline = false;
for (Map.Entry<String, Client> e : ONLINE_USERS.entrySet()) {
if (e.getValue().getUserId().equals(userId)) {
isUserOnline = true;
break;
}
}
return isUserOnline;
}
public static List<Client> getClients() {
return new ArrayList<Client>(ONLINE_USERS.values());
}
public static List<Client> getClients(Long userId) {
List<Client> result = new ArrayList<>();
for (Map.Entry<String, Client> e : ONLINE_USERS.entrySet()) {
if (e.getValue().getUserId().equals(userId)) {
result.add(e.getValue());
break;
}
}
return result;
}
public static int getClientsSize() {
return ONLINE_USERS.size();
}
public static Client getClientByKeys(Long userId, String sessionId) {
Client client = null;
for (Map.Entry<String, Client> e : ONLINE_USERS.entrySet()) {
Client c = e.getValue();
if (c.getUserId().equals(userId) && c.getSessionId().equals(sessionId)) {
client = c;
break;
}
}
return client;
}
@Override
public void invalidateClient(Long userId, String sessionId) {
Client client = getClientByKeys(userId, sessionId);
if (client != null) {
if (!INVALID_SESSIONS.containsKey(client.getSessionId())) {
INVALID_SESSIONS.put(client.getSessionId(), client);
removeOnlineUser(client);
}
}
}
public static boolean isInvaldSession(String sessionId) {
return sessionId == null ? false : INVALID_SESSIONS.containsKey(sessionId);
}
public static void removeInvalidSession(String sessionId) {
if (sessionId != null){
INVALID_SESSIONS.remove(sessionId);
}
}
public static Client addUserToRoom(Client c) {
log.debug("Adding online room client: {}, room: {}", c.getUid(), c.getRoomId());
ROOMS.putIfAbsent(c.getRoomId(), new ConcurrentHashSet<String>());
ROOMS.get(c.getRoomId()).add(c.getUid());
return c;
}
public static void removeUserFromRoom(long roomId, int pageId) {
Client c = new Client(WebSession.get().getId(), pageId, WebSession.getUserId());
c.setRoomId(roomId);
removeUserFromRoom(c);
}
public static Client removeUserFromRoom(Client c) {
log.debug("Removing online room client: {}, room: {}", c.getUid(), c.getRoomId());
if (c.getRoomId() != null) {
Set<String> clients = ROOMS.get(c.getRoomId());
if (clients != null) {
clients.remove(c.getUid());
c.setRoomId(null);
}
}
return c;
}
public static List<Client> getRoomClients(long roomId) {
List<Client> clients = new ArrayList<>();
Set<String> uids = ROOMS.get(roomId);
if (uids != null) {
for (String uid : uids) {
Client c = getOnlineClient(uid);
if (c != null) {
clients.add(c);
}
}
}
return clients;
}
public static Set<Long> getUserRooms(long userId) {
Set<Long> result = new HashSet<>();
for (Entry<Long, Set<String>> me : ROOMS.entrySet()) {
for (String uid : me.getValue()) {
Client c = getOnlineClient(uid);
if (c != null && c.getUserId().equals(userId)) {
result.add(me.getKey());
}
}
}
return result;
}
public static boolean isUserInRoom(long roomId, long userId) {
Set<String> clients = ROOMS.get(roomId);
if (clients != null) {
for (String uid : clients) {
if (getOnlineClient(uid).getUserId().equals(userId)) {
return true;
}
}
}
return false;
}
//TODO need more safe way FIXME
public <T> T _getBean(Class<T> clazz) {
WebApplicationContext wac = getWebApplicationContext(getServletContext());
return wac == null ? null : wac.getBean(clazz);
}
public static String getString(long id) {
return getString(id, WebSession.getLanguage());
}
public static String getString(String id) {
return getString(id, WebSession.getLanguage());
}
public static Locale getLocale(final long languageId) {
Locale loc = LabelDao.languages.get(languageId);
if (loc == null) {
loc = WebSession.exists() ? WebSession.get().getLocale() : Locale.ENGLISH;
}
return loc;
}
public static String getString(String key, final long languageId) {
return getString(key, getLocale(languageId), false);
}
public static String getString(long id, final long languageId) {
return getString(id, getLocale(languageId));
}
public static String getString(long id, final Locale loc) {
return getString("" + id, loc, false);
}
public static String getString(String key, final Locale loc, boolean noReplace) {
if (!exists()) {
ThreadContext.setApplication(Application.get(appName));
}
Localizer l = get().getResourceSettings().getLocalizer();
String value = l.getStringIgnoreSettings(key, null, null, loc, null, "[Missing]");
if (!noReplace && STRINGS_WITH_APP.contains(key)) {
final MessageFormat format = new MessageFormat(value, loc);
value = format.format(new Object[]{getBean(ConfigurationDao.class).getAppName()});
}
if (!noReplace && RuntimeConfigurationType.DEVELOPMENT == get().getConfigurationType()) {
value += String.format(" [%s]", key);
}
return value;
}
public static boolean isInstalled() {
boolean result = isInstalled;
if (!isInstalled) {
if (InitializationContainer.initComplete) {
//TODO can also check crypt class here
isInstalled = result = get()._getBean(UserDao.class).count() > 0;
}
}
return result;
}
public static <T> T getBean(Class<T> clazz) {
if (InitializationContainer.initComplete) {
if (!isInstalled()) {
throw new RestartResponseException(InstallWizardPage.class);
}
return get()._getBean(clazz);
} else {
throw new RestartResponseException(NotInitedPage.class);
}
}
@Override
public <T> T getOmBean(Class<T> clazz) { //FIXME hack for email templates support (should be in separate module for now
return Application.getBean(clazz);
}
public static String getContactsLink() {
return PROFILE_MESSAGES.getLink();
}
@Override
public String getOmContactsLink() { //FIXME hack for email templates support (should be in separate module for now
return getContactsLink();
}
public static String getInvitationLink(String baseUrl, Invitation i) {
String link = baseUrl;
if (link == null) {
return null;
}
Room r = i.getRoom();
User u = i.getInvitee();
if (r != null) {
boolean allowed = u.getType() != Type.contact;
if (allowed) {
allowed = getBean(MainService.class).isRoomAllowedToUser(r, u);
}
if (!allowed) {
link += "?invitationHash=" + i.getHash();
if (u.getLanguageId() > 0) {
link += "&language=" + u.getLanguageId();
}
} else {
link = getRoomUrlFragment(r.getId()).getLink();
}
}
return link;
}
@Override
public String getOmInvitationLink(String baseUrl, Invitation i) { //FIXME hack for email templates support (should be in separate module for now
return getInvitationLink(baseUrl, i);
}
public static String urlForPage(Class<? extends Page> clazz, PageParameters pp) {
RequestCycle rc = RequestCycle.get();
return rc.getUrlRenderer().renderFullUrl(Url.parse(getBean(ConfigurationDao.class).getBaseUrl() + rc.urlFor(clazz, pp)));
}
@Override
public String urlForActivatePage(PageParameters pp) { //FIXME hack for email templates support (should be in separate module for now
return urlForPage(ActivatePage.class, pp);
}
@Override
public String getOmString(long id) {
return getString(id);
}
@Override
public String getOmString(long id, long languageId) {
return getString(id, languageId);
}
@Override
public String getOmString(String key, long languageId) {
return getString(key, languageId);
}
}