1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package nginx.unit.websocket.pojo; 18 19 import java.io.IOException; 20 import java.lang.reflect.InvocationTargetException; 21 import java.util.Map; 22 import java.util.Set; 23 24 import javax.websocket.CloseReason; 25 import javax.websocket.Endpoint; 26 import javax.websocket.EndpointConfig; 27 import javax.websocket.MessageHandler; 28 import javax.websocket.Session; 29 30 import org.apache.juli.logging.Log; 31 import org.apache.juli.logging.LogFactory; 32 import org.apache.tomcat.util.ExceptionUtils; 33 import org.apache.tomcat.util.res.StringManager; 34 35 /** 36 * Base implementation (client and server have different concrete 37 * implementations) of the wrapper that converts a POJO instance into a 38 * WebSocket endpoint instance. 39 */ 40 public abstract class PojoEndpointBase extends Endpoint { 41 42 private final Log log = LogFactory.getLog(PojoEndpointBase.class); // must not be static 43 private static final StringManager sm = StringManager.getManager(PojoEndpointBase.class); 44 45 private Object pojo; 46 private Map<String,String> pathParameters; 47 private PojoMethodMapping methodMapping; 48 49 doOnOpen(Session session, EndpointConfig config)50 protected final void doOnOpen(Session session, EndpointConfig config) { 51 PojoMethodMapping methodMapping = getMethodMapping(); 52 Object pojo = getPojo(); 53 Map<String,String> pathParameters = getPathParameters(); 54 55 // Add message handlers before calling onOpen since that may trigger a 56 // message which in turn could trigger a response and/or close the 57 // session 58 for (MessageHandler mh : methodMapping.getMessageHandlers(pojo, 59 pathParameters, session, config)) { 60 session.addMessageHandler(mh); 61 } 62 63 if (methodMapping.getOnOpen() != null) { 64 try { 65 methodMapping.getOnOpen().invoke(pojo, 66 methodMapping.getOnOpenArgs( 67 pathParameters, session, config)); 68 69 } catch (IllegalAccessException e) { 70 // Reflection related problems 71 log.error(sm.getString( 72 "pojoEndpointBase.onOpenFail", 73 pojo.getClass().getName()), e); 74 handleOnOpenOrCloseError(session, e); 75 } catch (InvocationTargetException e) { 76 Throwable cause = e.getCause(); 77 handleOnOpenOrCloseError(session, cause); 78 } catch (Throwable t) { 79 handleOnOpenOrCloseError(session, t); 80 } 81 } 82 } 83 84 handleOnOpenOrCloseError(Session session, Throwable t)85 private void handleOnOpenOrCloseError(Session session, Throwable t) { 86 // If really fatal - re-throw 87 ExceptionUtils.handleThrowable(t); 88 89 // Trigger the error handler and close the session 90 onError(session, t); 91 try { 92 session.close(); 93 } catch (IOException ioe) { 94 log.warn(sm.getString("pojoEndpointBase.closeSessionFail"), ioe); 95 } 96 } 97 98 @Override onClose(Session session, CloseReason closeReason)99 public final void onClose(Session session, CloseReason closeReason) { 100 101 if (methodMapping.getOnClose() != null) { 102 try { 103 methodMapping.getOnClose().invoke(pojo, 104 methodMapping.getOnCloseArgs(pathParameters, session, closeReason)); 105 } catch (Throwable t) { 106 log.error(sm.getString("pojoEndpointBase.onCloseFail", 107 pojo.getClass().getName()), t); 108 handleOnOpenOrCloseError(session, t); 109 } 110 } 111 112 // Trigger the destroy method for any associated decoders 113 Set<MessageHandler> messageHandlers = session.getMessageHandlers(); 114 for (MessageHandler messageHandler : messageHandlers) { 115 if (messageHandler instanceof PojoMessageHandlerWholeBase<?>) { 116 ((PojoMessageHandlerWholeBase<?>) messageHandler).onClose(); 117 } 118 } 119 } 120 121 122 @Override onError(Session session, Throwable throwable)123 public final void onError(Session session, Throwable throwable) { 124 125 if (methodMapping.getOnError() == null) { 126 log.error(sm.getString("pojoEndpointBase.onError", 127 pojo.getClass().getName()), throwable); 128 } else { 129 try { 130 methodMapping.getOnError().invoke( 131 pojo, 132 methodMapping.getOnErrorArgs(pathParameters, session, 133 throwable)); 134 } catch (Throwable t) { 135 ExceptionUtils.handleThrowable(t); 136 log.error(sm.getString("pojoEndpointBase.onErrorFail", 137 pojo.getClass().getName()), t); 138 } 139 } 140 } 141 getPojo()142 protected Object getPojo() { return pojo; } setPojo(Object pojo)143 protected void setPojo(Object pojo) { this.pojo = pojo; } 144 145 getPathParameters()146 protected Map<String,String> getPathParameters() { return pathParameters; } setPathParameters(Map<String,String> pathParameters)147 protected void setPathParameters(Map<String,String> pathParameters) { 148 this.pathParameters = pathParameters; 149 } 150 151 getMethodMapping()152 protected PojoMethodMapping getMethodMapping() { return methodMapping; } setMethodMapping(PojoMethodMapping methodMapping)153 protected void setMethodMapping(PojoMethodMapping methodMapping) { 154 this.methodMapping = methodMapping; 155 } 156 } 157