001/** 002 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 003 * This file is part of the LDP4j Project: 004 * http://www.ldp4j.org/ 005 * 006 * Center for Open Middleware 007 * http://www.centeropenmiddleware.com/ 008 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 009 * Copyright (C) 2014 Center for Open Middleware. 010 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 011 * Licensed under the Apache License, Version 2.0 (the "License"); 012 * you may not use this file except in compliance with the License. 013 * You may obtain a copy of the License at 014 * 015 * http://www.apache.org/licenses/LICENSE-2.0 016 * 017 * Unless required by applicable law or agreed to in writing, software 018 * distributed under the License is distributed on an "AS IS" BASIS, 019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 020 * See the License for the specific language governing permissions and 021 * limitations under the License. 022 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 023 * Artifact : org.ldp4j.framework:ldp4j-application-api:0.1.0 024 * Bundle : ldp4j-application-api-0.1.0.jar 025 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 026 */ 027package org.ldp4j.application; 028 029import org.ldp4j.application.data.Individual; 030import org.ldp4j.application.data.Name; 031import org.ldp4j.application.ext.ResourceHandler; 032import org.ldp4j.application.session.ResourceSnapshot; 033import org.ldp4j.application.session.WriteSession; 034import org.ldp4j.application.session.WriteSessionException; 035import org.ldp4j.application.spi.RuntimeDelegate; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import static com.google.common.base.Preconditions.*; 040 041 042/** 043 * A proxy class that acts as a facade for interacting with the Application 044 * Engine proactively. 045 */ 046public final class ApplicationContext { 047 048 private static final class ApplicationEngineSingleton { 049 050 private static final ApplicationContext SINGLETON=new ApplicationContext(); 051 052 private ApplicationEngineSingleton() { 053 } 054 055 } 056 057 private final class SafeWriteSession implements WriteSession { 058 059 private final WriteSession nativeSession; 060 061 private boolean dispossed; 062 private boolean completed; 063 064 private SafeWriteSession(WriteSession session) { 065 this.nativeSession = session; 066 } 067 068 private void verifyExecutability() { 069 checkState(!this.dispossed,"Session has already been dispossed"); 070 checkState(!this.completed,"Session has already been completed"); 071 } 072 073 @Override 074 public <S extends ResourceSnapshot> S resolve(Class<? extends S> snapshotClass, Individual<?, ?> individual) { 075 verifyExecutability(); 076 return this.nativeSession.resolve(snapshotClass,individual); 077 } 078 079 @Override 080 public <S extends ResourceSnapshot> S find(Class<? extends S> snapshotClass, Name<?> id, Class<? extends ResourceHandler> handlerClass) { 081 verifyExecutability(); 082 return this.nativeSession.find(snapshotClass,id,handlerClass); 083 } 084 085 @Override 086 public void modify(ResourceSnapshot resource) { 087 verifyExecutability(); 088 this.nativeSession.modify(resource); 089 } 090 091 @Override 092 public void delete(ResourceSnapshot resource) { 093 verifyExecutability(); 094 this.nativeSession.delete(resource); 095 } 096 097 @Override 098 public void saveChanges() throws WriteSessionException { 099 verifyExecutability(); 100 this.completed=true; 101 this.nativeSession.saveChanges(); 102 } 103 104 @Override 105 public void discardChanges() throws WriteSessionException { 106 verifyExecutability(); 107 this.completed=true; 108 this.nativeSession.discardChanges(); 109 } 110 111 void dispose() throws ApplicationContextException { 112 if(!this.dispossed) { 113 this.dispossed=true; 114 ApplicationContext.this.session.remove(); 115 ApplicationContext.this.delegate.terminateSession(this.nativeSession); 116 } 117 } 118 119 } 120 121 private static final Logger LOGGER=LoggerFactory.getLogger(ApplicationContext.class); 122 123 private final RuntimeDelegate delegate; 124 125 private final ThreadLocal<WriteSession> session; 126 127 private ApplicationContext() { 128 this.delegate=RuntimeDelegate.getInstance(); 129 this.session=new ThreadLocal<WriteSession>(); 130 LOGGER.info("Initialized Application Context"); 131 } 132 133 private ApplicationContextException failure(Throwable cause, String fmt, Object... args) { 134 String message=String.format(fmt,args); 135 136 if(cause!=null) { 137 LOGGER.error(message+". Full stacktrace follows",cause); 138 } else { 139 LOGGER.error(message); 140 } 141 142 return new ApplicationContextException(message,cause); 143 } 144 145 /** 146 * Create a {@code WriteSession}. Only one write session can be active per 147 * thread. Sessions should not be shared among threads. 148 * 149 * @return the write session 150 * @throws ApplicationContextException 151 * if no write session can be created for whichever reason, 152 * e.g., the Application Engine is off-line or is not available. 153 */ 154 public WriteSession createSession() throws ApplicationContextException { 155 if(this.session.get()!=null) { 156 throw failure(null,"Thread already has an active session"); 157 } 158 try { 159 if(this.delegate.isOffline()) { 160 throw failure(null,"The Application Engine is off-line"); 161 } 162 return new SafeWriteSession(this.delegate.createSession()); 163 } catch (UnsupportedOperationException e) { 164 throw failure(e,"No Application Engine is available"); 165 } 166 } 167 168 169 /** 170 * Dispose a {@code WriteSession}. Once the session has been disposed it 171 * will not be active (usable) any longer. 172 * 173 * @param session 174 * the session to be disposed 175 * @throws NullPointerException 176 * if the session is null. 177 * @throws IllegalArgumentException 178 * if the session to be disposed was not created by the 179 * Application Context. 180 * @throws ApplicationContextException 181 * if the session cannot be disposed, e.g., the session is not 182 * owned by the current thread. 183 */ 184 public void disposeSession(WriteSession session) throws ApplicationContextException { 185 checkNotNull(session,"Session cannot be null"); 186 checkArgument(session instanceof SafeWriteSession,"Unknown session"); 187 if(this.session.get()!=session) { 188 throw failure(null,"Session '%s' is not owned by current thread",session); 189 } 190 SafeWriteSession safeWriteSession = (SafeWriteSession)session; 191 safeWriteSession.dispose(); 192 } 193 194 195 /** 196 * Get the Application context. 197 * 198 * @return the application context. 199 */ 200 public static ApplicationContext getInstance() { 201 return ApplicationEngineSingleton.SINGLETON; 202 } 203 204}