crypto/fips/org.argeo.tp.crypto \
org.argeo.tp \
org.argeo.tp.httpd \
+org.argeo.tp.sshd \
osgi/equinox/org.argeo.tp.osgi \
osgi/equinox/org.argeo.tp.eclipse \
swt/rap/org.argeo.tp.swt \
org.argeo.cms \
swt/org.argeo.cms \
org.argeo.tp \
-org.argeo.tp.jetty \
+org.argeo.tp.httpd \
osgi/equinox/org.argeo.tp.eclipse \
osgi/api/org.argeo.tp.osgi \
swt/rcp/org.argeo.tp.swt \
static CmsLog getLog(String name) {
if (isSystemLoggerAvailable) {
- return new SystemCmsLog(name);
+ SystemCmsLog systemCmsLog = new SystemCmsLog(name);
+ if (systemCmsLog.logger == null) {
+ System.err.println("System logger unexpectedly null for " + name + ", switching to fall back");
+ return new FallBackCmsLog();
+ }
+ return systemCmsLog;
} else { // typically Android
return new FallBackCmsLog();
}
* Java platform.
*/
class SystemCmsLog implements CmsLog {
- private final Logger logger;
+ final Logger logger;
SystemCmsLog(String name) {
logger = System.getLogger(name);
+ assert logger != null : "System logger should not be null";
}
@Override
Object getData(String key);
void setData(String key, Object value);
-
+
CmsView getCmsView();
+
+ void updateLastAccess();
+
+ default boolean isTimedOut() {
+ return false;
+ };
+
}
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
+import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
throw new UnsupportedOperationException();
}
+ /** Schedule a one-shot UX task to be executed within the UX context/thread. */
+ TimerTask schedule(Runnable task, long delay);
+
+ /** Schedule a recurring UX task to be executed within the UX context/thread. */
+ TimerTask schedule(Runnable task, long delay, long period);
+
}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.util.ExceptionsChain;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/** Serialisable wrapper of a {@link Throwable}. */
-public class CmsExceptionsChain extends ExceptionsChain {
- public final static CmsLog log = CmsLog.getLog(CmsExceptionsChain.class);
-
- public CmsExceptionsChain() {
- super();
- }
-
- public CmsExceptionsChain(Throwable exception) {
- super(exception);
- if (log.isDebugEnabled())
- log.error("Exception chain", exception);
- }
-
- public String toJsonString(ObjectMapper objectMapper) {
- try {
- return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(this);
- } catch (JsonProcessingException e) {
- throw new IllegalStateException("Cannot write system exceptions " + toString(), e);
- }
- }
-
- public void writeAsJson(ObjectMapper objectMapper, Writer writer) {
- try {
- JsonGenerator jg = objectMapper.writerWithDefaultPrettyPrinter().getFactory().createGenerator(writer);
- jg.writeObject(this);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot write system exceptions " + toString(), e);
- }
- }
-
- public void writeAsJson(ObjectMapper objectMapper, HttpServletResponse resp) {
- try {
- resp.setContentType("application/json");
- resp.setStatus(500);
- writeAsJson(objectMapper, resp.getWriter());
- } catch (IOException e) {
- throw new IllegalStateException("Cannot write system exceptions " + toString(), e);
- }
- }
-
-// public static void main(String[] args) throws Exception {
-// try {
-// try {
-// try {
-// testDeeper();
-// } catch (Exception e) {
-// throw new Exception("Less deep exception", e);
-// }
-// } catch (Exception e) {
-// throw new RuntimeException("Top exception", e);
-// }
-// } catch (Exception e) {
-// CmsExceptionsChain systemErrors = new CmsExceptionsChain(e);
-// ObjectMapper objectMapper = new ObjectMapper();
-// System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(systemErrors));
-// System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(e));
-// e.printStackTrace();
-// }
-// }
-//
-// static void testDeeper() throws Exception {
-// throw new IllegalStateException("Deep exception");
-// }
-
-}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsSessionId;
-import org.argeo.cms.auth.RemoteAuthCallback;
-import org.argeo.cms.auth.RemoteAuthCallbackHandler;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-import org.osgi.service.useradmin.Authorization;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/** Externally authenticate an http session. */
-public class CmsLoginServlet extends HttpServlet {
- public final static String PARAM_USERNAME = "username";
- public final static String PARAM_PASSWORD = "password";
-
- private static final long serialVersionUID = 2478080654328751539L;
- private ObjectMapper objectMapper = new ObjectMapper();
-
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doPost(request, response);
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- LoginContext lc = null;
- String username = req.getParameter(PARAM_USERNAME);
- String password = req.getParameter(PARAM_PASSWORD);
- ServletHttpRequest request = new ServletHttpRequest(req);
- ServletHttpResponse response = new ServletHttpResponse(resp);
- try {
- lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, new RemoteAuthCallbackHandler(request, response) {
- public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
- for (Callback callback : callbacks) {
- if (callback instanceof NameCallback && username != null)
- ((NameCallback) callback).setName(username);
- else if (callback instanceof PasswordCallback && password != null)
- ((PasswordCallback) callback).setPassword(password.toCharArray());
- else if (callback instanceof RemoteAuthCallback) {
- ((RemoteAuthCallback) callback).setRequest(request);
- ((RemoteAuthCallback) callback).setResponse(response);
- }
- }
- }
- });
- lc.login();
-
- Subject subject = lc.getSubject();
- CmsSessionId cmsSessionId = extractFrom(subject.getPrivateCredentials(CmsSessionId.class));
- if (cmsSessionId == null) {
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- return;
- }
- Authorization authorization = extractFrom(subject.getPrivateCredentials(Authorization.class));
- Locale locale = extractFrom(subject.getPublicCredentials(Locale.class));
-
- CmsSessionDescriptor cmsSessionDescriptor = new CmsSessionDescriptor(authorization.getName(),
- cmsSessionId.getUuid().toString(), authorization.getRoles(), authorization.toString(),
- locale != null ? locale.toString() : null);
-
- resp.setContentType("application/json");
- JsonGenerator jg = objectMapper.getFactory().createGenerator(resp.getWriter());
- jg.writeObject(cmsSessionDescriptor);
-
- String redirectTo = redirectTo(req);
- if (redirectTo != null)
- resp.sendRedirect(redirectTo);
- } catch (LoginException e) {
- resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- return;
- }
- }
-
- protected <T> T extractFrom(Set<T> creds) {
- if (creds.size() > 0)
- return creds.iterator().next();
- else
- return null;
- }
-
- /**
- * To be overridden in order to return a richer {@link CmsSessionDescriptor} to
- * be serialized.
- */
- protected CmsSessionDescriptor enrichJson(CmsSessionDescriptor cmsSessionDescriptor) {
- return cmsSessionDescriptor;
- }
-
- protected String redirectTo(HttpServletRequest request) {
- return null;
- }
-}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.IOException;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsSessionId;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.auth.RemoteAuthCallback;
-import org.argeo.cms.auth.RemoteAuthCallbackHandler;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-
-/** Externally authenticate an http session. */
-public class CmsLogoutServlet extends HttpServlet {
- private static final long serialVersionUID = 2478080654328751539L;
-
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doPost(request, response);
- }
-
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- ServletHttpRequest httpRequest = new ServletHttpRequest(request);
- ServletHttpResponse httpResponse = new ServletHttpResponse(response);
- LoginContext lc = null;
- try {
- lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER,
- new RemoteAuthCallbackHandler(httpRequest, httpResponse) {
- public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
- for (Callback callback : callbacks) {
- if (callback instanceof RemoteAuthCallback) {
- ((RemoteAuthCallback) callback).setRequest(httpRequest);
- ((RemoteAuthCallback) callback).setResponse(httpResponse);
- }
- }
- }
- });
- lc.login();
-
- Subject subject = lc.getSubject();
- CmsSessionId cmsSessionId = extractFrom(subject.getPrivateCredentials(CmsSessionId.class));
- if (cmsSessionId != null) {// logged in
- CurrentUser.logoutCmsSession(subject);
- }
-
- } catch (LoginException e) {
- // ignore
- }
-
- String redirectTo = redirectTo(request);
- if (redirectTo != null)
- response.sendRedirect(redirectTo);
- }
-
- protected <T> T extractFrom(Set<T> creds) {
- if (creds.size() > 0)
- return creds.iterator().next();
- else
- return null;
- }
-
- protected String redirectTo(HttpServletRequest request) {
- return null;
- }
-}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.IOException;
-import java.security.AccessControlContext;
-import java.util.Map;
-
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.cms.auth.RemoteAuthCallbackHandler;
-import org.argeo.cms.auth.RemoteAuthUtils;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-import org.osgi.service.http.context.ServletContextHelper;
-
-/** Manages security access to servlets. */
-public class CmsPrivateServletContext extends ServletContextHelper {
- public final static String LOGIN_PAGE = "argeo.cms.integration.loginPage";
- public final static String LOGIN_SERVLET = "argeo.cms.integration.loginServlet";
- private String loginPage;
- private String loginServlet;
-
- public void init(Map<String, String> properties) {
- loginPage = properties.get(LOGIN_PAGE);
- loginServlet = properties.get(LOGIN_SERVLET);
- }
-
- /**
- * Add the {@link AccessControlContext} as a request attribute, or redirect to
- * the login page.
- */
- @Override
- public boolean handleSecurity(final HttpServletRequest req, HttpServletResponse resp) throws IOException {
- LoginContext lc = null;
- ServletHttpRequest request = new ServletHttpRequest(req);
- ServletHttpResponse response = new ServletHttpResponse(resp);
-
- String pathInfo = req.getPathInfo();
- String servletPath = req.getServletPath();
- if ((pathInfo != null && (servletPath + pathInfo).equals(loginPage)) || servletPath.contentEquals(loginServlet))
- return true;
- try {
- lc = CmsAuth.USER.newLoginContext(new RemoteAuthCallbackHandler(request, response));
- lc.login();
- } catch (LoginException e) {
- lc = processUnauthorized(req, resp);
- if (lc == null)
- return false;
- }
-// Subject.doAs(lc.getSubject(), new PrivilegedAction<Void>() {
-//
-// @Override
-// public Void run() {
-// // TODO also set login context in order to log out ?
-// RemoteAuthUtils.configureRequestSecurity(request);
-// return null;
-// }
-//
-// });
-
- return true;
- }
-
-// @Override
-// public void finishSecurity(HttpServletRequest req, HttpServletResponse resp) {
-// RemoteAuthUtils.clearRequestSecurity(new ServletHttpRequest(req));
-// }
-
- protected LoginContext processUnauthorized(HttpServletRequest request, HttpServletResponse response) {
- try {
- response.sendRedirect(loginPage);
- } catch (IOException e) {
- throw new RuntimeException("Cannot redirect to login page", e);
- }
- return null;
- }
-}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.argeo.api.cms.CmsSession;
-import org.osgi.service.useradmin.Authorization;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-
-/** A serializable descriptor of an internal {@link CmsSession}. */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class CmsSessionDescriptor implements Serializable, Authorization {
- private static final long serialVersionUID = 8592162323372641462L;
-
- private String name;
- private String cmsSessionId;
- private String displayName;
- private String locale;
- private Set<String> roles;
-
- public CmsSessionDescriptor() {
- }
-
- public CmsSessionDescriptor(String name, String cmsSessionId, String[] roles, String displayName, String locale) {
- this.name = name;
- this.displayName = displayName;
- this.cmsSessionId = cmsSessionId;
- this.locale = locale;
- this.roles = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList(roles)));
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
- public String getCmsSessionId() {
- return cmsSessionId;
- }
-
- public void setCmsSessionId(String cmsSessionId) {
- this.cmsSessionId = cmsSessionId;
- }
-
- public Boolean isAnonymous() {
- return name == null;
- }
-
- public String getLocale() {
- return locale;
- }
-
- public void setLocale(String locale) {
- this.locale = locale;
- }
-
- @Override
- public boolean hasRole(String name) {
- return roles.contains(name);
- }
-
- @Override
- public String[] getRoles() {
- return roles.toArray(new String[roles.size()]);
- }
-
- public void setRoles(String[] roles) {
- this.roles = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList(roles)));
- }
-
- @Override
- public int hashCode() {
- return cmsSessionId != null ? cmsSessionId.hashCode() : super.hashCode();
- }
-
- @Override
- public String toString() {
- return displayName != null ? displayName : name != null ? name : super.toString();
- }
-
-}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.IOException;
-import java.time.ZonedDateTime;
-import java.util.Set;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.acr.ldap.NamingUtils;
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.directory.CmsUserManager;
-import org.argeo.cms.auth.RemoteAuthCallback;
-import org.argeo.cms.auth.RemoteAuthCallbackHandler;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-import org.osgi.service.useradmin.Authorization;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/** Provides access to tokens. */
-public class CmsTokenServlet extends HttpServlet {
- private static final long serialVersionUID = 302918711430864140L;
-
- public final static String PARAM_EXPIRY_DATE = "expiryDate";
- public final static String PARAM_TOKEN = "token";
-
- private final static int DEFAULT_HOURS = 24;
-
- private CmsUserManager userManager;
- private ObjectMapper objectMapper = new ObjectMapper();
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- ServletHttpRequest request = new ServletHttpRequest(req);
- ServletHttpResponse response = new ServletHttpResponse(resp);
- LoginContext lc = null;
- try {
- lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, new RemoteAuthCallbackHandler(request, response) {
- public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
- for (Callback callback : callbacks) {
- if (callback instanceof RemoteAuthCallback) {
- ((RemoteAuthCallback) callback).setRequest(request);
- ((RemoteAuthCallback) callback).setResponse(response);
- }
- }
- }
- });
- lc.login();
- } catch (LoginException e) {
- // ignore
- }
-
- try {
- Subject subject = lc.getSubject();
- Authorization authorization = extractFrom(subject.getPrivateCredentials(Authorization.class));
- String token = UUID.randomUUID().toString();
- String expiryDateStr = req.getParameter(PARAM_EXPIRY_DATE);
- ZonedDateTime expiryDate;
- if (expiryDateStr != null) {
- expiryDate = NamingUtils.ldapDateToZonedDateTime(expiryDateStr);
- } else {
- expiryDate = ZonedDateTime.now().plusHours(DEFAULT_HOURS);
- expiryDateStr = NamingUtils.instantToLdapDate(expiryDate);
- }
- userManager.addAuthToken(authorization.getName(), token, expiryDate);
-
- TokenDescriptor tokenDescriptor = new TokenDescriptor();
- tokenDescriptor.setUsername(authorization.getName());
- tokenDescriptor.setToken(token);
- tokenDescriptor.setExpiryDate(expiryDateStr);
-// tokenDescriptor.setRoles(Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList(roles))));
-
- resp.setContentType("application/json");
- JsonGenerator jg = objectMapper.getFactory().createGenerator(resp.getWriter());
- jg.writeObject(tokenDescriptor);
- } catch (Exception e) {
- new CmsExceptionsChain(e).writeAsJson(objectMapper, resp);
- }
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // temporarily wrap POST for ease of testing
- doPost(req, resp);
- }
-
- @Override
- protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- try {
- String token = req.getParameter(PARAM_TOKEN);
- userManager.expireAuthToken(token);
- } catch (Exception e) {
- new CmsExceptionsChain(e).writeAsJson(objectMapper, resp);
- }
- }
-
- protected <T> T extractFrom(Set<T> creds) {
- if (creds.size() > 0)
- return creds.iterator().next();
- else
- return null;
- }
-
- public void setUserManager(CmsUserManager userManager) {
- this.userManager = userManager;
- }
-}
+++ /dev/null
-package org.argeo.cms.integration;
-
-import java.io.Serializable;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-
-/** A serializable descriptor of a token. */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class TokenDescriptor implements Serializable {
- private static final long serialVersionUID = -6607393871416803324L;
-
- private String token;
- private String username;
- private String expiryDate;
-// private Set<String> roles;
-
- public String getToken() {
- return token;
- }
-
- public void setToken(String token) {
- this.token = token;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
-// public Set<String> getRoles() {
-// return roles;
-// }
-//
-// public void setRoles(Set<String> roles) {
-// this.roles = roles;
-// }
-
- public String getExpiryDate() {
- return expiryDate;
- }
-
- public void setExpiryDate(String expiryDate) {
- this.expiryDate = expiryDate;
- }
-
-}
+++ /dev/null
-/** Argeo CMS integration (JSON, web services). */
-package org.argeo.cms.integration;
\ No newline at end of file
@ServerEndpoint(value = "/cms/status/event/{topic}", configurator = CmsWebSocketConfigurator.class)
public class EventEndpoint implements CmsEventSubscriber {
private final static CmsLog log = CmsLog.getLog(EventEndpoint.class);
- private BundleContext bc = FrameworkUtil.getBundle(TestEndpoint.class).getBundleContext();
+ private BundleContext bc = FrameworkUtil.getBundle(EventEndpoint.class).getBundleContext();
private RemoteEndpoint.Basic remote;
private CmsEventBus cmsEventBus;
Set<Class<?>> res = new HashSet<>();
res.add(PingEndpoint.class);
res.add(EventEndpoint.class);
- res.add(TestEndpoint.class);
return res;
}
+++ /dev/null
-package org.argeo.cms.websocket.server;
-
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import javax.security.auth.Subject;
-import javax.websocket.CloseReason;
-import javax.websocket.EndpointConfig;
-import javax.websocket.OnClose;
-import javax.websocket.OnError;
-import javax.websocket.OnMessage;
-import javax.websocket.OnOpen;
-import javax.websocket.RemoteEndpoint;
-import javax.websocket.Session;
-import javax.websocket.server.PathParam;
-import javax.websocket.server.ServerEndpoint;
-
-import org.argeo.api.acr.ldap.NamingUtils;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.integration.CmsExceptionsChain;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventConstants;
-import org.osgi.service.event.EventHandler;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/** Provides WebSocket access. */
-@ServerEndpoint(value = "/cms/status/test/{topic}", configurator = CmsWebSocketConfigurator.class)
-public class TestEndpoint implements EventHandler {
- private final static CmsLog log = CmsLog.getLog(TestEndpoint.class);
-
- final static String TOPICS_BASE = "/test";
- final static String INPUT = "input";
- final static String TOPIC = "topic";
- final static String VIEW_UID = "viewUid";
- final static String COMPUTATION_UID = "computationUid";
- final static String MESSAGES = "messages";
- final static String ERRORS = "errors";
-
- final static String EXCEPTION = "exception";
- final static String MESSAGE = "message";
-
- private BundleContext bc = FrameworkUtil.getBundle(TestEndpoint.class).getBundleContext();
-
- private String wsSessionId;
- private RemoteEndpoint.Basic remote;
- private ServiceRegistration<EventHandler> eventHandlerSr;
-
- // json
- private ObjectMapper objectMapper = new ObjectMapper();
-
- private WebSocketView view;
-
- @OnOpen
- public void onOpen(Session session, EndpointConfig endpointConfig) {
- Map<String, List<String>> parameters = NamingUtils.queryToMap(session.getRequestURI());
- String path = NamingUtils.getQueryValue(parameters, "path");
- log.debug("WS Path: " + path);
-
- wsSessionId = session.getId();
-
- // 24h timeout
- session.setMaxIdleTimeout(1000 * 60 * 60 * 24);
-
- Map<String, Object> userProperties = session.getUserProperties();
- Subject subject = null;
-// AccessControlContext accessControlContext = (AccessControlContext) userProperties
-// .get(ServletContextHelper.REMOTE_USER);
-// Subject subject = Subject.getSubject(accessControlContext);
-// // Deal with authentication failure
-// if (subject == null) {
-// try {
-// CloseReason.CloseCode closeCode = new CloseReason.CloseCode() {
-//
-// @Override
-// public int getCode() {
-// return 4001;
-// }
-// };
-// session.close(new CloseReason(closeCode, "Unauthorized"));
-// if (log.isTraceEnabled())
-// log.trace("Unauthorized web socket " + wsSessionId + ". Closing with code " + closeCode.getCode()
-// + ".");
-// return;
-// } catch (IOException e) {
-// // silent
-// }
-// return;// ignore
-// }
-
- if (log.isDebugEnabled())
- log.debug("WS#" + wsSessionId + " open for: " + subject);
- remote = session.getBasicRemote();
- view = new WebSocketView(subject);
-
- // OSGi events
- String[] topics = new String[] { TOPICS_BASE + "/*" };
- Hashtable<String, Object> ht = new Hashtable<>();
- ht.put(EventConstants.EVENT_TOPIC, topics);
- ht.put(EventConstants.EVENT_FILTER, "(" + VIEW_UID + "=" + view.getUid() + ")");
- eventHandlerSr = bc.registerService(EventHandler.class, this, ht);
-
- if (log.isDebugEnabled())
- log.debug("New view " + view.getUid() + " opened, via web socket.");
- }
-
- @OnMessage
- public void onWebSocketText(@PathParam("topic") String topic, Session session, String message)
- throws JsonMappingException, JsonProcessingException {
- try {
- if (log.isTraceEnabled())
- log.trace("WS#" + view.getUid() + " received:\n" + message + "\n");
-// JsonNode jsonNode = objectMapper.readTree(message);
-// String topic = jsonNode.get(TOPIC).textValue();
-
- final String computationUid = null;
-// if (MY_TOPIC.equals(topic)) {
-// view.checkRole(SPECIFIC_ROLE);
-// computationUid= process();
-// }
- remote.sendText("ACK " + topic);
- } catch (Exception e) {
- log.error("Error when receiving web socket message", e);
- sendSystemErrorMessage(e);
- }
- }
-
- @OnClose
- public void onWebSocketClose(CloseReason reason) {
- if (eventHandlerSr != null)
- eventHandlerSr.unregister();
- if (view != null && log.isDebugEnabled())
- log.debug("WS#" + view.getUid() + " closed: " + reason);
- }
-
- @OnError
- public void onWebSocketError(Throwable cause) {
- if (view != null) {
- log.error("WS#" + view.getUid() + " ERROR", cause);
- } else {
- if (log.isTraceEnabled())
- log.error("Error in web socket session " + wsSessionId, cause);
- }
- }
-
- @Override
- public void handleEvent(Event event) {
- try {
- Object uid = event.getProperty(COMPUTATION_UID);
- Exception exception = (Exception) event.getProperty(EXCEPTION);
- if (exception != null) {
- CmsExceptionsChain systemErrors = new CmsExceptionsChain(exception);
- String sent = systemErrors.toJsonString(objectMapper);
- remote.sendText(sent);
- return;
- }
- String topic = event.getTopic();
- if (log.isTraceEnabled())
- log.trace("WS#" + view.getUid() + " " + topic + ": notify event " + topic + "#" + uid + ", " + event);
- } catch (Exception e) {
- log.error("Error when handling event for WebSocket", e);
- sendSystemErrorMessage(e);
- }
-
- }
-
- /** Sends an error message in JSON format. */
- protected void sendSystemErrorMessage(Exception e) {
- CmsExceptionsChain systemErrors = new CmsExceptionsChain(e);
- try {
- if (remote != null)
- remote.sendText(systemErrors.toJsonString(objectMapper));
- } catch (Exception e1) {
- log.error("Cannot send WebSocket system error messages " + systemErrors, e1);
- }
- }
-}
package org.argeo.cms.jshell;
import java.io.IOException;
+import java.nio.file.ClosedWatchServiceException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
private Path jtermBase;
private Path jtermLinkedDir;
+ private WatchService watchService;
+
public void start() throws Exception {
// TODO better define application id, make it configurable
String applicationID;
// TODO centralise state run dir
stateRunDir = OS.getRunDir().resolve(applicationID);
+ // TODO factorise create/delete pattern
jshBase = stateRunDir.resolve(JShellClient.JSH);
+ if (Files.exists(jshBase)) {
+ log.warn(jshBase + " already exists, deleting it...");
+ FsUtils.delete(jshBase);
+ }
Files.createDirectories(jshBase);
- jshLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JSH), jshBase);
+ jshLinkedDir = cmsState.getStatePath(JShellClient.JSH);
+ if (Files.exists(jshLinkedDir)) {
+ log.warn(jshLinkedDir + " already exists, deleting it...");
+ FsUtils.delete(jshLinkedDir);
+ }
+ Files.createSymbolicLink(jshLinkedDir, jshBase);
jtermBase = stateRunDir.resolve(JShellClient.JTERM);
+ if (Files.exists(jtermBase)) {
+ log.warn(jtermBase + " already exists, deleting it...");
+ FsUtils.delete(jtermBase);
+ }
Files.createDirectories(jtermBase);
- jtermLinkedDir = Files.createSymbolicLink(cmsState.getStatePath(JShellClient.JTERM), jtermBase);
+ jtermLinkedDir = cmsState.getStatePath(JShellClient.JTERM);
+ if (Files.exists(jtermLinkedDir)) {
+ log.warn(jtermLinkedDir + " already exists, deleting it...");
+ FsUtils.delete(jtermLinkedDir);
+ }
+ Files.createSymbolicLink(jtermLinkedDir, jtermBase);
log.info("Local JShell on " + jshBase + ", linked to " + jshLinkedDir);
log.info("Local JTerm on " + jtermBase + ", linked to " + jtermLinkedDir);
new Thread(() -> {
try {
- WatchService watchService = FileSystems.getDefault().newWatchService();
+ watchService = FileSystems.getDefault().newWatchService();
jshBase.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE);
}
key.reset();
}
+ } catch (ClosedWatchServiceException e) {
+ if (log.isTraceEnabled())
+ log.trace("JShell file watch service was closed");
} catch (IOException | InterruptedException e) {
- e.printStackTrace();
+ log.error("Unexpected exception in JShell file watch service", e);
}
}, "JShell local sessions watcher").start();
}
}
public void stop() {
+ if (watchService != null)
+ try {
+ watchService.close();
+ } catch (IOException e) {
+ log.error("Cannot close JShell watch service", e);
+ }
try {
Files.delete(jshLinkedDir);
} catch (IOException e) {
- log.error("Cannot remove " + jshLinkedDir);
+ log.error("Cannot remove " + jshLinkedDir, e);
+ }
+ try {
+ FsUtils.delete(jshBase);
+ } catch (IOException e) {
+ log.error("Cannot remove " + jshBase, e);
}
try {
Files.delete(jtermLinkedDir);
} catch (IOException e) {
- log.error("Cannot remove " + jtermLinkedDir);
+ log.error("Cannot remove " + jtermLinkedDir, e);
+ }
+ try {
+ FsUtils.delete(jtermBase);
+ } catch (IOException e) {
+ log.error("Cannot remove " + jtermBase, e);
}
}
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
-import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
+import org.eclipse.jetty.ee8.nested.SessionHandler;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee8.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.eclipse.jetty.ee8.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
/** A {@link JettyHttpServer} which is compatible with Equinox servlets. */
public class CmsJettyServer extends JettyHttpServer {
import java.util.Map;
import java.util.Set;
-import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.ee8.nested.ContextHandler;
/**
* A {@link Map} implementation wrapping the attributes of a Jetty
package org.argeo.cms.jetty;
-import java.util.AbstractMap;
-import java.util.Enumeration;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
import javax.servlet.ServletContext;
import javax.websocket.DeploymentException;
import org.argeo.cms.servlet.httpserver.HttpContextServlet;
import org.argeo.cms.websocket.server.WebsocketEndpoints;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
+import org.eclipse.jetty.ee8.nested.SessionHandler;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee8.servlet.ServletHolder;
+import org.eclipse.jetty.ee8.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.eclipse.jetty.ee8.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
import com.sun.net.httpserver.HttpHandler;
import java.util.List;
import java.util.Objects;
-import javax.servlet.ServletContext;
-import javax.websocket.DeploymentException;
-import javax.websocket.server.ServerContainer;
-
import org.argeo.cms.websocket.server.WebsocketEndpoints;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Filter;
import org.argeo.api.cms.CmsState;
import org.argeo.cms.CmsDeployProperty;
import org.argeo.cms.http.server.HttpServerUtils;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
JettyHttpContext httpContext = contexts.remove(path);
if (httpContext instanceof ContextHandlerHttpContext contextHandlerHttpContext) {
// TODO stop handler first?
- contextHandlerCollection.removeHandler(contextHandlerHttpContext.getServletContextHandler());
+ // FIXME understand compatibility with Jetty 12
+ //contextHandlerCollection.removeHandler(contextHandlerHttpContext.getServletContextHandler());
} else {
// FIXME apparently servlets cannot be removed in Jetty, we should replace the
// handler
import org.argeo.api.cms.CmsLog;
import org.argeo.cms.servlet.httpserver.HttpContextServlet;
import org.argeo.cms.websocket.server.WebsocketEndpoints;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee8.servlet.ServletHolder;
import com.sun.net.httpserver.HttpHandler;
(PrivateKey) store.getKey(CmsConstants.NODE, keyStorePassword));
} catch (IOException | KeyStoreException | NoSuchProviderException | NoSuchAlgorithmException
| CertificateException | IllegalArgumentException | UnrecoverableKeyException e) {
- log.error("Cannot add node public key to SSH authorized keys", e);
+ if (log.isTraceEnabled())
+ log.error("Cannot add node public key to SSH authorized keys", e);
+ else
+ log.error("Cannot add node public key to SSH authorized keys: " + e.getMessage());
return null;
}
+++ /dev/null
-package org.argeo.cms.media;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import org.apache.batik.transcoder.TranscoderException;
-import org.apache.batik.transcoder.TranscoderInput;
-import org.apache.batik.transcoder.TranscoderOutput;
-import org.apache.batik.transcoder.image.ImageTranscoder;
-import org.apache.batik.transcoder.image.PNGTranscoder;
-
-public class SvgToPng {
-
- public void convertSvgDir(Path sourceDir, Path targetDir, int width) {
- System.out.println("##\n## " + width + "px - " + sourceDir + "\n##");
- try {
- if (targetDir == null)
- targetDir = sourceDir.getParent().resolve(Integer.toString(width));
- Files.createDirectories(targetDir);
-
- PNGTranscoder transcoder = new PNGTranscoder();
- // transcoder.addTranscodingHint(ImageTranscoder.KEY_BACKGROUND_COLOR,
- // Color.WHITE);
- transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) width);
- transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) width);
-
- for (Path source : Files.newDirectoryStream(sourceDir, "*.svg")) {
- // FIXME extract base name
- String baseName = null; // = FilenameUtils.getBaseName(source.toString());
- Path target = targetDir.resolve(baseName + ".png");
- convertSvgFile(transcoder, source, target);
- }
- } catch (IOException | TranscoderException e) {
- throw new IllegalStateException("Cannot convert from " + sourceDir + " to " + targetDir, e);
- }
-
- }
-
- protected void convertSvgFile(ImageTranscoder transcoder, Path source, Path target)
- throws IOException, TranscoderException {
- try (Reader reader = Files.newBufferedReader(source); OutputStream out = Files.newOutputStream(target);) {
- TranscoderInput input = new TranscoderInput(reader);
-// BufferedImage image = transcoder.createImage(32, 32);
- TranscoderOutput output = new TranscoderOutput(out);
- transcoder.transcode(input, output);
- System.out.println(source.getFileName() + " -> " + target);
- }
- }
-
- public static void main(String[] args) throws Exception {
-
- Path path = Paths.get(args[0]);
-
- SvgToPng svgToPng = new SvgToPng();
- svgToPng.convertSvgDir(path, null, 16);
- svgToPng.convertSvgDir(path, null, 32);
- svgToPng.convertSvgDir(path, null, 64);
- svgToPng.convertSvgDir(path, null, 96);
- }
-}
import org.argeo.api.cms.ux.CmsView;
import org.argeo.cms.util.CurrentSubject;
+/** Utilities around UX. */
public class CmsUxUtils {
public static ContentSession getContentSession(ContentRepository contentRepository, CmsView cmsView) {
return CurrentSubject.callAs(cmsView.getCmsSession().getSubject(), () -> contentRepository.get());
return content.getName().getLocalPart();
}
- /** singleton */
- private CmsUxUtils() {
-
- }
-
public static StringBuilder imgBuilder(String src, String width, String height) {
return new StringBuilder(64).append("<img width='").append(width).append("' height='").append(height)
.append("' src='").append(src).append("'");
public static String img(String src, Cms2DSize size) {
return img(src, Integer.toString(size.width()), Integer.toString(size.height()));
}
+
+ /** singleton */
+ private CmsUxUtils() {
+
+ }
+
}
import org.argeo.api.cms.CmsLog;
import org.argeo.api.uuid.ConcurrentUuidFactory;
+import org.argeo.api.uuid.NodeIdSupplier;
import org.argeo.api.uuid.UuidBinaryUtils;
public class CmsUuidFactory extends ConcurrentUuidFactory {
}
}
InetAddress selectedIp = selectedIpv6 != null ? selectedIpv6 : selectedIpv4;
- if (selectedIp == null)
- throw new IllegalStateException("No IP address found");
+ if (selectedIp == null) {
+ log.warn("No IP address found, using a random node id for UUID generation");
+ return NodeIdSupplier.randomNodeId();
+ }
byte[] digest = sha1(selectedIp.getAddress());
log.info("Use IP " + selectedIp + " hashed as " + UuidBinaryUtils.toHexString(digest) + " as node id");
byte[] nodeId = toNodeIdBytes(digest, 0);
if (log.isDebugEnabled()) {
StringBuilder msg = new StringBuilder();
msg.append("Logged in to CMS: '" + authorization + "' (" + authorization.getName() + ")\n");
- for (Principal principal : subject.getPrincipals()) {
- msg.append(" Principal: " + principal.getName()).append(" (")
- .append(principal.getClass().getSimpleName()).append(")\n");
- }
- for (Object credential : subject.getPublicCredentials()) {
- msg.append(" Public Credential: " + credential).append(" (")
- .append(credential.getClass().getSimpleName()).append(")\n");
+ if (log.isTraceEnabled()) {
+ for (Principal principal : subject.getPrincipals()) {
+ msg.append(" Principal: " + principal.getName()).append(" (")
+ .append(principal.getClass().getSimpleName()).append(")\n");
+ }
+ for (Object credential : subject.getPublicCredentials()) {
+ msg.append(" Public Credential: " + credential).append(" (")
+ .append(credential.getClass().getSimpleName()).append(")\n");
+ }
}
log.debug(msg);
}
private Long availableSince;
+ // time in ms to wait for CMS to be ready
+ private final long readynessTimeout = 30 * 1000;
+
// CMS sessions
private Map<UUID, CmsSessionImpl> cmsSessionsByUuid = new HashMap<>();
private Map<String, CmsSessionImpl> cmsSessionsByLocalId = new HashMap<>();
defaultLocale = locales.get(0);
new Thread(() -> {
- while (!checkReadiness()) {
+ long begin = System.currentTimeMillis();
+ long duration = 0;
+ readyness: while (!checkReadiness()) {
+ duration = System.currentTimeMillis() - begin;
+ if (duration > readynessTimeout) {
+ log.error("## CMS not ready after " + duration + " ms. Giving up checking.");
+ break readyness;
+ }
try {
- Thread.sleep(500);
+ Thread.sleep(100);
} catch (InterruptedException e) {
}
}
CmsAuthenticator authenticator = isPublic ? new PublicCmsAuthenticator() : new CmsAuthenticator();
httpHandlers.put(contextPath, httpHandler);
httpAuthenticators.put(contextPath, authenticator);
- if (httpServer == null) {
+ if (httpServer.join() == null) {
return;
} else {
createHttpContext(contextPath, httpHandler, authenticator);
if (contextPath == null)
return; // ignore silently
httpHandlers.remove(contextPath);
- if (httpServer == null)
+ if (httpServer.join() == null)
return;
httpServer.join().removeContext(contextPath);
log.debug(() -> "Removed handler " + contextPath + " : " + httpHandler.getClass().getName());
}
public boolean allExpectedServicesAvailable() {
- if (httpExpected && httpServer == null)
+ if (httpExpected && !httpServer.isDone())
return false;
- if (sshdExpected && cmsSshd == null)
+ if (sshdExpected && !cmsSshd.isDone())
return false;
return true;
}
if (uri == null)
throw new IllegalArgumentException("URI cannot be null");
try {
+ // FIXME does not work if URI contains illegal characters (such as spaces, etc.)
return new URI(uri);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Badly formatted URI " + uri, e);
public class StreamUtils {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
- /*
- * APACHE COMMONS IO (inspired)
- */
-
/** @return the number of bytes */
- public static Long copy(InputStream in, OutputStream out) throws IOException {
- Long count = 0l;
+ public static long copy(InputStream in, OutputStream out) throws IOException {
+ long count = 0l;
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
while (true) {
int length = in.read(buf);
}
/** @return the number of chars */
- public static Long copy(Reader in, Writer out) throws IOException {
- Long count = 0l;
+ public static long copy(Reader in, Writer out) throws IOException {
+ long count = 0l;
char[] buf = new char[DEFAULT_BUFFER_SIZE];
while (true) {
int length = in.read(buf);
import java.io.IOException;
import java.io.InputStream;
import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
private static RuntimeContext runtimeContext = null;
+ private static List<Runnable> postStart = Collections.synchronizedList(new ArrayList<>());
+
protected Service(String[] args) {
}
OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(config);
osgiRuntimeContext.run();
Service.runtimeContext = osgiRuntimeContext;
+ for (Runnable run : postStart) {
+ try {
+ run.run();
+ } catch (Exception e) {
+ logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
+ }
+ }
Service.runtimeContext.waitForStop(0);
- } catch (NoClassDefFoundError e) {
+ } catch (NoClassDefFoundError noClassDefFoundE) {
StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext((Map<String, String>) config);
staticRuntimeContext.run();
Service.runtimeContext = staticRuntimeContext;
+ for (Runnable run : postStart) {
+ try {
+ run.run();
+ } catch (Exception e) {
+ logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
+ }
+ }
Service.runtimeContext.waitForStop(0);
}
} catch (Exception e) {
public static RuntimeContext getRuntimeContext() {
return runtimeContext;
}
+
+ /** Add a post-start call back to be run after the runtime has been started. */
+ public static void addPostStart(Runnable runnable) {
+ postStart.add(runnable);
+ }
}
}
A2Branch getOrAddBranch(String branchId) {
- if (branches.containsKey(branchId))
- return branches.get(branchId);
- else
- return new A2Branch(this, branchId);
+ if (!branches.containsKey(branchId)) {
+ A2Branch a2Branch = new A2Branch(this, branchId);
+ branches.put(branchId, a2Branch);
+ }
+ return branches.get(branchId);
}
A2Module getOrAddModule(Version version, Object locator) {
if (variantPath == null)
continue contributions;
- if (Files.exists(variantPath)) {
- // a variant was found, let's collect its contributions (also common ones in its
- // parent)
+ // a variant was found, let's collect its contributions (also common ones in its
+ // parent)
+ if (Files.exists(variantPath.getParent())) {
for (Path variantContributionPath : Files.newDirectoryStream(variantPath.getParent())) {
String variantContributionId = variantContributionPath.getFileName().toString();
if (variantContributionId.contains(".")) {
contributions.put(variantContributionPath, contribution);
}
}
+ }
+ if (Files.exists(variantPath)) {
for (Path variantContributionPath : Files.newDirectoryStream(variantPath)) {
String variantContributionId = variantContributionPath.getFileName().toString();
if (variantContributionId.contains(".")) {
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
-/** A running OSGi bundle context seen as a {@link AbstractProvisioningSource}. */
+/**
+ * A running OSGi bundle context seen as a {@link AbstractProvisioningSource}.
+ */
class OsgiContext extends AbstractProvisioningSource {
private final BundleContext bc;
+ private A2Contribution runtimeContribution;
+
public OsgiContext(BundleContext bc) {
super(false);
this.bc = bc;
+ runtimeContribution = getOrAddContribution(A2Contribution.RUNTIME);
}
public OsgiContext() {
}
void load() {
- A2Contribution runtimeContribution = getOrAddContribution( A2Contribution.RUNTIME);
for (Bundle bundle : bc.getBundles()) {
- // OsgiBootUtils.debug(bundle.getDataFile("/"));
- String componentId = bundle.getSymbolicName();
- Version version = bundle.getVersion();
- A2Component component = runtimeContribution.getOrAddComponent(componentId);
- A2Module module = component.getOrAddModule(version, bundle);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module + " (location id: " + bundle.getLocation() + ")");
+ registerBundle(bundle);
}
}
+
+ void registerBundle(Bundle bundle) {
+ String componentId = bundle.getSymbolicName();
+ Version version = bundle.getVersion();
+ A2Component component = runtimeContribution.getOrAddComponent(componentId);
+ A2Module module = component.getOrAddModule(version, bundle);
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Registered bundle module " + module + " (location id: " + bundle.getLocation() + ")");
+
+ }
}
Version moduleVersion = module.getVersion();
A2Branch osgiBranch = osgiContext.findBranch(module.getBranch().getComponent().getId(), moduleVersion);
if (osgiBranch == null) {
-// Bundle bundle = bc.installBundle(module.getBranch().getCoordinates(),
-// moduleSource.newInputStream(module.getLocator()));
Bundle bundle = moduleSource.install(bc, module);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
+ // TODO make it more dynamic, based on OSGi APIs
+ osgiContext.registerBundle(bundle);
+// if (OsgiBootUtils.isDebug())
+// OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
return bundle;
} else {
A2Module lastOsgiModule = osgiBranch.last();
int compare = moduleVersion.compareTo(lastOsgiModule.getVersion());
- if (compare > 0) {// update
+ if (compare >= 0) {// update (also if same version)
Bundle bundle = (Bundle) lastOsgiModule.getLocator();
-// bundle.update(moduleSource.newInputStream(module.getLocator()));
+ if (bundle.getBundleId() == 0)// ignore framework bundle
+ return null;
moduleSource.update(bundle, module);
+ // TODO make it more dynamic, based on OSGi APIs
+ // TODO remove old module? Or keep update history?
+ osgiContext.registerBundle(bundle);
OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
return bundle;
+ } else {
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Did not install as bundle module " + module
+ + " since a module with higher version " + lastOsgiModule.getVersion()
+ + " is already installed for branch " + osgiBranch);
}
}
} catch (Exception e) {
package org.argeo.init.logging;
+import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.text.MessageFormat;
import java.time.Instant;
import java.util.Collections;
*/
final static String PROP_ARGEO_LOGGING_SYNCHRONOUS = "argeo.logging.synchronous";
/**
- * Whether to enable jounrald compatible output, either: auto (default), true,
+ * Whether to enable journald compatible output, either: auto (default), true,
* or false.
*/
final static String PROP_ARGEO_LOGGING_JOURNALD = "argeo.logging.journald";
+ /** A file to which additionally write log entries. */
+ final static String PROP_ARGEO_LOGGING_FILE = "argeo.logging.file";
/**
* The level from which call location (that is, line number in Java code) will
* be searched (default is WARNING)
private final LogEntryPublisher publisher;
private PrintStreamSubscriber synchronousSubscriber;
+ private PrintStream fileOut;
private final boolean journald;
private final Level callLocationLevel;
PrintStreamSubscriber subscriber = new PrintStreamSubscriber();
publisher = new LogEntryPublisher();
publisher.subscribe(subscriber);
+ String logFileStr = System.getProperty(PROP_ARGEO_LOGGING_FILE);
+ if (logFileStr != null) {
+ Path logFilePath = Paths.get(logFileStr);
+ if (!Files.exists(logFilePath.getParent())) {
+ System.err.println("Parent directory of " + logFilePath + " does not exist");
+ } else {
+ try {
+ fileOut = new PrintStream(Files.newOutputStream(logFilePath), true, StandardCharsets.UTF_8);
+ publisher.subscribe(new PrintStreamSubscriber(fileOut, fileOut));
+ } catch (IOException e) {
+ System.err.println("Cannot write log to " + logFilePath);
+ e.printStackTrace();
+ }
+ }
+ }
}
Runtime.getRuntime().addShutdownHook(new Thread(() -> close(), "Log shutdown"));
// silent
}
}
+
+ if (fileOut != null) {
+ try {
+ fileOut.close();
+ } catch (Exception e) {
+ // silent
+ }
+ }
}
private Level computeApplicableLevel(String name) {
for (String bsn : startLevels.get(level))
bundleStartLevels.put(bsn, level);
}
- for (Bundle bundle : bundleContext.getBundles()) {
+
+ // keep only bundles with the highest version
+ Map<String, Bundle> startableBundles = new HashMap<>();
+ bundles: for (Bundle bundle : bundleContext.getBundles()) {
+ if (bundle.getVersion() == null)
+ continue bundles;
+ String bsn = bundle.getSymbolicName();
+ if (!startableBundles.containsKey(bsn)) {
+ startableBundles.put(bsn, bundle);
+ } else {
+ if (bundle.getVersion().compareTo(startableBundles.get(bsn).getVersion()) > 0) {
+ startableBundles.put(bsn, bundle);
+ }
+ }
+ }
+
+ for (Bundle bundle : startableBundles.values()) {
String bsn = bundle.getSymbolicName();
if (bundleStartLevels.containsKey(bsn)) {
BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
}
if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug(bsn + " starts at level " + level);
+ OsgiBootUtils.debug(bsn + " v" + bundle.getVersion() + " starts at level " + level);
}
}
}
Service-Component: \
OSGI-INF/equinoxJettyServer.xml,\
+
+Import-Package:\
+org.eclipse.jetty.session,\
+*
import org.argeo.cms.jetty.CmsJettyServer;
import org.eclipse.equinox.http.servlet.HttpServiceServlet;
-import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.ee8.nested.SessionHandler;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee8.servlet.ServletHolder;
import org.osgi.framework.Constants;
/** A {@link CmsJettyServer} integrating with Equinox HTTP framework. */
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
-import javax.websocket.DeploymentException;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.argeo.api.cms.CmsConstants;
import org.argeo.cms.CmsDeployProperty;
import org.argeo.cms.util.LangUtils;
import org.argeo.cms.websocket.server.CmsWebSocketConfigurator;
-import org.argeo.cms.websocket.server.TestEndpoint;
import org.eclipse.equinox.http.jetty.JettyConfigurator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
public class JettyConfig {
private final static CmsLog log = CmsLog.getLog(JettyConfig.class);
startServer(properties);
});
- ServiceTracker<ServerContainer, ServerContainer> serverSt = new ServiceTracker<ServerContainer, ServerContainer>(
- bc, ServerContainer.class, null) {
-
- @Override
- public ServerContainer addingService(ServiceReference<ServerContainer> reference) {
- ServerContainer serverContainer = super.addingService(reference);
-
- BundleContext bc = reference.getBundle().getBundleContext();
- ServiceReference<ServerEndpointConfig.Configurator> srConfigurator = bc
- .getServiceReference(ServerEndpointConfig.Configurator.class);
- ServerEndpointConfig.Configurator endpointConfigurator = bc.getService(srConfigurator);
- ServerEndpointConfig config = ServerEndpointConfig.Builder
- .create(TestEndpoint.class, "/ws/test/events/").configurator(endpointConfigurator).build();
- try {
- serverContainer.addEndpoint(config);
- } catch (DeploymentException e) {
- throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e);
- }
- return serverContainer;
- }
-
- };
- serverSt.open();
+// ServiceTracker<ServerContainer, ServerContainer> serverSt = new ServiceTracker<ServerContainer, ServerContainer>(
+// bc, ServerContainer.class, null) {
+//
+// @Override
+// public ServerContainer addingService(ServiceReference<ServerContainer> reference) {
+// ServerContainer serverContainer = super.addingService(reference);
+//
+// BundleContext bc = reference.getBundle().getBundleContext();
+// ServiceReference<ServerEndpointConfig.Configurator> srConfigurator = bc
+// .getServiceReference(ServerEndpointConfig.Configurator.class);
+// ServerEndpointConfig.Configurator endpointConfigurator = bc.getService(srConfigurator);
+// ServerEndpointConfig config = ServerEndpointConfig.Builder
+// .create(TestEndpoint.class, "/ws/test/events/").configurator(endpointConfigurator).build();
+// try {
+// serverContainer.addEndpoint(config);
+// } catch (DeploymentException e) {
+// throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e);
+// }
+// return serverContainer;
+// }
+//
+// };
+// serverSt.open();
// check initialisation
// ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee8.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
+import org.eclipse.jetty.ee8.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.eclipse.jetty.ee8.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
-Subproject commit f2ebcc21eecda1ee5db65700cf16f4833a7190b0
+Subproject commit cabcc3462226b71849ca42301c21e05b63f150c2
major=2
minor=3
-micro=23
+micro=27
qualifier=
Bundle-Copyright= \
argeo.http.port=7070
#argeo.http.host=[IP address to listen to]
#argeo.https.port=7073
-#argeo.sshd.port=2222
+argeo.sshd.port=2222
# Logging
log.org.argeo=DEBUG
# DON'T CHANGE BELOW
org.eclipse.equinox.http.jetty.autostart=false
org.osgi.framework.system.packages.extra=\
+sun.security.util,\
sun.security.internal.spec,\
sun.security.provider,\
com.sun.net.httpserver,\
import java.util.HashMap;
import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public abstract class AbstractSwtCmsView implements CmsView {
private final static CmsLog log = CmsLog.getLog(AbstractSwtCmsView.class);
+ /** A timer to be used to perform background UX tasks. */
+ private final static Timer uxTimer = new Timer(true);
+
+ static {
+ // purge every day
+ uxTimer.schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+ uxTimer.purge();
+ }
+ }, 0, 24 * 60 * 60 * 1000);
+ }
+
protected final String uiName;
protected LoginContext loginContext;
}
}
+ @Override
+ public TimerTask schedule(Runnable task, long delay) {
+ TimerTask timerTask = newSwtUxTimerTask(task);
+ uxTimer.schedule(timerTask, delay);
+ return timerTask;
+ }
+
+ @Override
+ public TimerTask schedule(Runnable task, long delay, long period) {
+ TimerTask timerTask = newSwtUxTimerTask(task);
+ uxTimer.schedule(timerTask, delay, period);
+ return timerTask;
+ }
+
+ protected TimerTask newSwtUxTimerTask(Runnable todo) {
+ return new TimerTask() {
+
+ @Override
+ public void run() {
+ synchronized (display) {
+ try {
+ if (!display.isDisposed()) {
+ display.syncExec(() -> {
+ todo.run();
+ });
+ }
+ } catch (Exception e) {
+ log.error("Cannot run UX timer task", e);
+ }
+ }
+ }
+ };
+ }
}
private static final long serialVersionUID = -107939076610406448L;
+ /** Last time the UI was accessed. */
+ private long lastAccess = System.currentTimeMillis();
+// private TimerTask timeoutTask;
+ private long uiTimeout = 0;
+
private CmsView cmsView;
public CmsSwtUi(Composite parent, int style) {
super(parent, style);
cmsView = CmsSwtUtils.getCmsView(parent);
-
setLayout(new GridLayout());
}
+ @Override
public CmsView getCmsView() {
return cmsView;
}
+ @Override
+ public void updateLastAccess() {
+ this.lastAccess = System.currentTimeMillis();
+ }
+
+ public void setUiTimeout(long uiTimeout) {
+// clearTimeoutTask();
+ this.uiTimeout = uiTimeout;
+ if (this.uiTimeout <= 0)
+ return;
+ // TODO introduce mechanism to check whether the UI is "zombie"
+ // (that is the UI thread still exists, but cannot execute anything)
+// final long timeoutTaskPeriod = 60 * 60 * 1000;// 1h
+// timeoutTask = cmsView.schedule(() -> {
+// disposeIfTimedout();
+// }, timeoutTaskPeriod, timeoutTaskPeriod);
+// addDisposeListener((e) -> {
+// clearTimeoutTask();
+// });
+ }
+
+// /** Must be run in UI thread. */
+// public void disposeIfTimedout() {
+// System.out.println("Enter disposeIfTimedout");
+// if (isDisposed()) {
+// clearTimeoutTask();
+// return;
+// }
+// if (isTimedOut()) {
+// dispose();
+// clearTimeoutTask();
+// System.out.println("Disposed after timeout");
+// }
+// }
+
+// private void clearTimeoutTask() {
+// if (timeoutTask != null) {
+// timeoutTask.cancel();
+// timeoutTask = null;
+// }
+// }
+
+ @Override
+ public boolean isTimedOut() {
+ return uiTimeout > 0 && (System.currentTimeMillis() - lastAccess >= uiTimeout);
+ }
+
+// class DisposeIfTimedOutTask implements Runnable {
+// public void run() {
+// disposeIfTimedout();
+// getDisplay().timerExec(1000, new DisposeIfTimedOutTask());
+// }
+//
+// }
+
}
\ No newline at end of file
+++ /dev/null
-package org.argeo.cms.swt.osgi;
-
-import java.awt.Color;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.batik.transcoder.TranscoderException;
-import org.apache.batik.transcoder.TranscoderInput;
-import org.apache.batik.transcoder.TranscoderOutput;
-import org.apache.batik.transcoder.image.ImageTranscoder;
-import org.apache.batik.transcoder.image.PNGTranscoder;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.framework.BundleContext;
-
-/** Theme which can dynamically create icons from SVG data. */
-public class BundleSvgTheme extends BundleCmsSwtTheme {
- private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName());
-
- private Map<String, Map<Integer, ImageData>> imageDataCache = Collections.synchronizedMap(new HashMap<>());
-
- private Map<Integer, ImageTranscoder> transcoders = Collections.synchronizedMap(new HashMap<>());
-
- private final static String IMAGE_CACHE_KEY = BundleSvgTheme.class.getName() + ".imageCache";
-
- @Override
- public Image getIcon(String name, Integer preferredSize) {
- String path = "icons/types/svg/" + name + ".svg";
- return createImageFromSvg(path, preferredSize);
- }
-
- @SuppressWarnings("unchecked")
- protected Image createImageFromSvg(String path, Integer preferredSize) {
- Display display = Display.getCurrent();
- Objects.requireNonNull(display, "Not a user interface thread");
-
- Map<String, Map<Integer, Image>> imageCache = (Map<String, Map<Integer, Image>>) display
- .getData(IMAGE_CACHE_KEY);
- if (imageCache == null)
- display.setData(IMAGE_CACHE_KEY, new HashMap<String, Map<Integer, Image>>());
- imageCache = (Map<String, Map<Integer, Image>>) display.getData(IMAGE_CACHE_KEY);
-
- Image image = null;
- if (imageCache.containsKey(path)) {
- image = imageCache.get(path).get(preferredSize);
- }
- if (image != null)
- return image;
- ImageData imageData = loadFromSvg(path, preferredSize);
- image = new Image(display, imageData);
- if (!imageCache.containsKey(path))
- imageCache.put(path, Collections.synchronizedMap(new HashMap<>()));
- imageCache.get(path).put(preferredSize, image);
- return image;
- }
-
- protected ImageData loadFromSvg(String path, int size) {
- ImageData imageData = null;
- if (imageDataCache.containsKey(path))
- imageData = imageDataCache.get(path).get(size);
- if (imageData != null)
- return imageData;
-
- ImageTranscoder transcoder = null;
- synchronized (this) {
- transcoder = transcoders.get(size);
- if (transcoder == null) {
- transcoder = new PNGTranscoder();
- transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) size);
- transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) size);
- transcoder.addTranscodingHint(PNGTranscoder.KEY_BACKGROUND_COLOR, new Color(255, 255, 255, 0));
- transcoders.put(size, transcoder);
- }
- }
- try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
- if (in == null)
- throw new IllegalArgumentException(path + " not found");
- TranscoderInput input = new TranscoderInput(in);
- TranscoderOutput output = new TranscoderOutput(out);
- transcoder.transcode(input, output);
- try (InputStream imageIn = new ByteArrayInputStream(out.toByteArray())) {
- imageData = new ImageData(imageIn);
- }
- logger.log(Level.DEBUG, () -> "Generated " + size + "x" + size + " PNG icon from " + path);
- } catch (IOException | TranscoderException e) {
- throw new RuntimeException("Cannot transcode SVG " + path, e);
- }
-
- // cache it
- if (!imageDataCache.containsKey(path))
- imageDataCache.put(path, Collections.synchronizedMap(new HashMap<>()));
- imageDataCache.get(path).put(size, imageData);
-
- return imageData;
- }
-
- @Override
- public void init(BundleContext bundleContext, Map<String, String> properties) {
- super.init(bundleContext, properties);
-
- // preload all icons
-// paths: for (String p : getImagesPaths()) {
-// if (!p.endsWith(".svg"))
-// continue paths;
-// createImageFromSvg(p, getDefaultIconSize());
-// }
- }
-
-// @Override
-// public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-// Display display = Display.getDefault();
-// if (display != null)
-// for (String path : imageCache.keySet()) {
-// for (Image image : imageCache.get(path).values()) {
-// display.syncExec(() -> image.dispose());
-// }
-// }
-// super.destroy(bundleContext, properties);
-// }
-
-}
continue eventLoop;
}
}
+ if (serverPushSession != null)
+ serverPushSession.stop();
if (!display.isDisposed())
display.dispose();
}
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionBindingListener;
import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.UISession;
import org.eclipse.swt.widgets.Display;
/** Singleton class providing single sources infos about the UI context. */
public class UiContext {
/** Can be null, thus indicating that we are not in a web context. */
+ @Deprecated
public static HttpServletRequest getHttpRequest() {
return RWT.getRequest();
}
+ @Deprecated
public static HttpServletResponse getHttpResponse() {
return RWT.getResponse();
}
display.setData(key, value);
}
+ public static void killDisplay(Display display) {
+ UISession uiSession = RWT.getUISession(display);
+ ((HttpSessionBindingListener) uiSession).valueUnbound(null);
+ }
+
private static Display getDisplay() {
return Display.getCurrent();
}
/** Singleton class providing single sources infos about the UI context. */
public class UiContext {
+ @Deprecated
public static HttpServletRequest getHttpRequest() {
return null;
}
+ @Deprecated
public static HttpServletResponse getHttpResponse() {
return null;
}
display.setData(key, value);
}
+ public static void killDisplay(Display display) {
+ display.dispose();
+ }
+
private static Display getDisplay() {
return Display.getCurrent();
}