Skip to content

Instantly share code, notes, and snippets.

@PauloMigAlmeida
Last active August 29, 2015 13:56
Show Gist options
  • Save PauloMigAlmeida/8919982 to your computer and use it in GitHub Desktop.
Save PauloMigAlmeida/8919982 to your computer and use it in GitHub Desktop.
Steps to make you app and database work with utf8mb4 characters in order to accept emojis. It's based on a webapp using Maven+Hibernate+Spring+Jetty+MySQL
//
// AppDelegate.m
//
// Created by andre.michels on 12/3/13.
//
#import "AppDelegate.h"
#import "NetworkAvailabilityUtils.h"
@implementation TableViewHistoryAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
//Initializing Network Observer
[NetworkAvailabilityUtils instance];
return YES;
}
//
// BaseNetworkRequest.h
//
// Created by Paulo Almeida on 12/9/13.
//
#import <Foundation/Foundation.h>
@protocol BaseNetworkDelegate <NSObject>
-(void) sucessOnRequest:(NSDictionary*) jsonArray AndIdentifier:(NSString*) identifier;
-(void) errorOnRequest:(NSString*) identifier;
-(void) noConnectivity;
@end
@interface BaseNetworkRequest : NSObject
#pragma Properties
@property (nonatomic, strong) NSURL *url;
@property (nonatomic,strong) id <BaseNetworkDelegate> delegate;
@property (nonatomic,strong) NSString* identifier;
#pragma Methods
-(void) startGetRequest;
-(void) startPostRequest:(NSData*)postBody;
-(void) startPostMultipartDataRequest:(NSDictionary *)postParameters;
@end
//
// BaseNetworkRequest.m
// now
//
// Created by Paulo Almeida on 12/9/13.
// Copyright (c) 2013 Loducca. All rights reserved.
//
#import "BaseNetworkRequest.h"
#import "NetworkAvailabilityUtils.h"
@implementation BaseNetworkRequest
-(void) startGetRequest
{
if(![NetworkAvailabilityUtils isConnected]){
[self.delegate noConnectivity];
return;
}
NSURLRequest *request = [NSURLRequest requestWithURL:[self url]];
[NSURLConnection sendAsynchronousRequest:request queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if(error)
{
[self.delegate errorOnRequest:[self identifier]];
}
else
{
if([data length]>0)
{
NSError *error = nil;
NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) {
[self.delegate errorOnRequest:[self identifier]];
}
else {
NSNumber *code = [jsonArray objectForKey:@"code"];
if([code intValue] == 0)
{
//NSLog(@"Array: %@", jsonArray);
[self.delegate sucessOnRequest:jsonArray AndIdentifier:[self identifier]];
}
else
{
[self.delegate errorOnRequest:[self identifier]];
}
}
}
else
{
[self.delegate errorOnRequest:[self identifier]];
}
}
}];
}
-(void) startPostRequest:(NSData *)postBody
{
if(![NetworkAvailabilityUtils isConnected]){
[self.delegate noConnectivity];
return;
}
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[self url]];
[postRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[postRequest setValue:[NSString
stringWithFormat:@"%lu", (unsigned long)[postBody length]]
forHTTPHeaderField:@"Content-length"];
[postRequest setHTTPMethod:@"POST"];
[postRequest setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:postRequest queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if(error)
{
[self.delegate errorOnRequest:[self identifier]];
}
else
{
if([data length]>0)
{
//NSLog(@"Return from Server: %@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
NSError *error = nil;
NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) {
[self.delegate errorOnRequest:[self identifier]];
}
else {
NSNumber *code = [jsonArray objectForKey:@"code"];
if([code intValue] == 0)
{
NSLog(@"Array: %@", jsonArray);
[self.delegate sucessOnRequest:jsonArray AndIdentifier:[self identifier]];
}
else
{
[self.delegate errorOnRequest:[self identifier]];
}
}
}
else
{
[self.delegate errorOnRequest:[self identifier]];
}
}
}];
}
-(void) startPostMultipartDataRequest:(NSDictionary *)postParameters
{
if(![NetworkAvailabilityUtils isConnected]){
[self.delegate noConnectivity];
return;
}
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[self url]];
NSString *boundary = @"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[postRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];
NSMutableData *postbody = [NSMutableData data];
NSEnumerator *enumerator = [postParameters keyEnumerator];
id key;
while ((key = [enumerator nextObject])) {
NSObject *tmp = [postParameters objectForKey:key];
if([tmp isKindOfClass:[NSData class]]){
[postbody appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@.jpg\"\r\n", key,key] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[NSData dataWithData:[postParameters objectForKey:key]]];
[postbody appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}else{
[postbody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:@"%@\r\n", [postParameters objectForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
}
}
[postbody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postRequest setHTTPMethod:@"POST"];
[postRequest setHTTPBody:postbody];
NSString *postLength = [NSString stringWithFormat:@"%d", (int)[postbody length]];
[postRequest setValue:postLength forHTTPHeaderField:@"Content-Length"];
[NSURLConnection sendAsynchronousRequest:postRequest queue: [NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if(error)
{
[self.delegate errorOnRequest:[self identifier]];
}
else
{
if([data length]>0)
{
NSError *error = nil;
NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) {
[self.delegate errorOnRequest:[self identifier]];
}
else {
NSNumber *code = [jsonArray objectForKey:@"code"];
if([code intValue] == 0)
{
//NSLog(@"Array: %@", jsonArray);
[self.delegate sucessOnRequest:jsonArray AndIdentifier:[self identifier]];
}
else
{
[self.delegate errorOnRequest:[self identifier]];
}
}
}
else
{
[self.delegate errorOnRequest:[self identifier]];
}
}
}];
}
@end
package com.github.gist.utf8mb4.domain;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.persistence.*;
import java.util.Date;
@Entity
@SQLDelete(sql = "UPDATE Comment SET dateRemoval = CURRENT_TIMESTAMP() WHERE id = ?")
@Where(clause = "dateRemoval is null")
public class Comment {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne(targetEntity = User.class)
@Valid
@NotNull
private User user;
@Column(length = 400, nullable = false)
@NotNull
@Size(min = 1, max = 400)
private String text;
@Column(insertable = true, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date dateInsertion;
@Column(insertable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date dateRemoval;
public Comment() {
}
//Getters and Setters
}
NSString *postData = [NSString stringWithFormat:@"text=%@",_homeTextComment.text ];
BaseNetworkRequest *insertCommentRequest = [[BaseNetworkRequest alloc] init];
[insertCommentRequest setDelegate:self];
[insertCommentRequest setUrl:[NSURL URLWithString:[Constants COMMENT_INSERT_SERVICE]]];
[insertCommentRequest setIdentifier:@"insertCommentRequest"];
[insertCommentRequest startPostRequest:[postData dataUsingEncoding:NSUTF8StringEncoding]];
<!--
This file must be place within src/main/webapp/WEB-INF/jetty-env.xml
-->
<?xml version="1.0"?>
<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
<New id="jdbc/ds" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg>jdbc/now</Arg>
<Arg>
<New class="org.apache.commons.dbcp.BasicDataSource">
<Set name="driverClassName">com.mysql.jdbc.Driver</Set>
<Set name="url">jdbc:mysql://<yourhost>:3306/<db>
</Set>
<Set name="username">yourusername</Set>
<Set name="password">yourpass</Set>
<Set name="initialSize">10</Set>
<Set name="maxActive">20</Set>
<Set name="maxIdle">15</Set>
</New>
</Arg>
</New>
</Configure>
#Add the following lines to your my.cnf file
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
//
// NetworkAvailabilityUtils.h
//
// Created by Paulo Almeida on 12/10/13.
//
#import <Foundation/Foundation.h>
#import "Reachability.h"
@interface NetworkAvailabilityUtils : NSObject
@property (nonatomic,strong) Reachability *reachability ;
+(NetworkAvailabilityUtils*) instance;
+(BOOL) isConnected;
@end
//
// NetworkAvailabilityUtils.m
//
// Created by Paulo Almeida on 12/10/13.
//
#import "NetworkAvailabilityUtils.h"
@implementation NetworkAvailabilityUtils
static NetworkAvailabilityUtils *instance;
static bool isConnected;
+(NetworkAvailabilityUtils*) instance
{
if(instance == NULL)
{
instance = [[self alloc]init];
}
return instance;
}
+(BOOL) isConnected
{
return isConnected;
}
- (id)init {
if ( (self = [super init]) ) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChanged:) name:kReachabilityChangedNotification object:nil];
_reachability = [Reachability reachabilityForInternetConnection];
[_reachability startNotifier];
//Perform a first check in order to get the actual scenario
[self networkChanged:nil];
}
return self;
}
- (void)networkChanged:(NSNotification *)notification
{
NetworkStatus remoteHostStatus = [_reachability currentReachabilityStatus];
if(remoteHostStatus == NotReachable)
{
NSLog(@"not reachable");
isConnected = NO;
}
else if (remoteHostStatus == ReachableViaWiFi)
{
NSLog(@"wifi");
isConnected = YES;
}
else if (remoteHostStatus == ReachableViaWWAN)
{
NSLog(@"carrier");
isConnected = YES;
}
}
@end
<!--
To run this application using command line: mvn clean compile package jetty:run -DskipTests -Pjetty
-->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.gist</groupId>
<artifactId>utf8mb4example</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<properties>
<configuration.source>1.6</configuration.source>
<configuration.target>1.6</configuration.target>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework.version>3.1.1.RELEASE</org.springframework.version>
<com.google.code.gson.gson.version>1.7.1</com.google.code.gson.gson.version>
</properties>
<dependencies>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.5.3</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>servlet-api-2.5</artifactId>
<version>6.1.11</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- JSON to Java Converter -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${com.google.code.gson.gson.version}</version>
</dependency>
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>jetty</id>
<dependencies>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.28</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.1.0.M0</version>
<configuration>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
<systemProperties>
<systemProperty>
<name>org.mortbay.util.URI.charset</name>
<value>UTF-8</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>${configuration.source}</source>
<target>${configuration.target}</target>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>
ALTER DATABASE db
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
ALTER TABLE Comment CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY COLUMN text varchar(400) CHARACTER SET utf8mb4 ;
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>WebApp</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/security-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
<!-- Filter Encoding -->
<filter>
<filter-name>encoding-filter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Filter Config -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- Filter Mappings -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<resource-ref>
<description>DataSource</description>
<res-ref-name>jdbc/ds</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment