parent
28647d3643
commit
42727d01d5
@ -0,0 +1,2 @@ |
||||
disabled=06target |
||||
eclipse.preferences.version=1 |
||||
@ -0,0 +1,147 @@ |
||||
package de.kreth.clubhelperbackend.google; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.InputStreamReader; |
||||
import java.security.GeneralSecurityException; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.google.api.client.auth.oauth2.Credential; |
||||
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; |
||||
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; |
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; |
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; |
||||
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; |
||||
import com.google.api.client.http.HttpTransport; |
||||
import com.google.api.client.json.JsonFactory; |
||||
import com.google.api.client.json.jackson2.JacksonFactory; |
||||
import com.google.api.client.util.store.FileDataStoreFactory; |
||||
import com.google.api.services.calendar.CalendarScopes; |
||||
|
||||
public abstract class GoogleBaseAdapter { |
||||
|
||||
private static final int GOOGLE_SECRET_PORT = 59431; |
||||
|
||||
/** Application name. */ |
||||
protected static final String APPLICATION_NAME = "ClubHelperVaadin"; |
||||
/** Directory to store user credentials for this application. */ |
||||
protected static final File DATA_STORE_DIR = new File( |
||||
System.getProperty("catalina.base"), ".credentials"); |
||||
/** Global instance of the JSON factory. */ |
||||
protected static final JsonFactory JSON_FACTORY = JacksonFactory |
||||
.getDefaultInstance(); |
||||
/** |
||||
* Global instance of the scopes required by this quickstart. |
||||
* |
||||
* If modifying these scopes, delete your previously saved credentials |
||||
*/ |
||||
static final List<String> SCOPES = Arrays.asList(CalendarScopes.CALENDAR); |
||||
|
||||
protected static Credential credential; |
||||
|
||||
protected static final Logger log = LoggerFactory |
||||
.getLogger(GoogleBaseAdapter.class); |
||||
/** Global instance of the {@link FileDataStoreFactory}. */ |
||||
protected final FileDataStoreFactory DATA_STORE_FACTORY; |
||||
/** Global instance of the HTTP transport. */ |
||||
protected final HttpTransport HTTP_TRANSPORT; |
||||
|
||||
public GoogleBaseAdapter() throws GeneralSecurityException, IOException { |
||||
super(); |
||||
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); |
||||
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); |
||||
DATA_STORE_DIR.mkdirs(); |
||||
} |
||||
|
||||
protected void checkRefreshToken(String serverName) throws IOException { |
||||
|
||||
if (credential == null) { |
||||
credential = authorize(serverName); |
||||
} |
||||
|
||||
if ((credential.getExpiresInSeconds() != null |
||||
&& credential.getExpiresInSeconds() < 3600)) { |
||||
|
||||
if (log.isDebugEnabled()) { |
||||
log.debug("Security needs refresh, trying."); |
||||
} |
||||
boolean result = credential.refreshToken(); |
||||
if (log.isDebugEnabled()) { |
||||
log.debug("Token refresh " |
||||
+ (result ? "successfull." : "failed.")); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates an authorized Credential object. |
||||
* |
||||
* @param request |
||||
* |
||||
* @return an authorized Credential object. |
||||
* @throws IOException |
||||
*/ |
||||
private synchronized Credential authorize(String serverName) |
||||
throws IOException { |
||||
if (credential != null && (credential.getExpiresInSeconds() != null |
||||
&& credential.getExpiresInSeconds() < 3600)) { |
||||
credential.refreshToken(); |
||||
return credential; |
||||
} |
||||
// Load client secrets.
|
||||
InputStream in = getClass().getResourceAsStream("/client_secret.json"); |
||||
if (in == null) { |
||||
File inHome = new File(System.getProperty("user.home"), |
||||
"client_secret.json"); |
||||
if (inHome.exists()) { |
||||
if (log.isInfoEnabled()) { |
||||
log.info( |
||||
"Google secret not found as Resource, using user Home file."); |
||||
} |
||||
in = new FileInputStream(inHome); |
||||
} else { |
||||
log.error( |
||||
"Failed to load client_secret.json. Download from google console."); |
||||
return null; |
||||
} |
||||
} |
||||
GoogleClientSecrets clientSecrets = GoogleClientSecrets |
||||
.load(JSON_FACTORY, new InputStreamReader(in)); |
||||
if (log.isTraceEnabled()) { |
||||
log.trace("client secret json resource loaded."); |
||||
} |
||||
|
||||
// Build flow and trigger user authorization request.
|
||||
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( |
||||
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES) |
||||
.setDataStoreFactory(DATA_STORE_FACTORY) |
||||
.setAccessType("offline").setApprovalPrompt("force") |
||||
.build(); |
||||
|
||||
if (log.isDebugEnabled()) { |
||||
log.debug("Configuring google LocalServerReceiver on " + serverName |
||||
+ ":" + GOOGLE_SECRET_PORT); |
||||
} |
||||
|
||||
LocalServerReceiver localServerReceiver = new LocalServerReceiver.Builder() |
||||
.setHost(serverName).setPort(GOOGLE_SECRET_PORT).build(); |
||||
|
||||
Credential credential = new AuthorizationCodeInstalledApp(flow, |
||||
localServerReceiver).authorize("user"); |
||||
if (log.isDebugEnabled()) { |
||||
log.debug( |
||||
"Credentials saved to " + DATA_STORE_DIR.getAbsolutePath()); |
||||
} |
||||
|
||||
credential.setExpiresInSeconds(Long.valueOf(691200L)); |
||||
|
||||
return credential; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,182 @@ |
||||
package de.kreth.clubhelperbackend.google.calendar; |
||||
|
||||
import java.io.IOException; |
||||
import java.security.GeneralSecurityException; |
||||
import java.util.ArrayList; |
||||
import java.util.GregorianCalendar; |
||||
import java.util.List; |
||||
import java.util.concurrent.ExecutorService; |
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.TimeUnit; |
||||
import java.util.concurrent.locks.Lock; |
||||
import java.util.concurrent.locks.ReentrantLock; |
||||
|
||||
import com.google.api.client.util.DateTime; |
||||
import com.google.api.services.calendar.model.Calendar; |
||||
import com.google.api.services.calendar.model.CalendarList; |
||||
import com.google.api.services.calendar.model.CalendarListEntry; |
||||
import com.google.api.services.calendar.model.Event; |
||||
import com.google.api.services.calendar.model.EventAttendee; |
||||
|
||||
import de.kreth.clubhelperbackend.google.GoogleBaseAdapter; |
||||
import de.kreth.clubhelperbackend.google.calendar.CalendarResource.CalendarKonfig; |
||||
|
||||
public class CalendarAdapter extends GoogleBaseAdapter { |
||||
|
||||
com.google.api.services.calendar.Calendar service; |
||||
private Lock lock = new ReentrantLock(); |
||||
private CalendarResource res; |
||||
|
||||
public CalendarAdapter() throws GeneralSecurityException, IOException { |
||||
super(); |
||||
res = new CalendarResource(); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected void checkRefreshToken(String serverName) throws IOException { |
||||
try { |
||||
if (lock.tryLock(10, TimeUnit.SECONDS)) { |
||||
try { |
||||
super.checkRefreshToken(serverName); |
||||
if (service == null && credential != null) { |
||||
service = new com.google.api.services.calendar.Calendar.Builder( |
||||
HTTP_TRANSPORT, JSON_FACTORY, credential) |
||||
.setApplicationName(APPLICATION_NAME) |
||||
.build(); |
||||
} |
||||
} finally { |
||||
lock.unlock(); |
||||
} |
||||
} |
||||
} catch (InterruptedException e) { |
||||
if (log.isWarnEnabled()) { |
||||
log.warn("Lock interrupted", e); |
||||
} |
||||
} |
||||
if (service == null) { |
||||
throw new IllegalStateException( |
||||
"Calendar Service not instanciated!"); |
||||
} |
||||
} |
||||
|
||||
Calendar getCalendarBySummaryName(List<CalendarListEntry> items, |
||||
String calendarSummary) throws IOException { |
||||
|
||||
String calendarId = null; |
||||
for (CalendarListEntry e : items) { |
||||
if (calendarSummary.equals(e.getSummary())) { |
||||
calendarId = e.getId(); |
||||
break; |
||||
} |
||||
} |
||||
if (calendarId == null) { |
||||
throw new IllegalStateException( |
||||
"Calendar " + calendarSummary + " not found!"); |
||||
} |
||||
Calendar cal = service.calendars().get(calendarId).execute(); |
||||
return cal; |
||||
} |
||||
|
||||
public List<Event> getAllEvents(String serverName) |
||||
throws IOException, InterruptedException { |
||||
|
||||
final List<Event> events = new ArrayList<>(); |
||||
|
||||
List<CalendarListEntry> items = getCalendarList(serverName); |
||||
final long oldest = getOldest(); |
||||
|
||||
List<CalendarKonfig> configs = res.getConfigs(); |
||||
ExecutorService exec = Executors.newFixedThreadPool(configs.size()); |
||||
for (CalendarKonfig c : configs) { |
||||
exec.execute(new FetchEventsRunner(items, c.getName(), events, |
||||
oldest, c.getColor())); |
||||
} |
||||
|
||||
exec.shutdown(); |
||||
try { |
||||
exec.awaitTermination(20, TimeUnit.SECONDS); |
||||
} catch (InterruptedException e) { |
||||
log.error("Thread terminated - event list may be incomplete.", e); |
||||
} |
||||
return events; |
||||
} |
||||
|
||||
private long getOldest() { |
||||
GregorianCalendar oldestCal = new GregorianCalendar(); |
||||
oldestCal.set(java.util.Calendar.DAY_OF_MONTH, 1); |
||||
oldestCal.set(java.util.Calendar.HOUR_OF_DAY, 0); |
||||
oldestCal.set(java.util.Calendar.MINUTE, 0); |
||||
oldestCal.add(java.util.Calendar.MONTH, -1); |
||||
oldestCal.add(java.util.Calendar.YEAR, -1); |
||||
oldestCal.add(java.util.Calendar.HOUR_OF_DAY, -1); |
||||
final long oldest = oldestCal.getTimeInMillis(); |
||||
return oldest; |
||||
} |
||||
|
||||
List<CalendarListEntry> getCalendarList(String serverName) |
||||
throws IOException { |
||||
checkRefreshToken(serverName); |
||||
CalendarList calendarList; |
||||
try { |
||||
calendarList = service.calendarList().list().execute(); |
||||
} catch (IOException e) { |
||||
if (log.isWarnEnabled()) { |
||||
log.warn("Error fetching Calendar List, trying token refresh", |
||||
e); |
||||
} |
||||
credential.refreshToken(); |
||||
if (log.isInfoEnabled()) { |
||||
log.info("Successfully refreshed Google Security Token."); |
||||
} |
||||
calendarList = service.calendarList().list().execute(); |
||||
} |
||||
return calendarList.getItems(); |
||||
} |
||||
|
||||
private final class FetchEventsRunner implements Runnable { |
||||
private final List<Event> events; |
||||
private final long oldest; |
||||
private String colorClass; |
||||
private List<CalendarListEntry> items; |
||||
private String summary; |
||||
|
||||
private FetchEventsRunner(List<CalendarListEntry> items, String summary, |
||||
List<Event> events, long oldest, String colorClass) { |
||||
this.events = events; |
||||
this.oldest = oldest; |
||||
this.items = items; |
||||
this.summary = summary; |
||||
this.colorClass = colorClass; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
|
||||
try { |
||||
log.debug("Fetching events of calendar \"" + summary + "\""); |
||||
Calendar calendar = getCalendarBySummaryName(items, summary); |
||||
DateTime timeMin = new DateTime(oldest); |
||||
List<Event> items = service.events().list(calendar.getId()) |
||||
.setTimeMin(timeMin).execute().getItems(); |
||||
items.forEach(item -> item.set("colorClass", colorClass)); |
||||
events.addAll(items); |
||||
log.debug("Added " + items.size() + " Events for \"" + summary |
||||
+ "\""); |
||||
|
||||
} catch (IOException e) { |
||||
log.error("Unable to fetch Events from " + summary, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static Event createDefaultEvent(String summary) { |
||||
Event ev = new Event().setGuestsCanInviteOthers(false) |
||||
.setGuestsCanModify(false).setGuestsCanSeeOtherGuests(true) |
||||
.setSummary(summary); |
||||
List<EventAttendee> attendees = new ArrayList<>(); |
||||
ev.setAttendees(attendees); |
||||
return ev; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,81 @@ |
||||
package de.kreth.clubhelperbackend.google.calendar; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.ArrayList; |
||||
import java.util.Enumeration; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
import java.util.StringTokenizer; |
||||
|
||||
public class CalendarResource { |
||||
|
||||
private Map<String, CalendarKonfig> configs; |
||||
|
||||
public CalendarResource() throws IOException { |
||||
configs = new HashMap<>(); |
||||
|
||||
InputStream resStream = getClass() |
||||
.getResourceAsStream("/calendars.properties"); |
||||
Properties prop = new Properties(); |
||||
prop.load(resStream); |
||||
|
||||
Enumeration<Object> keys = prop.keys(); |
||||
String className = getClass().getName(); |
||||
String packageName = className.substring(0, className.lastIndexOf('.')); |
||||
|
||||
while (keys.hasMoreElements()) { |
||||
String key = keys.nextElement().toString(); |
||||
String name = key.substring(packageName.length()); |
||||
String value = prop.getProperty(key); |
||||
|
||||
StringTokenizer tok = new StringTokenizer(name, "."); |
||||
name = tok.nextToken(); |
||||
String type = tok.nextToken(); |
||||
CalendarKonfig conf; |
||||
|
||||
if (configs.containsKey(name)) { |
||||
conf = configs.get(name); |
||||
} else { |
||||
conf = new CalendarKonfig(null, null); |
||||
configs.put(name, conf); |
||||
} |
||||
switch (type) { |
||||
case "name" : |
||||
conf.name = value; |
||||
break; |
||||
|
||||
case "color" : |
||||
conf.color = value; |
||||
|
||||
default : |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public List<CalendarKonfig> getConfigs() { |
||||
return new ArrayList<>(configs.values()); |
||||
} |
||||
|
||||
public class CalendarKonfig { |
||||
private String name; |
||||
private String color; |
||||
|
||||
CalendarKonfig(String name, String color) { |
||||
super(); |
||||
this.name = name; |
||||
this.color = color; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public String getColor() { |
||||
return color; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,83 @@ |
||||
package de.kreth.clubhelperbackend.google.calendar; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.ZoneId; |
||||
import java.time.ZonedDateTime; |
||||
import java.util.Calendar; |
||||
import java.util.Date; |
||||
import java.util.GregorianCalendar; |
||||
|
||||
import org.vaadin.addon.calendar.item.BasicItem; |
||||
|
||||
import com.google.api.services.calendar.model.Event; |
||||
import com.google.api.services.calendar.model.EventDateTime; |
||||
|
||||
public class ClubEvent extends BasicItem { |
||||
|
||||
private static final long serialVersionUID = -3600971939167437577L; |
||||
|
||||
ClubEvent() { |
||||
} |
||||
|
||||
public static ClubEvent parse(Event ev) { |
||||
ClubEvent clubEvent = new ClubEvent(); |
||||
clubEvent.setCaption(ev.getSummary()); |
||||
clubEvent.setStart(toZoned(parse(ev.getStart()))); |
||||
|
||||
if (clubEvent.getStart() == null) { |
||||
clubEvent.setStart(toZoned(parse(ev.getOriginalStartTime()))); |
||||
} |
||||
clubEvent.setEnd(toZoned(adjustExcludedEndDate(ev))); |
||||
clubEvent.setDescription(ev.getDescription()); |
||||
|
||||
return clubEvent; |
||||
} |
||||
|
||||
private static ZonedDateTime toZoned(Date parse) { |
||||
if (parse != null) { |
||||
Instant instant = parse.toInstant(); |
||||
return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public static Date parse(EventDateTime date) { |
||||
if (date != null) { |
||||
if (date.getDateTime() != null) { |
||||
return new Date(date.getDateTime().getValue()); |
||||
} else if (date.getDate() != null) { |
||||
return new Date(date.getDate().getValue()); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static Date adjustExcludedEndDate( |
||||
com.google.api.services.calendar.model.Event e) { |
||||
if (e.isEndTimeUnspecified() == false) { |
||||
EventDateTime end = e.getEnd(); |
||||
GregorianCalendar calendar = new GregorianCalendar(); |
||||
calendar.setTimeInMillis(end.getDate() != null |
||||
? end.getDate().getValue() |
||||
: end.getDateTime().getValue()); |
||||
if (startIsDateOnly(e)) { |
||||
calendar.add(Calendar.DAY_OF_MONTH, -1); |
||||
} |
||||
return calendar.getTime(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static boolean startIsDateOnly( |
||||
com.google.api.services.calendar.model.Event e) { |
||||
|
||||
EventDateTime start = e.getStart(); |
||||
if (start == null) { |
||||
start = e.getOriginalStartTime(); |
||||
} |
||||
return (start.getDate() != null || (start.getDateTime() != null |
||||
&& start.getDateTime().isDateOnly())); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,46 @@ |
||||
package de.kreth.vaadin.clubhelper.vaadinclubhelper.dao; |
||||
|
||||
import java.io.IOException; |
||||
import java.security.GeneralSecurityException; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import com.vaadin.server.VaadinRequest; |
||||
|
||||
import de.kreth.clubhelperbackend.google.calendar.CalendarAdapter; |
||||
import de.kreth.clubhelperbackend.google.calendar.ClubEvent; |
||||
|
||||
public class EventBusiness { |
||||
|
||||
private final List<ClubEvent> cache = new ArrayList<>(); |
||||
|
||||
public List<ClubEvent> loadEvents(VaadinRequest request) { |
||||
if (cache.isEmpty() == false) { |
||||
return Collections.unmodifiableList(cache); |
||||
} |
||||
|
||||
cache.clear(); |
||||
|
||||
try { |
||||
|
||||
// String remoteHost = request.getRemoteHost();
|
||||
String remoteHost = "localhost"; |
||||
CalendarAdapter adapter = new CalendarAdapter(); |
||||
List<com.google.api.services.calendar.model.Event> events = adapter |
||||
.getAllEvents(remoteHost); |
||||
|
||||
for (com.google.api.services.calendar.model.Event ev : events) { |
||||
if ("cancelled".equals(ev.getStatus()) == false) { |
||||
cache.add(ClubEvent.parse(ev)); |
||||
} else { |
||||
System.out.println("Cancelled: " + ev.getSummary()); |
||||
} |
||||
} |
||||
} catch (GeneralSecurityException | IOException |
||||
| InterruptedException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return Collections.unmodifiableList(cache); |
||||
} |
||||
} |
||||
@ -1,3 +1,6 @@ |
||||
spring.datasource.url=jdbc:mysql://192.168.0.8:3306/clubhelper |
||||
spring.datasource.username=markus |
||||
spring.datasource.password=0773 |
||||
spring.datasource.password=0773 |
||||
security.ignored=/** |
||||
security.basic.enable: false |
||||
management.security.enabled: false |
||||
|
||||
@ -0,0 +1,6 @@ |
||||
de.kreth.clubhelperbackend.google.calendar.1.name=mtv_wettkampf |
||||
de.kreth.clubhelperbackend.google.calendar.1.color=color1 |
||||
de.kreth.clubhelperbackend.google.calendar.2.name=mtv_allgemein |
||||
de.kreth.clubhelperbackend.google.calendar.2.color=color2 |
||||
de.kreth.clubhelperbackend.google.calendar.3.name=Schulferien |
||||
de.kreth.clubhelperbackend.google.calendar.3.color=color3 |
||||
@ -0,0 +1,13 @@ |
||||
{ |
||||
"web": { |
||||
"client_id": "18873888282-iptk63468sf4to7ajihqmq1l5ggkq54o.apps.googleusercontent.com", |
||||
"project_id": "clubhelper", |
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth", |
||||
"token_uri": "https://accounts.google.com/o/oauth2/token", |
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", |
||||
"client_secret": "RxtsmNfj6CmQOX_4enrOl9aM", |
||||
"redirect_uris": [ |
||||
"http://localhost:59431/Callback" |
||||
] |
||||
} |
||||
} |
||||
Loading…
Reference in new issue