001/* 002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 */ 020 021package org.nuxeo.connect.client.jsf; 022 023import java.io.IOException; 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.Calendar; 027import java.util.List; 028import java.util.Map; 029 030import javax.faces.context.FacesContext; 031import javax.faces.model.SelectItem; 032import javax.servlet.http.HttpServletRequest; 033 034import org.apache.commons.codec.binary.Base64; 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.jboss.seam.ScopeType; 038import org.jboss.seam.annotations.Factory; 039import org.jboss.seam.annotations.In; 040import org.jboss.seam.annotations.Name; 041import org.jboss.seam.annotations.Scope; 042import org.jboss.seam.contexts.Contexts; 043import org.jboss.seam.faces.FacesMessages; 044import org.jboss.seam.international.StatusMessage; 045import org.nuxeo.common.utils.ExceptionUtils; 046import org.nuxeo.connect.client.status.ConnectStatusHolder; 047import org.nuxeo.connect.client.status.ConnectUpdateStatusInfo; 048import org.nuxeo.connect.client.status.SubscriptionStatusWrapper; 049import org.nuxeo.connect.connector.NuxeoClientInstanceType; 050import org.nuxeo.connect.connector.http.ConnectUrlConfig; 051import org.nuxeo.connect.data.SubscriptionStatusType; 052import org.nuxeo.connect.identity.LogicalInstanceIdentifier; 053import org.nuxeo.connect.identity.LogicalInstanceIdentifier.InvalidCLID; 054import org.nuxeo.connect.identity.LogicalInstanceIdentifier.NoCLID; 055import org.nuxeo.connect.identity.TechnicalInstanceIdentifier; 056import org.nuxeo.connect.registration.ConnectRegistrationService; 057import org.nuxeo.connect.update.PackageException; 058import org.nuxeo.connect.update.PackageUpdateService; 059import org.nuxeo.ecm.core.api.Blob; 060import org.nuxeo.ecm.core.api.CloseableFile; 061import org.nuxeo.runtime.api.Framework; 062 063/** 064 * Seam Bean to expose Connect Registration operations. 065 * <ul> 066 * <li>getting status 067 * <li>registering 068 * <li>... 069 * </ul> 070 * 071 * @author <a href="mailto:[email protected]">Thierry Delprat</a> 072 */ 073@Name("connectStatus") 074@Scope(ScopeType.CONVERSATION) 075public class ConnectStatusActionBean implements Serializable { 076 077 private static final long serialVersionUID = 1L; 078 079 public static final String CLIENT_BANNER_TYPE = "clientSideBanner"; 080 081 public static final String SERVER_BANNER_TYPE = "serverSideBanner"; 082 083 private static final Log log = LogFactory.getLog(ConnectStatusActionBean.class); 084 085 @In(create = true, required = false) 086 protected FacesMessages facesMessages; 087 088 @In(create = true, required = true, value = "appsViews") 089 protected AppCenterViewsManager appsViews; 090 091 @In(create = true) 092 protected Map<String, String> messages; 093 094 protected String CLID; 095 096 protected String token; 097 098 protected ConnectUpdateStatusInfo connectionStatusCache; 099 100 public String getRegistredCLID() throws NoCLID { 101 if (isRegistered()) { 102 return LogicalInstanceIdentifier.instance().getCLID(); 103 } else { 104 return null; 105 } 106 } 107 108 public String getCLID() { 109 return CLID; 110 } 111 112 public void setCLID(String cLID) { 113 CLID = cLID; 114 } 115 116 public String unregister() { 117 LogicalInstanceIdentifier.cleanUp(); 118 resetRegister(); 119 return null; 120 } 121 122 public List<SelectItem> getInstanceTypes() { 123 List<SelectItem> types = new ArrayList<>(); 124 for (NuxeoClientInstanceType itype : NuxeoClientInstanceType.values()) { 125 SelectItem item = new SelectItem(itype.getValue(), "label.instancetype." + itype.getValue()); 126 types.add(item); 127 } 128 return types; 129 } 130 131 protected ConnectRegistrationService getService() { 132 return Framework.getService(ConnectRegistrationService.class); 133 } 134 135 /** 136 * @since 9.2 137 */ 138 @Factory(scope = ScopeType.APPLICATION, value = "registredConnectInstance") 139 public boolean isRegistered() { 140 return getService().isInstanceRegistered(); 141 } 142 143 /** 144 * @deprecated Since 9.2, use {@link #isRegistered()} instead. 145 */ 146 @Deprecated 147 public boolean isRegistred() { 148 return isRegistered(); 149 } 150 151 protected void flushContextCache() { 152 // A4J and Event cache don't play well ... 153 Contexts.getApplicationContext().remove("registredConnectInstance"); 154 Contexts.getApplicationContext().remove("connectUpdateStatusInfo"); 155 appsViews.flushCache(); 156 } 157 158 @Factory(value = "connectServerReachable", scope = ScopeType.EVENT) 159 public boolean isConnectServerReachable() { 160 return !ConnectStatusHolder.instance().getStatus().isConnectServerUnreachable(); 161 } 162 163 public String refreshStatus() { 164 ConnectStatusHolder.instance().getStatus(true); 165 flushContextCache(); 166 return null; 167 } 168 169 public SubscriptionStatusWrapper getStatus() { 170 return ConnectStatusHolder.instance().getStatus(); 171 } 172 173 /** 174 * Returns the registration expiration date included in the CLID, or {@code null} if the CLID cannot be loaded or 175 * doesn't include the expiration date (old v0 format). 176 * 177 * @since 10.2 178 */ 179 public Calendar getRegistrationExpirationDate() { 180 long timestamp = ConnectStatusHolder.instance().getRegistrationExpirationTimestamp(); 181 if (timestamp > -1) { 182 Calendar calendar = Calendar.getInstance(); 183 calendar.setTimeInMillis(timestamp * 1000); 184 return calendar; 185 } else { 186 return null; 187 } 188 } 189 190 public String resetRegister() { 191 flushContextCache(); 192 return null; 193 } 194 195 public String getToken() { 196 return token; 197 } 198 199 public void setToken(String token) throws IOException, InvalidCLID { 200 if (token != null) { 201 String tokenData = new String(Base64.decodeBase64(token)); 202 String[] tokenDataLines = tokenData.split("\n"); 203 for (String line : tokenDataLines) { 204 String[] parts = line.split(":"); 205 if (parts.length > 1 && "CLID".equals(parts[0])) { 206 getService().localRegisterInstance(parts[1], " "); 207 // force refresh of connect status info 208 connectionStatusCache = null; 209 flushContextCache(); 210 ConnectStatusHolder.instance().flush(); 211 } 212 } 213 } 214 } 215 216 public String getCTID() { 217 try { 218 return TechnicalInstanceIdentifier.instance().getCTID(); 219 } catch (Exception e) { // stupid API 220 throw ExceptionUtils.runtimeException(e); 221 } 222 } 223 224 public String localRegister() { 225 try { 226 getService().localRegisterInstance(CLID, ""); 227 } catch (InvalidCLID e) { 228 facesMessages.addToControl("offline_clid", StatusMessage.Severity.WARN, 229 messages.get("label.connect.wrongCLID")); 230 } catch (IOException e) { 231 facesMessages.addToControl("offline_clid", StatusMessage.Severity.ERROR, 232 messages.get("label.connect.registrationError")); 233 log.error("Error while registering instance locally", e); 234 } 235 flushContextCache(); 236 return null; 237 } 238 239 protected Blob packageToUpload; 240 241 protected String packageFileName; 242 243 public String getPackageFileName() { 244 return packageFileName; 245 } 246 247 public void setPackageFileName(String packageFileName) { 248 this.packageFileName = packageFileName; 249 } 250 251 public Blob getPackageToUpload() { 252 return packageToUpload; 253 } 254 255 public void setPackageToUpload(Blob packageToUpload) { 256 this.packageToUpload = packageToUpload; 257 } 258 259 public void uploadPackage() throws IOException { 260 if (packageToUpload == null) { 261 facesMessages.add(StatusMessage.Severity.WARN, "label.connect.nofile"); 262 return; 263 } 264 PackageUpdateService pus = Framework.getService(PackageUpdateService.class); 265 try (CloseableFile cfile = packageToUpload.getCloseableFile()) { 266 pus.addPackage(cfile.getFile()); 267 } catch (PackageException e) { 268 log.warn(e, e); 269 facesMessages.add(StatusMessage.Severity.ERROR, 270 messages.get("label.connect.wrong.package") + ":" + e.getMessage()); 271 return; 272 } finally { 273 packageFileName = null; 274 packageToUpload = null; 275 } 276 } 277 278 public ConnectUpdateStatusInfo getDynamicConnectUpdateStatusInfo() { 279 HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance() 280 .getExternalContext() 281 .getRequest(); 282 String bannerType = req.getParameter("bannerType"); 283 if ("unregistered".equals(bannerType)) { 284 return ConnectUpdateStatusInfo.unregistered(); 285 } else if ("notreachable".equals(bannerType)) { 286 return ConnectUpdateStatusInfo.connectServerUnreachable(); 287 } else if ("notvalid".equals(bannerType)) { 288 return ConnectUpdateStatusInfo.notValid(); 289 } else if ("ok".equals(bannerType)) { 290 return ConnectUpdateStatusInfo.ok(); 291 } 292 return getConnectUpdateStatusInfo(); 293 } 294 295 /** 296 * @since 5.9.2 297 */ 298 @Factory(scope = ScopeType.APPLICATION, value = "connectBannerEnabled") 299 public boolean isConnectBannerEnabled() { 300 final String testerName = Framework.getProperty("org.nuxeo.ecm.tester.name"); 301 if (testerName != null && testerName.equals("Nuxeo-Selenium-Tester")) { 302 // disable banner when running selenium tests 303 return false; 304 } 305 return true; 306 } 307 308 @Factory(scope = ScopeType.APPLICATION, value = "connectUpdateStatusInfo") 309 public ConnectUpdateStatusInfo getConnectUpdateStatusInfo() { 310 if (connectionStatusCache == null) { 311 if (!isRegistered()) { 312 connectionStatusCache = ConnectUpdateStatusInfo.unregistered(); 313 } else { 314 if (isConnectBannerEnabled() && isConnectServerReachable()) { 315 if (getStatus().isError()) { 316 connectionStatusCache = ConnectUpdateStatusInfo.connectServerUnreachable(); 317 } else { 318 if (ConnectStatusHolder.instance().getStatus().status() == SubscriptionStatusType.OK) { 319 connectionStatusCache = ConnectUpdateStatusInfo.ok(); 320 } else { 321 connectionStatusCache = ConnectUpdateStatusInfo.notValid(); 322 } 323 } 324 } else { 325 connectionStatusCache = ConnectUpdateStatusInfo.connectServerUnreachable(); 326 } 327 } 328 } 329 return connectionStatusCache; 330 } 331 332 @Factory("nuxeoConnectUrl") 333 public String getConnectServerUrl() { 334 return ConnectUrlConfig.getBaseUrl(); 335 } 336 337}