[kune-commits] r1175 - in trunk/src/main: java/cc/kune/core/client/sitebar/logo java/org/ourproject/kune/app/server/wave resources

Vicente J. Ruiz Jurado vjrj_ at ourproject.org
Wed Dec 29 04:37:13 CET 2010


Author: vjrj_
Date: 2010-12-29 04:37:12 +0100 (Wed, 29 Dec 2010)
New Revision: 1175

Added:
   trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoUiHandlers.java
   trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoViewImpl.ui.xml
   trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomAttachmentServlet.java
   trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomFetchServlet.java
   trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomServerModule.java
   trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSessionManagerImpl.java
   trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSettingsBinder.java
   trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomUserRegistrationServlet.java
   trunk/src/main/resources/wave-server.properties
Log:
NEW - # 10: Wave server code update 
http://kune.ourproject.org/issues/ticket/10
NEW - # 9: Roo partial use/integration 
http://kune.ourproject.org/issues/ticket/9

Added: trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoUiHandlers.java
===================================================================
--- trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoUiHandlers.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoUiHandlers.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,7 @@
+package cc.kune.core.client.sitebar.logo;
+
+import com.gwtplatform.mvp.client.UiHandlers;
+
+public interface SiteLogoUiHandlers extends UiHandlers {
+    void onClick();
+}
\ No newline at end of file

Added: trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoViewImpl.ui.xml
===================================================================
--- trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoViewImpl.ui.xml	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/client/sitebar/logo/SiteLogoViewImpl.ui.xml	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,10 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:g="urn:import:com.google.gwt.user.client.ui">
+  <ui:style>
+    .important {
+    	font-weight: bold;
+    }
+  </ui:style>
+    <g:Image ui:field="logo" addStyleNames="k-floatright k-pointer" />
+</ui:UiBinder>
\ No newline at end of file

Added: trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomAttachmentServlet.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomAttachmentServlet.java	                        (rev 0)
+++ trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomAttachmentServlet.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed 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.ourproject.kune.app.server.wave;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.waveprotocol.box.server.persistence.AttachmentStore;
+import org.waveprotocol.box.server.persistence.AttachmentStore.AttachmentData;
+import org.waveprotocol.wave.util.logging.Log;
+
+import com.google.inject.Inject;
+
+/**
+ * An attachment servlet is a simple servlet that serves up attachments from a
+ * provided store.
+ */
+public class CustomAttachmentServlet extends HttpServlet {
+    private static final Log LOG = Log.get(CustomAttachmentServlet.class);
+
+    private final AttachmentStore store;
+
+    @Inject
+    public CustomAttachmentServlet(AttachmentStore store) {
+        this.store = store;
+    }
+
+    /**
+     * Get the attachment id from the URL in the request.
+     * 
+     * @param request
+     * @return the id of the referenced attachment.
+     */
+    private static String getAttachmentIdFromRequest(HttpServletRequest request) {
+        if (request.getPathInfo().length() == 0) {
+            return "";
+        }
+
+        // Discard the leading '/' in the pathinfo. Whats left will be the
+        // attachment id.
+        return request.getPathInfo().substring(1);
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        // TODO: Authenticate that the user has permission to access the
+        // attachment.
+
+        String attachmentId = getAttachmentIdFromRequest(request);
+        if (attachmentId.length() == 0) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+
+        AttachmentData data = store.getAttachment(attachmentId);
+
+        if (data == null) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+
+        response.setContentType("text/html");
+        response.setStatus(HttpServletResponse.SC_OK);
+        response.setContentLength((int) data.getContentSize());
+        response.setDateHeader("Last-Modified", data.getLastModifiedDate().getTime());
+        data.writeDataTo(response.getOutputStream());
+
+        LOG.info("Fetched attachment with id '" + attachmentId + "'");
+    }
+
+    @Override
+    protected void doPut(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
+        // TODO: Authenticate the attachment data
+
+        String attachmentId = getAttachmentIdFromRequest(request);
+        if (attachmentId.length() == 0) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+
+        store.storeAttachment(attachmentId, request.getInputStream());
+
+        response.setContentType("text/html");
+        response.setStatus(HttpServletResponse.SC_OK);
+        response.getWriter().write("<html><body><h1>Data written</h1></body></html>");
+
+        LOG.info("Added attachment with id '" + attachmentId + "'");
+    }
+}

