/** * Topic encapsulates particular endpoint for sending/receiving messages. * * @author Nick Belaevski * @see Topic */ public abstract class AbstractTopic implements Topic { private static final Logger LOGGER = RichfacesLogger.APPLICATION.getLogger(); private TopicKey key; private volatile MessageDataSerializer serializer; private volatile boolean allowSubtopics; private List<TopicListener> listeners = new CopyOnWriteArrayList<TopicListener>(); public AbstractTopic(TopicKey key) { super(); this.key = key; } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#getMessageDataSerializer() */ @Override public MessageDataSerializer getMessageDataSerializer() { if (serializer == null) { return DefaultMessageDataSerializer.instance(); } return serializer; } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#setMessageDataSerializer(org.richfaces.application.push.MessageDataSerializer) */ @Override public void setMessageDataSerializer(MessageDataSerializer serializer) { this.serializer = serializer; } /** Returns true if this topic allow to use subtopics */ public boolean isAllowSubtopics() { return allowSubtopics; } /** Allow or disallow use of topics */ public void setAllowSubtopics(boolean allowSubtopics) { this.allowSubtopics = allowSubtopics; } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#getKey() */ @Override public TopicKey getKey() { return key; } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#addTopicListener(org.richfaces.application.push.TopicListener) */ @Override public void addTopicListener(TopicListener topicListener) { TopicListener listener = topicListener; listeners.add(listener); } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#removeTopicListener(org.richfaces.application.push.TopicListener) */ @Override public void removeTopicListener(TopicListener topicListener) { listeners.remove(topicListener); } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#checkSubscription(org.richfaces.application.push.Session) */ @Override public void checkSubscription(Session session) throws SubscriptionFailureException { SessionPreSubscriptionEvent event = new SessionPreSubscriptionEvent(this, getKey(), session); for (TopicListener listener : listeners) { if (event.isAppropriateListener(listener)) { try { event.invokeListener(listener); } catch (SubscriptionFailureException e) { throw e; } catch (Exception e) { logError(e); } } } } private void logError(Exception e) { LOGGER.error(MessageFormat.format("Exception invoking listener: {0}", e.getMessage()), e); } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#publishEvent(org.richfaces.application.push.TopicEvent) */ @Override public void publishEvent(TopicEvent event) { for (TopicListener listener : listeners) { if (event.isAppropriateListener(listener)) { try { event.invokeListener(listener); } catch (Exception e) { logError(e); } } } } /* * (non-Javadoc) * @see org.richfaces.application.push.Topic#publish(java.lang.Object) */ @Override public abstract void publish(Object messageData) throws MessageException; }
/** @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a> */ public class SelectManyHelper { private static final Logger LOG = RichfacesLogger.APPLICATION.getLogger(); public static final String CELL_CSS = "-c"; public static final String ITEM_CSS = "-opt"; public static final String ITEM_CSS_DIS = "-opt-dis"; public static final String BUTTON_CSS = "-btn"; public static final String BUTTON_CSS_DIS = "-btn-dis"; public static Comparator<ClientSelectItem> clientSelectItemComparator = new Comparator<ClientSelectItem>() { public int compare(ClientSelectItem clientSelectItem, ClientSelectItem clientSelectItem1) { Integer sortOrder = (clientSelectItem == null || clientSelectItem.getSortOrder() == null) ? 0 : clientSelectItem.getSortOrder(); Integer sortOrder1 = (clientSelectItem1 == null || clientSelectItem1.getSortOrder() == null) ? 0 : clientSelectItem1.getSortOrder(); return sortOrder.compareTo(sortOrder1); } }; public static Predicate<ClientSelectItem> SELECTED_PREDICATE = new Predicate<ClientSelectItem>() { public boolean apply(@Nullable ClientSelectItem clientSelectItem) { return clientSelectItem.isSelected(); } }; public static Predicate<ClientSelectItem> UNSELECTED_PREDICATE = Predicates.not(SELECTED_PREDICATE); public static void encodeHeader( FacesContext facesContext, UIComponent component, SelectManyRendererBase renderer, String rowClass, String cellClass) throws IOException { ResponseWriter writer = facesContext.getResponseWriter(); AbstractSelectManyComponent select = (AbstractSelectManyComponent) component; Iterator<UIColumn> headers = select.columns(); if (headers.hasNext()) { writer.startElement("tr", component); StringBuilder headerClass = new StringBuilder(rowClass); if (select.getHeaderClass() != null && !select.getHeaderClass().isEmpty()) { if (headerClass.length() > 0) { headerClass.append(" "); } headerClass.append(select.getHeaderClass()); } writer.writeAttribute("class", headerClass, null); while (headers.hasNext()) { UIColumn header = headers.next(); writer.startElement("th", component); writer.writeAttribute("class", cellClass, null); UIComponent facet = header.getFacet("header"); if (facet != null && facet.isRendered()) { facet.encodeBegin(facesContext); if (facet.getRendersChildren()) { facet.encodeChildren(facesContext); } else { renderer.renderChildren(facesContext, facet); } facet.encodeEnd(facesContext); } writer.endElement("th"); } writer.endElement("tr"); } } public static void encodeRows( FacesContext facesContext, UIComponent component, SelectManyRendererBase renderer, Iterator<ClientSelectItem> clientSelectItems, String cssPrefix) throws IOException { AbstractSelectManyComponent select = (AbstractSelectManyComponent) component; if (clientSelectItems != null && clientSelectItems.hasNext()) { String clientId = component.getClientId(facesContext); Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap(); Object oldVar = requestMap.get(select.getVar()); while (clientSelectItems.hasNext()) { ClientSelectItem clientSelectItem = clientSelectItems.next(); requestMap.put(select.getVar(), clientSelectItem.getSelectItem().getValue()); encodeOneRow(facesContext, component, renderer, clientSelectItem, cssPrefix); } requestMap.put(select.getVar(), oldVar); oldVar = null; } } public static void encodeOneRow( FacesContext facesContext, UIComponent component, SelectManyRendererBase renderer, ClientSelectItem clientSelectItem, String cssPrefix) throws IOException { AbstractSelectManyComponent table = (AbstractSelectManyComponent) component; String defaultItemCss = cssPrefix + ITEM_CSS; String defaultItemCssDis = cssPrefix + ITEM_CSS_DIS; ResponseWriter writer = facesContext.getResponseWriter(); String clientId = table.getClientId(facesContext); String itemClientId = clientId + "Item" + clientSelectItem.getSortOrder(); clientSelectItem.setClientId(itemClientId); writer.startElement(HtmlConstants.TR_ELEMENT, table); writer.writeAttribute("id", itemClientId, null); String itemCss; if (!table.isDisabled()) { itemCss = HtmlUtil.concatClasses(table.getItemClass(), defaultItemCss); } else { itemCss = HtmlUtil.concatClasses(table.getItemClass(), defaultItemCss, defaultItemCssDis); } writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, itemCss, null); String cellClassName = cssPrefix + CELL_CSS; String[] columnClasses; if (table.getColumnClasses() != null) { columnClasses = table.getColumnClasses().split(","); } else { columnClasses = new String[0]; } int columnCounter = 0; Iterator<UIColumn> columnIterator = table.columns(); while (columnIterator.hasNext()) { UIColumn column = columnIterator.next(); if (column.isRendered()) { writer.startElement(HtmlConstants.TD_ELEM, table); Object width = column.getAttributes().get("width"); if (width != null) { writer.writeAttribute( "style", "width: " + HtmlDimensions.formatSize(width.toString()), null); } String columnClass; if (columnClasses.length > 0) { columnClass = HtmlUtil.concatClasses( cellClassName, columnClasses[columnCounter % columnClasses.length], column.getAttributes().get("styleClass")); } else { columnClass = HtmlUtil.concatClasses(cellClassName, column.getAttributes().get("styleClass")); } writer.writeAttribute("class", columnClass, null); renderer.renderChildren(facesContext, column); writer.endElement(HtmlConstants.TD_ELEM); columnCounter++; } } writer.endElement(HtmlConstants.TR_ELEMENT); } public static void encodeItems( FacesContext facesContext, UIComponent component, Iterator<ClientSelectItem> clientSelectItems, String cssPrefix) throws IOException { AbstractSelectManyComponent select = (AbstractSelectManyComponent) component; String defaultItemCss = cssPrefix + ITEM_CSS; String defaultItemCssDis = cssPrefix + ITEM_CSS_DIS; if (clientSelectItems != null && clientSelectItems.hasNext()) { ResponseWriter writer = facesContext.getResponseWriter(); String clientId = component.getClientId(facesContext); while (clientSelectItems.hasNext()) { ClientSelectItem clientSelectItem = clientSelectItems.next(); String itemClientId = clientId + "Item" + clientSelectItem.getSortOrder(); clientSelectItem.setClientId(itemClientId); writer.startElement(HtmlConstants.DIV_ELEM, component); writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, itemClientId, null); String itemCss; if (!select.isDisabled()) { itemCss = HtmlUtil.concatClasses(select.getItemClass(), defaultItemCss); } else { itemCss = HtmlUtil.concatClasses(select.getItemClass(), defaultItemCss, defaultItemCssDis); } writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, itemCss, null); String label = clientSelectItem.getLabel(); if (label != null && label.trim().length() > 0) { writer.writeText(label, null); } else { writer.write("\u00a0"); } writer.endElement(HtmlConstants.DIV_ELEM); writer.write('\n'); } } } public static List<ClientSelectItem> getClientSelectItems( FacesContext facesContext, AbstractSelectManyComponent select, Iterator<SelectItem> selectItems) { List<ClientSelectItem> clientSelectItems = new ArrayList<ClientSelectItem>(); Object object = select.getValue(); List values; if (object == null) { values = new ArrayList(); } else if (object instanceof List) { values = (List) object; } else if (object instanceof Object[]) { values = Arrays.asList((Object[]) object); } else { throw new IllegalArgumentException( "Value expression must evaluate to either a List or Object[]"); } int count = values.size(); // TODO: Deal with SelectItemGroups while (selectItems.hasNext()) { SelectItem selectItem = selectItems.next(); boolean selected; int sortOrder; if (values.contains( selectItem .getValue())) { // TODO: this requires value#equals() to be overridden. Redo with // comparators? selected = true; sortOrder = values.indexOf(selectItem.getValue()); } else { selected = false; sortOrder = count; } ClientSelectItem clientSelectItem = SelectHelper.generateClientSelectItem( facesContext, select, selectItem, sortOrder, selected); clientSelectItems.add(clientSelectItem); if (!selected) { count++; } } Collections.sort(clientSelectItems, clientSelectItemComparator); return clientSelectItems; } public static Object getConvertedValue( FacesContext facesContext, UIComponent component, Object val) throws ConverterException { String[] values = (val == null) ? new String[0] : (String[]) val; Converter converter = SelectManyHelper.getItemConverter(facesContext, component); ValueExpression ve = component.getValueExpression("value"); Object targetForConvertedValues = null; if (ve != null) { // If the component has a ValueExpression for value, let modelType be the type of the value // expression Class<?> modelType = ve.getType(facesContext.getELContext()); if (modelType.isArray()) { // If the component has a ValueExpression for value and the type of the expression is an // array, let targetForConvertedValues be a new array of the expected type. Class<?> arrayComponentType = modelType.getComponentType(); targetForConvertedValues = Array.newInstance(arrayComponentType, values.length); } else if (Collection.class.isAssignableFrom(modelType) || Object.class.equals(modelType)) { // If modelType is a Collection, do the following to arrive at targetForConvertedValues: // Ask the component for its attribute under the key "collectionType" String collectionType = (String) component.getAttributes().get("collectionType"); if (collectionType != null) { // Let targetForConvertedValues be a new instance of Collection implemented by the // concrete class specified in collectionType Class<?> collectionClass = getCollectionClass(collectionType); try { targetForConvertedValues = collectionClass.newInstance(); } catch (Exception e) { throw new FacesException(e); } } else { // If there is no "collectionType" attribute, call getValue() on the component // The result will implement Collection. Collection value = (Collection) ((EditableValueHolder) component).getValue(); if (value instanceof Cloneable) { // If the result also implements Cloneable, let targetForConvertedValues be the result // of calling its clone() method, // then calling clear() on the cloned Collection. try { targetForConvertedValues = (Collection) value.getClass().getMethod("clone").invoke(value); ((Collection) targetForConvertedValues).clear(); } catch (Exception e) { // If unable to clone the value for any reason, log a message LOG.log(Logger.Level.WARNING, "Unable to clone collection"); } } if (targetForConvertedValues == null) { // and proceed to the next step Class<?> collectionClass = value == null ? modelType : value.getClass(); try { // If modelType is a concrete class, let targetForConvertedValues be a new instance of // that class. targetForConvertedValues = collectionClass.newInstance(); ((Collection) targetForConvertedValues).clear(); } catch (Exception e) { // Otherwise, the concrete type for targetForConvertedValues is taken from the // following table if (Collection.class.isAssignableFrom(modelType)) { if (SortedSet.class.isAssignableFrom(modelType)) { targetForConvertedValues = new TreeSet(); } else if (Queue.class.isAssignableFrom(modelType)) { targetForConvertedValues = new LinkedList(); } else if (Set.class.isAssignableFrom(modelType)) { targetForConvertedValues = new HashSet(values.length); } else { targetForConvertedValues = new ArrayList(values.length); } } } } } } else { throw new FacesException("ValueExpression must be either an Array, or a Collection"); } } else { // If the component does not have a ValueExpression for value, let targetForConvertedValues be // an array of type Object. targetForConvertedValues = new Object[values.length]; } for (int i = 0; i < values.length; i++) { Object value; if (converter == null) { value = values[i]; } else { value = converter.getAsObject(facesContext, component, values[i]); } if (targetForConvertedValues.getClass().isArray()) { Array.set(targetForConvertedValues, i, value); } else { ((Collection) targetForConvertedValues).add(value); } } return targetForConvertedValues; } private static Class getCollectionClass(String collectionType) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Class<?> collectionClass = null; if (classLoader == null) { classLoader = SelectManyRendererBase.class.getClassLoader(); } try { collectionClass = classLoader.loadClass(collectionType).asSubclass(Collection.class); } catch (ClassNotFoundException e) { throw new FacesException(e); } return collectionClass; } public static Converter getItemConverter(FacesContext facesContext, UIComponent component) { Converter converter = null; if (component instanceof ValueHolder) { // If the component has an attached Converter, use it. converter = ((ValueHolder) component).getConverter(); if (converter != null) { return converter; } } // If not, look for a ValueExpression for value (if any). The ValueExpression must point to // something that is: ValueExpression ve = component.getValueExpression("value"); if (ve != null) { Class<?> valueType = ve.getType(facesContext.getELContext()); // An array of primitives (such as int[]). Look up the registered by-class Converter for this // primitive type. // An array of objects (such as Integer[] or String[]). Look up the registered by-class // Converter for the underlying element type. if (valueType != null && valueType.isArray()) { converter = facesContext.getApplication().createConverter(valueType); } // A java.util.Collection. Do not convert the values. } if (converter == null) { // Spec says "If for any reason a Converter cannot be found, assume the type to be a String // array." However // if we don't have an explicit converter, see if one is registered for the class of the // SelectItem values Iterator<SelectItem> selectItems = SelectUtils.getSelectItems(facesContext, component); converter = getSelectItemConverter(facesContext.getApplication(), selectItems); } return converter; } public static Converter getSelectItemConverter( Application facesApplication, Iterator<SelectItem> selectItems) { Converter converter = null; while (selectItems.hasNext() && converter == null) { SelectItem selectItem = selectItems.next(); if (selectItem instanceof SelectItemGroup) { SelectItemGroup selectItemGroup = (SelectItemGroup) selectItem; Iterator<SelectItem> groupSelectItems = Iterators.forArray(selectItemGroup.getSelectItems()); // Recursively get the converter from the SelectItems of the SelectItemGroup converter = getSelectItemConverter(facesApplication, groupSelectItems); } else { Class<?> selectItemClass = selectItem.getValue().getClass(); if (String.class.equals(selectItemClass)) { return null; // No converter required for strings } try { converter = facesApplication.createConverter( selectItemClass); // Lookup the converter registered for the class } catch (FacesException exception) { // Converter cannot be created } } } return converter; } public static UISelectItems getPseudoSelectItems(SelectItemsInterface selectItemsInterface) { UISelectItems selectItems = null; if (selectItemsInterface.getVar() != null && selectItemsInterface.getItemValues() != null) { selectItems = new UISelectItems(); selectItems.setValue(selectItemsInterface.getItemValues()); selectItems.getAttributes().put("var", selectItemsInterface.getVar()); if (selectItemsInterface.getItemValue() != null) { selectItems.getAttributes().put("itemValue", selectItemsInterface.getItemValue()); } if (selectItemsInterface.getItemLabel() != null) { selectItems.getAttributes().put("itemLabel", selectItemsInterface.getItemLabel()); } } return selectItems; } }
/** * When deserializing objects, first check that the class being deserialized is in the allowed * whitelist. * * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a> */ public class LookAheadObjectInputStream extends ObjectInputStream { private static final Logger LOGGER = RichfacesLogger.APPLICATION.getLogger(); private static final Map<String, Class<?>> PRIMITIVE_TYPES = new HashMap<String, Class<?>>(9, 1.0F); private static Set<Class> whitelistBaseClasses = new HashSet<Class>(); private static Set<String> whitelistClassNameCache = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); static { PRIMITIVE_TYPES.put("bool", Boolean.TYPE); PRIMITIVE_TYPES.put("byte", Byte.TYPE); PRIMITIVE_TYPES.put("char", Character.TYPE); PRIMITIVE_TYPES.put("short", Short.TYPE); PRIMITIVE_TYPES.put("int", Integer.TYPE); PRIMITIVE_TYPES.put("long", Long.TYPE); PRIMITIVE_TYPES.put("float", Float.TYPE); PRIMITIVE_TYPES.put("double", Double.TYPE); PRIMITIVE_TYPES.put("void", Void.TYPE); whitelistClassNameCache.add(new Object[0].getClass().getName()); whitelistClassNameCache.add(new String[0].getClass().getName()); whitelistClassNameCache.add(new Boolean[0].getClass().getName()); whitelistClassNameCache.add(new boolean[0].getClass().getName()); whitelistClassNameCache.add(new Byte[0].getClass().getName()); whitelistClassNameCache.add(new byte[0].getClass().getName()); whitelistClassNameCache.add(new Character[0].getClass().getName()); whitelistClassNameCache.add(new char[0].getClass().getName()); whitelistClassNameCache.add(new Short[0].getClass().getName()); whitelistClassNameCache.add(new short[0].getClass().getName()); whitelistClassNameCache.add(new Integer[0].getClass().getName()); whitelistClassNameCache.add(new int[0].getClass().getName()); whitelistClassNameCache.add(new Long[0].getClass().getName()); whitelistClassNameCache.add(new long[0].getClass().getName()); whitelistClassNameCache.add(new Float[0].getClass().getName()); whitelistClassNameCache.add(new float[0].getClass().getName()); whitelistClassNameCache.add(new Double[0].getClass().getName()); whitelistClassNameCache.add(new double[0].getClass().getName()); whitelistClassNameCache.add(new Void[0].getClass().getName()); whitelistBaseClasses.add(String.class); whitelistBaseClasses.add(Boolean.class); whitelistBaseClasses.add(Byte.class); whitelistBaseClasses.add(Character.class); whitelistBaseClasses.add(Number.class); whitelistBaseClasses.add(Collection.class); loadWhitelist(); } public LookAheadObjectInputStream(InputStream in) throws IOException { super(in); } /** Only deserialize primitive or whitelisted classes */ @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { Class<?> primitiveType = PRIMITIVE_TYPES.get(desc.getName()); if (primitiveType != null) { return primitiveType; } if (!isClassValid(desc.getName())) { throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName()); } return super.resolveClass(desc); } /** Determine if the given requestedClassName is allowed by the whitelist */ boolean isClassValid(String requestedClassName) { if (whitelistClassNameCache.contains(requestedClassName)) { return true; } try { Class<?> requestedClass = Class.forName(requestedClassName); for (Class baseClass : whitelistBaseClasses) { if (baseClass.isAssignableFrom(requestedClass)) { whitelistClassNameCache.add(requestedClassName); return true; } } } catch (ClassNotFoundException e) { return false; } return false; } /** Load the whitelist from the properties file */ static void loadWhitelist() { Properties whitelistProperties = new Properties(); InputStream stream = null; try { stream = LookAheadObjectInputStream.class.getResourceAsStream( "/org/richfaces/resource/resource-serialization.properties"); whitelistProperties.load(stream); } catch (IOException e) { throw new RuntimeException("Error loading the resource-serialization.properties file", e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { throw new RuntimeException("Error closing the resource-serialization.properties file", e); } } } for (String baseClassName : whitelistProperties.getProperty("whitelist").split(",")) { try { Class<?> baseClass = Class.forName(baseClassName); whitelistBaseClasses.add(baseClass); } catch (ClassNotFoundException e) { if (LOGGER.isInfoEnabled()) { LOGGER.debug(e); } } } } }
public class MultipartRequest25 extends BaseMultipartRequest { private static final Logger LOGGER = RichfacesLogger.APPLICATION.getLogger(); private static final Function<Collection<String>, Object> MULTIMAP_VALUE_TRANSFORMER = new Function<Collection<String>, Object>() { public Object apply(Collection<String> input) { if (input.isEmpty()) { return null; } if (input.size() == 1) { return Iterables.get(input, 0); } return input.toArray(new String[input.size()]); } }; private MultipartRequestParser requestParser; private ResponseState responseState; private Iterable<UploadedFile> uploadedFiles; private Multimap<String, String> params; public MultipartRequest25( HttpServletRequest request, String uploadId, ProgressControl progressControl, MultipartRequestParser requestParser) { super(request, uploadId, progressControl); this.requestParser = requestParser; } private void parseIfNecessary() { if (responseState != null) { return; } try { requestParser.parse(); uploadedFiles = requestParser.getUploadedFiles(); params = requestParser.getParameters(); responseState = ResponseState.ok; } catch (FileUploadException e) { LOGGER.error(e.getMessage(), e); responseState = ResponseState.serverError; } } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Enumeration getParameterNames() { Collection<Object> result = Sets.newHashSet(); Enumeration names = super.getParameterNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); result.add(name); } parseIfNecessary(); result.addAll(params.keySet()); return Iterators.asEnumeration(result.iterator()); } @Override public String getParameter(String name) { String parameter = super.getParameter(name); if (parameter != null) { return parameter; } parseIfNecessary(); Collection<String> values = params.get(name); if (values.isEmpty()) { return null; } return Iterables.get(values, 0); } @Override public String[] getParameterValues(String name) { String[] parameterValues = super.getParameterValues(name); if (parameterValues != null) { return parameterValues; } parseIfNecessary(); Collection<String> values = params.get(name); if (values.isEmpty()) { return null; } return values.toArray(new String[values.size()]); } @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Map getParameterMap() { Map parameterMap = Maps.newHashMap(super.getParameterMap()); parseIfNecessary(); parameterMap.putAll(Maps.transformValues(params.asMap(), MULTIMAP_VALUE_TRANSFORMER)); return parameterMap; } public Iterable<UploadedFile> getUploadedFiles() { parseIfNecessary(); return uploadedFiles; } public void release() { super.release(); if (uploadedFiles != null) { for (UploadedFile uploadedFile : uploadedFiles) { try { uploadedFile.delete(); } catch (IOException e) { LOGGER.error(e.getMessage(), e); } } } } public ResponseState getResponseState() { parseIfNecessary(); return responseState; } }