Added: trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomFetchServlet.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomFetchServlet.java	                        (rev 0)
+++ trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomFetchServlet.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,186 @@
+/**
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed 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.ourproject.kune.app.server.wave;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.waveprotocol.box.common.comms.WaveClientRpc.DocumentSnapshot;
+import org.waveprotocol.box.common.comms.WaveClientRpc.WaveViewSnapshot;
+import org.waveprotocol.box.common.comms.WaveClientRpc.WaveletSnapshot;
+import org.waveprotocol.box.server.authentication.SessionManager;
+import org.waveprotocol.box.server.frontend.WaveletSnapshotAndVersion;
+import org.waveprotocol.box.server.rpc.ProtoSerializer;
+import org.waveprotocol.box.server.waveserver.WaveletProvider;
+import org.waveprotocol.box.server.waveserver.WaveletStateException;
+import org.waveprotocol.wave.model.id.WaveletId;
+import org.waveprotocol.wave.model.id.WaveletName;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.waveref.InvalidWaveRefException;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+import org.waveprotocol.wave.util.escapers.jvm.JavaWaverefEncoder;
+import org.waveprotocol.wave.util.logging.Log;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.Inject;
+import com.google.protobuf.MessageLite;
+
+/**
+ * A servlet for static fetching of wave data. Typically, the servlet will be
+ * hosted on /fetch/*. A document, a wavelet, or a whole wave can be specified
+ * in the URL.
+ * 
+ * Valid request formats are: Fetch a wave: GET /fetch/wavedomain.com/waveid
+ * Fetch a wavelet: GET /fetch/wavedomain.com/waveid/waveletdomain.com/waveletid
+ * Fetch a document: GET
+ * /fetch/wavedomain.com/waveid/waveletdomain.com/waveletid/b+abc123
+ * 
+ * The format of the returned information is the protobuf-JSON format used by
+ * the websocket interface.
+ */
+public class CustomFetchServlet extends HttpServlet {
+    private static final Log LOG = Log.get(CustomFetchServlet.class);
+
+    @Inject
+    public CustomFetchServlet(WaveletProvider waveletProvider, ProtoSerializer serializer, SessionManager sessionManager) {
+        this.waveletProvider = waveletProvider;
+        this.serializer = serializer;
+        this.sessionManager = sessionManager;
+    }
+
+    private final ProtoSerializer serializer;
+    private final WaveletProvider waveletProvider;
+    private final SessionManager sessionManager;
+
+    /**
+     * Create an http response to the fetch query. Main entrypoint for this
+     * class.
+     */
+    @Override
+    @VisibleForTesting
+    protected void doGet(HttpServletRequest req, HttpServletResponse response) throws IOException {
+        ParticipantId user = sessionManager.getLoggedInUser(req.getSession(false));
+
+        // This path will look like "/example.com/w+abc123/foo.com/conv+root
+        // Strip off the leading '/'.
+        String urlPath = req.getPathInfo().substring(1);
+
+        // Extract the name of the wavelet from the URL
+        WaveRef waveref;
+        try {
+            waveref = JavaWaverefEncoder.decodeWaveRefFromPath(urlPath);
+        } catch (InvalidWaveRefException e) {
+            // The URL contains an invalid waveref. There's no document at this
+            // path.
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+
+        renderSnapshot(waveref, user, response);
+    }
+
+    private void serializeObjectToServlet(MessageLite message, HttpServletResponse dest) throws IOException {
+        if (message == null) {
+            // Snapshot is null. It would be nice to 404 here, but we can't let
+            // clients guess valid wavelet ids that they're not authorized to
+            // access.
+            dest.sendError(HttpServletResponse.SC_FORBIDDEN);
+        } else {
+            dest.setStatus(HttpServletResponse.SC_OK);
+            dest.setContentType("application/json");
+
+            // This is a hack to make sure the fetched data is fresh.
+            // TODO(josephg): Change this so that browsers can cache wave
+            // snapshots. Probably need:
+            // 'Cache-Control: must-revalidate, private' and an ETag with the
+            // wave[let]'s version.
+            dest.setHeader("Cache-Control", "no-store");
+
+            serializer.writeTo(dest.getWriter(), message);
+        }
+    }
+
+    /**
+     * Render the requested waveref out to the HttpServletResponse dest.
+     * 
+     * @param waveref
+     *            The referenced wave. Could be a whole wave, a wavelet or just
+     *            a document.
+     * @param dest
+     *            The servlet response to render the snapshot out to.
+     * @throws IOException
+     */
+    private void renderSnapshot(WaveRef waveref, ParticipantId requester, HttpServletResponse dest) throws IOException {
+        // TODO(josephg): Its currently impossible to fetch all wavelets inside
+        // a
+        // wave that are visible to the user. Until this is fixed, if no wavelet
+        // is
+        // specified we'll just return the conv+root.
+        WaveletId waveletId = waveref.hasWaveletId() ? waveref.getWaveletId() : WaveletId.of(
+                waveref.getWaveId().getDomain(), "conv+root");
+
+        WaveletName waveletName = WaveletName.of(waveref.getWaveId(), waveletId);
+
+        try {
+            if (!waveletProvider.checkAccessPermission(waveletName, requester)) {
+                dest.sendError(HttpServletResponse.SC_FORBIDDEN);
+                return;
+            }
+        } catch (WaveletStateException e) {
+            throw new IOException(e);
+        }
+
+        LOG.info("Fetching snapshot of wavelet " + waveletName);
+        WaveletSnapshotAndVersion snapshotAndV = waveletProvider.getSnapshot(waveletName);
+
+        if (snapshotAndV != null) {
+            WaveletSnapshot snapshot = snapshotAndV.snapshot;
+            if (waveref.hasDocumentId()) {
+                // We have a wavelet id and document id. Find the document in
+                // the
+                // snapshot
+                // and return it.
+                DocumentSnapshot docSnapshot = null;
+                for (DocumentSnapshot ds : snapshot.getDocumentList()) {
+                    if (ds.getDocumentId().equals(waveref.getDocumentId())) {
+                        docSnapshot = ds;
+                        break;
+                    }
+                }
+                serializeObjectToServlet(docSnapshot, dest);
+            } else if (waveref.hasWaveletId()) {
+                // We have a wavelet id. Pull up the wavelet snapshot and return
+                // it.
+                serializeObjectToServlet(snapshot, dest);
+            } else {
+                // Wrap the conv+root we fetched earlier in a WaveSnapshot
+                // object and
+                // send it.
+                WaveViewSnapshot waveSnapshot = WaveViewSnapshot.newBuilder().setWaveId(waveref.getWaveId().serialise()).addWavelet(
+                        snapshot).build();
+
+                serializeObjectToServlet(waveSnapshot, dest);
+            }
+        } else {
+            dest.sendError(HttpServletResponse.SC_FORBIDDEN);
+        }
+    }
+}

Added: trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomServerModule.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomServerModule.java	                        (rev 0)
+++ trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomServerModule.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,119 @@
+package org.ourproject.kune.app.server.wave;
+
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed 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.
+ *
+ */
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.security.auth.login.Configuration;
+
+import org.eclipse.jetty.server.session.HashSessionManager;
+import org.waveprotocol.box.server.CoreSettings;
+import org.waveprotocol.box.server.authentication.SessionManager;
+import org.waveprotocol.box.server.rpc.ProtoSerializer;
+import org.waveprotocol.box.server.rpc.ServerRpcProvider;
+import org.waveprotocol.box.server.waveserver.WaveServerImpl;
+import org.waveprotocol.box.server.waveserver.WaveServerModule;
+import org.waveprotocol.wave.federation.FederationHostBridge;
+import org.waveprotocol.wave.federation.FederationRemoteBridge;
+import org.waveprotocol.wave.federation.WaveletFederationListener;
+import org.waveprotocol.wave.federation.WaveletFederationProvider;
+import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.id.IdGeneratorImpl;
+import org.waveprotocol.wave.model.id.IdGeneratorImpl.Seed;
+import org.waveprotocol.wave.model.id.TokenGenerator;
+import org.waveprotocol.wave.model.id.TokenGeneratorImpl;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+/**
+ * Guice Module for the prototype Server.
+ * 
+ * 
+ */
+public class CustomServerModule extends AbstractModule {
+    private final boolean enableFederation;
+
+    public CustomServerModule(boolean enableFederation) {
+        this.enableFederation = enableFederation;
+    }
+
+    @Override
+    protected void configure() {
+        // Receive updates from the outside world, and push them into our local
+        // Wave
+        // Server.
+        bind(WaveletFederationListener.Factory.class).annotatedWith(FederationRemoteBridge.class).to(
+                WaveServerImpl.class);
+
+        // Provide history and respond to submits about our own local waves.
+        bind(WaveletFederationProvider.class).annotatedWith(FederationHostBridge.class).to(WaveServerImpl.class);
+
+        install(new WaveServerModule(enableFederation));
+        TypeLiteral<List<String>> certs = new TypeLiteral<List<String>>() {
+        };
+        bind(certs).annotatedWith(Names.named("certs")).toInstance(Arrays.<String> asList());
+
+        bind(ProtoSerializer.class).in(Singleton.class);
+
+        bind(Configuration.class).toInstance(Configuration.getConfiguration());
+        bind(SessionManager.class).to(CustomSessionManagerImpl.class).in(Singleton.class);
+
+        bind(org.eclipse.jetty.server.SessionManager.class).to(HashSessionManager.class).in(Singleton.class);
+
+        bind(ServerRpcProvider.class).in(Singleton.class);
+    }
+
+    @Provides
+    @Singleton
+    @Inject
+    public IdGenerator provideIdGenerator(@Named(CoreSettings.WAVE_SERVER_DOMAIN) String domain, Seed seed) {
+        return new IdGeneratorImpl(domain, seed);
+    }
+
+    @Provides
+    @Singleton
+    public SecureRandom provideSecureRandom() {
+        return new SecureRandom();
+    }
+
+    @Provides
+    @Singleton
+    @Inject
+    public TokenGenerator provideTokenGenerator(SecureRandom random) {
+        return new TokenGeneratorImpl(random);
+    }
+
+    @Provides
+    @Singleton
+    @Inject
+    public Seed provideSeed(final SecureRandom random) {
+        return new Seed() {
+            @Override
+            public String get() {
+                return Long.toString(Math.abs(random.nextLong()), 36);
+            }
+        };
+    }
+}
\ No newline at end of file

Added: trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSessionManagerImpl.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSessionManagerImpl.java	                        (rev 0)
+++ trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSessionManagerImpl.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,116 @@
+/**
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed 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.ourproject.kune.app.server.wave;
+
+import javax.servlet.http.HttpSession;
+
+import org.waveprotocol.box.server.account.AccountData;
+import org.waveprotocol.box.server.authentication.SessionManager;
+import org.waveprotocol.box.server.authentication.SessionManagerImpl;
+import org.waveprotocol.box.server.persistence.AccountStore;
+import org.waveprotocol.box.server.persistence.PersistenceException;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.util.escapers.PercentEscaper;
+import org.waveprotocol.wave.util.logging.Log;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.inject.Inject;
+
+/**
+ * Utility class for managing the session's authentication status.
+ * 
+ * @author josephg at gmail.com (Joseph Gentle)
+ */
+
+public class CustomSessionManagerImpl implements SessionManager {
+    private static final String USER_FIELD = "user";
+
+    private final AccountStore accountStore;
+    private final org.eclipse.jetty.server.SessionManager jettySessionManager;
+
+    private static final Log LOG = Log.get(SessionManagerImpl.class);
+
+    @Inject
+    public CustomSessionManagerImpl(AccountStore accountStore,
+            org.eclipse.jetty.server.SessionManager jettySessionManager) {
+        Preconditions.checkNotNull(accountStore, "Null account store");
+        Preconditions.checkNotNull(jettySessionManager, "Null jetty session manager");
+        this.accountStore = accountStore;
+        this.jettySessionManager = jettySessionManager;
+    }
+
+    @Override
+    public ParticipantId getLoggedInUser(HttpSession session) {
+        if (session != null) {
+            return (ParticipantId) session.getAttribute(USER_FIELD);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public AccountData getLoggedInAccount(HttpSession session) {
+        // Consider caching the account data in the session object.
+        ParticipantId user = getLoggedInUser(session);
+        if (user != null) {
+            try {
+                return accountStore.getAccount(user);
+            } catch (PersistenceException e) {
+                LOG.warning("Failed to retrieve account data for " + user, e);
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setLoggedInUser(HttpSession session, ParticipantId id) {
+        Preconditions.checkNotNull(session, "Session is null");
+        Preconditions.checkNotNull(id, "Participant id is null");
+        session.setAttribute(USER_FIELD, id);
+    }
+
+    @Override
+    public void logout(HttpSession session) {
+        if (session != null) {
+            // This function should also remove any other bound fields in the
+            // session
+            // object.
+            session.removeAttribute(USER_FIELD);
+        }
+    }
+
+    @Override
+    public String getLoginUrl(String redirect) {
+        if (Strings.isNullOrEmpty(redirect)) {
+            return SIGN_IN_URL;
+        } else {
+            PercentEscaper escaper = new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER, false);
+            String queryStr = "?r=" + escaper.escape(redirect);
+            return SIGN_IN_URL + queryStr;
+        }
+    }
+
+    @Override
+    public HttpSession getSessionFromToken(String token) {
+        Preconditions.checkNotNull(token);
+        return jettySessionManager.getHttpSession(token);
+    }
+}

Added: trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSettingsBinder.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSettingsBinder.java	                        (rev 0)
+++ trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomSettingsBinder.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed 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.ourproject.kune.app.server.wave;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.SystemConfiguration;
+import org.waveprotocol.wave.util.settings.Setting;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.AbstractModule;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+/**
+ * A property file parsing system that converts a given settings class into a
+ * Guice module with injectable
+ * 
+ * @Named parameters.
+ * 
+ *        Originally based on some CLI work by arb at google.com (Anthony Baxter).
+ *        Refactored by tad.glines at gmail.com (Tad Glines) to use Commons
+ *        Configuration and add support for List<String>.
+ */
+public class CustomSettingsBinder {
+
+    /**
+     * Used to validate that a type is supported. Some types may have generic
+     * parameters that need to be checked.
+     */
+    private interface SettingTypeValidator {
+        boolean check(Type type);
+    }
+
+    private static final Map<Type, SettingTypeValidator> supportedSettingTypes;
+
+    /**
+     * This default validator just returns true.
+     */
+    private static final SettingTypeValidator DEFAULT_TYPE_VALIDATOR = new SettingTypeValidator() {
+        @Override
+        public boolean check(Type type) {
+            return true;
+        }
+    };
+
+    /**
+     * This validator checks to make sure the {@link List}'s generic parameter
+     * is also supported.
+     */
+    private static final SettingTypeValidator LIST_TYPE_VALIDATOR = new SettingTypeValidator() {
+        @Override
+        public boolean check(Type type) {
+            if (type instanceof ParameterizedType) {
+                Type[] args = ((ParameterizedType) type).getActualTypeArguments();
+                if (args.length == 1) {
+                    // At the moment only List<String> is supported.
+                    if (args[0] == String.class) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    };
+
+    static {
+        ImmutableMap.Builder<Type, SettingTypeValidator> builder = ImmutableMap.builder();
+        builder.put(int.class, DEFAULT_TYPE_VALIDATOR);
+        builder.put(boolean.class, DEFAULT_TYPE_VALIDATOR);
+        builder.put(String.class, DEFAULT_TYPE_VALIDATOR);
+        builder.put(List.class, LIST_TYPE_VALIDATOR);
+        supportedSettingTypes = builder.build();
+    }
+
+    /**
+     * Bind configuration parameters into Guice Module.
+     * 
+     * @return a Guice module configured with setting support.
+     * @throws ConfigurationException
+     *             on configuration error
+     */
+    public static Module bindSettings(String propertyFile, Class<?>... settingsArg) throws ConfigurationException {
+        final CompositeConfiguration config = new CompositeConfiguration();
+        config.addConfiguration(new SystemConfiguration());
+        config.addConfiguration(new PropertiesConfiguration(propertyFile));
+
+        List<Field> fields = new ArrayList<Field>();
+        for (Class<?> settings : settingsArg) {
+            fields.addAll(Arrays.asList(settings.getDeclaredFields()));
+        }
+
+        // Reflect on settings class and absorb settings
+        final Map<Setting, Field> settings = new LinkedHashMap<Setting, Field>();
+        for (Field field : fields) {
+            if (!field.isAnnotationPresent(Setting.class)) {
+                continue;
+            }
+
+            // Validate target type
+            SettingTypeValidator typeHelper = supportedSettingTypes.get(field.getType());
+            if (typeHelper == null || !typeHelper.check(field.getGenericType())) {
+                throw new IllegalArgumentException(field.getType() + " is not one of the supported setting types");
+            }
+
+            Setting setting = field.getAnnotation(Setting.class);
+            settings.put(setting, field);
+        }
+
+        // Now validate them
+        List<String> missingProperties = new ArrayList<String>();
+        for (Setting setting : settings.keySet()) {
+            if (setting.defaultValue().isEmpty()) {
+                if (!config.containsKey(setting.name())) {
+                    missingProperties.add(setting.name());
+                }
+            }
+        }
+        if (missingProperties.size() > 0) {
+            StringBuilder error = new StringBuilder();
+            error.append("The following required properties are missing from the server configuration: ");
+            error.append(Joiner.on(", ").join(missingProperties));
+            throw new ConfigurationException(error.toString());
+        }
+
+        // bundle everything up in an injectable guice module
+        return new AbstractModule() {
+
+            @Override
+            protected void configure() {
+                // We must iterate the settings a third time when binding.
+                // Note: do not collapse these loops as that will damage
+                // early error detection. The runtime is still O(n) in setting
+                // count.
+                for (Map.Entry<Setting, Field> entry : settings.entrySet()) {
+                    Class<?> type = entry.getValue().getType();
+                    Setting setting = entry.getKey();
+
+                    if (int.class.equals(type)) {
+                        Integer defaultValue = null;
+                        if (!setting.defaultValue().isEmpty()) {
+                            defaultValue = Integer.parseInt(setting.defaultValue());
+                        }
+                        bindConstant().annotatedWith(Names.named(setting.name())).to(
+                                config.getInteger(setting.name(), defaultValue));
+                    } else if (boolean.class.equals(type)) {
+                        Boolean defaultValue = null;
+                        if (!setting.defaultValue().isEmpty()) {
+                            defaultValue = Boolean.parseBoolean(setting.defaultValue());
+                        }
+                        bindConstant().annotatedWith(Names.named(setting.name())).to(
+                                config.getBoolean(setting.name(), defaultValue));
+                    } else if (String.class.equals(type)) {
+                        bindConstant().annotatedWith(Names.named(setting.name())).to(
+                                config.getString(setting.name(), setting.defaultValue()));
+                    } else {
+                        String[] value = config.getStringArray(setting.name());
+                        if (value.length == 0 && !setting.defaultValue().isEmpty()) {
+                            value = setting.defaultValue().split(",");
+                        }
+                        bind(new TypeLiteral<List<String>>() {
+                        }).annotatedWith(Names.named(setting.name())).toInstance(ImmutableList.copyOf(value));
+                    }
+                }
+            }
+        };
+    }
+}

Added: trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomUserRegistrationServlet.java
===================================================================
--- trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomUserRegistrationServlet.java	                        (rev 0)
+++ trunk/src/main/java/org/ourproject/kune/app/server/wave/CustomUserRegistrationServlet.java	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,147 @@
+/**
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed 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.ourproject.kune.app.server.wave;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.waveprotocol.box.server.CoreSettings;
+import org.waveprotocol.box.server.account.HumanAccountDataImpl;
+import org.waveprotocol.box.server.authentication.HttpRequestBasedCallbackHandler;
+import org.waveprotocol.box.server.authentication.PasswordDigest;
+import org.waveprotocol.box.server.gxp.UserRegistrationPage;
+import org.waveprotocol.box.server.persistence.AccountStore;
+import org.waveprotocol.box.server.persistence.PersistenceException;
+import org.waveprotocol.box.server.rpc.AuthenticationServlet;
+import org.waveprotocol.wave.model.wave.InvalidParticipantAddress;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.util.logging.Log;
+
+import com.google.gxp.base.GxpContext;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+/**
+ * The user registration servlet allows new users to register accounts.
+ * 
+ * @author josephg at gmail.com (Joseph Gentle)
+ */
+public class CustomUserRegistrationServlet extends HttpServlet {
+    private final AccountStore accountStore;
+    private final String domain;
+
+    private final Log LOG = Log.get(CustomUserRegistrationServlet.class);
+
+    @Inject
+    public CustomUserRegistrationServlet(AccountStore accountStore,
+            @Named(CoreSettings.WAVE_SERVER_DOMAIN) String domain) {
+        this.accountStore = accountStore;
+        this.domain = domain;
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        writeRegistrationPage("", AuthenticationServlet.RESPONSE_STATUS_NONE, req.getLocale(), resp);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        String message = tryCreateUser(req.getParameter(HttpRequestBasedCallbackHandler.ADDRESS_FIELD),
+                req.getParameter(HttpRequestBasedCallbackHandler.PASSWORD_FIELD));
+        String responseType = AuthenticationServlet.RESPONSE_STATUS_SUCCESS;
+
+        if (message != null) {
+            resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            responseType = AuthenticationServlet.RESPONSE_STATUS_FAILED;
+        } else {
+            message = "Registration complete.";
+            resp.setStatus(HttpServletResponse.SC_OK);
+        }
+
+        writeRegistrationPage(message, responseType, req.getLocale(), resp);
+    }
+
+    /**
+     * Try to create a user with the provided username and password. On error,
+     * returns a string containing an error message. On success, returns null.
+     */
+    private String tryCreateUser(String username, String password) {
+        String message = null;
+        ParticipantId id = null;
+
+        try {
+            // First, some cleanup on the parameters.
+            if (username == null) {
+                return "Username portion of address cannot be less than 2 characters";
+            }
+            username = username.trim().toLowerCase();
+            if (username.contains(ParticipantId.DOMAIN_PREFIX)) {
+                id = ParticipantId.of(username);
+            } else {
+                id = ParticipantId.of(username + ParticipantId.DOMAIN_PREFIX + domain);
+            }
+            if (id.getAddress().indexOf("@") < 2) {
+                return "Username portion of address cannot be less than 2 characters";
+            }
+            String[] usernameSplit = id.getAddress().split("@");
+            if (usernameSplit.length != 2 || !usernameSplit[0].matches("[\\w\\.]+")) {
+                return "Only letters (a-z), numbers (0-9), and periods (.) are allowed in Username";
+            }
+            if (!id.getDomain().equals(domain)) {
+                return "You can only create users at the " + domain + " domain";
+            }
+        } catch (InvalidParticipantAddress e) {
+            return "Invalid username";
+        }
+
+        try {
+            if (accountStore.getAccount(id) != null) {
+                return "Account already exists";
+            }
+        } catch (PersistenceException e) {
+            LOG.severe("Failed to retreive account data for " + id, e);
+            return "An unexpected error occured while trying to retrieve account status";
+        }
+
+        if (password == null) {
+            // Register the user with an empty password.
+            password = "";
+        }
+
+        HumanAccountDataImpl account = new HumanAccountDataImpl(id, new PasswordDigest(password.toCharArray()));
+        try {
+            accountStore.putAccount(account);
+        } catch (PersistenceException e) {
+            LOG.severe("Failed to create new account for " + id, e);
+            return "An unexpected error occured while trying to create the account";
+        }
+
+        return null;
+    }
+
+    private void writeRegistrationPage(String message, String responseType, Locale locale, HttpServletResponse dest)
+            throws IOException {
+        dest.setContentType("text/html");
+        UserRegistrationPage.write(dest.getWriter(), new GxpContext(locale), domain, message, responseType);
+    }
+}

Added: trunk/src/main/resources/wave-server.properties
===================================================================
--- trunk/src/main/resources/wave-server.properties	                        (rev 0)
+++ trunk/src/main/resources/wave-server.properties	2010-12-29 03:37:12 UTC (rev 1175)
@@ -0,0 +1,61 @@
+# Core Configuration for the Wave in a Box server
+#
+
+# Domain name of the wave server 
+wave_server_domain = local
+
+# A comma separated list of address on which to listen for connections.
+# Each address is a comma separated host:port pair.
+http_frontend_public_address = localhost:9898
+http_frontend_addresses = ${http_frontend_public_address}
+
+### Server-specific variables
+###
+
+# Settings for the different persistence stores. Currently supported: memory, file, mongodb
+signer_info_store_type = memory
+
+# The location where signer info certificate data is stored on disk. This should be changed.
+# Note: This is only used when using the file signer info store. It is ignored
+# for other data store types.
+signer_info_store_directory = _certificates
+
+# Currently supported attachment types: mongodb, disk
+attachment_store_type = disk
+
+# The location where attachments are stored on disk. This should be changed.
+# Note: This is only used when using the disk attachment store. It is ignored
+# for other data store types.
+attachment_store_directory = _attachments
+
+# Currently supported account store types: fake, memory, file, mongodb
+account_store_type = memory
+
+# The location where accounts are stored on disk. This should be changed.
+# Note: This is only used when using the file account store. It is ignored
+# for other data store types.
+account_store_directory = _accounts
+
+# Currently supported delta store types: memory, file
+delta_store_type = memory
+
+# The location where deltas are stored on disk. This should be changed.
+# Note: This is only used when using the file delta store. It is ignored
+# for other data store types.
+delta_store_directory = _deltas
+
+# Set true to use Socket.IO instead of raw WebSockets in the webclient.
+use_socketio = false
+
+# To enable federation, edit the server.federation.config file and uncomment the line below
+#include = server.federation.config
+
+# These two parameters MUST appear in this file AFTER the above include of the
+# federation config file. This is necesary so that the federation config file
+# can override these two values.
+
+# Set true to disable the verification of signed deltas
+waveserver_disable_verification = true
+
+# Set true to disable the verification of signers (certificates)
+waveserver_disable_signer_verification = true




More information about the kune-commits mailing list